]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/function/RearrangeWaypointsFunction.java
Version 17, September 2014
[GpsPrune.git] / tim / prune / function / RearrangeWaypointsFunction.java
index e04aea0430877889f651f07ba0563054b26fef51..17e937027c2d021593979e7c54fefcdf4851f940 100644 (file)
@@ -1,61 +1,69 @@
 package tim.prune.function;
 
+import java.util.Arrays;
+
 import javax.swing.JOptionPane;
 
 import tim.prune.App;
-import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.data.Checker;
+import tim.prune.data.DataPoint;
 import tim.prune.data.Track;
+import tim.prune.data.sort.SortMode;
+import tim.prune.data.sort.WaypointComparer;
 import tim.prune.undo.UndoRearrangeWaypoints;
 
 /**
  * Class to provide the function for rearranging waypoints
  */
-public class RearrangeWaypointsFunction extends GenericFunction
+public class RearrangeWaypointsFunction extends RearrangeFunction
 {
 
-       /** Enumeration for rearrange commands */
-       public enum Rearrange
-       {
-               /** Rearrange all waypoints to start */
-               TO_START,
-               /** Rearrange all waypoints to end */
-               TO_END,
-               /** Rearrange each waypoint to nearest track point */
-               TO_NEAREST
-       }
-
        /**
         * Constructor
         * @param inApp app object
         */
        public RearrangeWaypointsFunction(App inApp)
        {
-               super(inApp);
+               super(inApp, true);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.rearrangewaypoints";
        }
 
-       /** Begin the rearrange (not needed) */
-       public void begin() {
+       /** Get whether sorting by time is allowed or not */
+       protected boolean isSortByTimeAllowed() {
+               return Checker.haveWaypointsGotTimestamps(_app.getTrackInfo().getTrack());
        }
 
-       /** Get the name key (not needed) */
-       public String getNameKey() {
-               return null;
+       /** Get the description key */
+       public String getDescriptionKey() {
+               return "dialog.rearrangewaypoints.desc";
+       }
+
+       /** Sort by name key */
+       protected String getSortNameKey() {
+               return "sortbyname";
        }
 
        /**
-        * Rearrange the waypoints into track order
-        * @param inFunction nearest point, all to end or all to start
+        * Perform the rearrange and sort according to the radio buttons
         */
-       public void rearrangeWaypoints(Rearrange inFunction)
+       protected void finish()
        {
                Track track = _app.getTrackInfo().getTrack();
+               // Figure out what is required from the radio buttons
+               Rearrange rearrangeOption = getRearrangeOption();
+               SortMode sortOption = getSortMode();
+
                UndoRearrangeWaypoints undo = new UndoRearrangeWaypoints(track);
                boolean success = false;
-               if (inFunction == Rearrange.TO_START || inFunction == Rearrange.TO_END)
+               if (rearrangeOption == Rearrange.TO_START || rearrangeOption == Rearrange.TO_END)
                {
                        // Collect the waypoints to the start or end of the track
-                       success = track.collectWaypoints(inFunction == Rearrange.TO_START);
+                       success = collectWaypoints(rearrangeOption, sortOption);
                }
                else
                {
@@ -74,4 +82,108 @@ public class RearrangeWaypointsFunction extends GenericFunction
                }
        }
 
+
+       /**
+        * Do the collection and sorting of the waypoints
+        * @param inRearrangeOption beginning or end
+        * @param inSortOption optional sort criterion
+        * @return true on success
+        */
+       private boolean collectWaypoints(Rearrange inRearrangeOption, SortMode inSortOption)
+       {
+               // Check for mixed data, numbers of waypoints & nons
+               int numWaypoints = 0, numNonWaypoints = 0;
+               boolean wayAfterNon = false, nonAfterWay = false;
+               Track track = _app.getTrackInfo().getTrack();
+               final int numPoints = track.getNumPoints();
+               DataPoint[] waypoints = new DataPoint[numPoints];
+               DataPoint[] nonWaypoints = new DataPoint[numPoints];
+               DataPoint point = null;
+               for (int i=0; i<numPoints; i++)
+               {
+                       point = track.getPoint(i);
+                       if (point.isWaypoint())
+                       {
+                               waypoints[numWaypoints] = point;
+                               numWaypoints++;
+                               wayAfterNon |= (numNonWaypoints > 0);
+                       }
+                       else
+                       {
+                               nonWaypoints[numNonWaypoints] = point;
+                               numNonWaypoints++;
+                               nonAfterWay |= (numWaypoints > 0);
+                       }
+               }
+
+               // Exit if the data is already in the specified order
+               final boolean wpsToStart = (inRearrangeOption == Rearrange.TO_START);
+               final boolean doSort = (inSortOption != SortMode.DONT_SORT);
+               if (numWaypoints == 0 || numNonWaypoints == 0
+                       || (wpsToStart && !wayAfterNon && nonAfterWay && !doSort)
+                       || (!wpsToStart && wayAfterNon && !nonAfterWay && !doSort)
+                       || inRearrangeOption == Rearrange.TO_NEAREST)
+               {
+                       return false;
+               }
+               // Note: it could still be that the rearrange and sort has no effect, but we don't know yet
+               // Make a copy of the waypoints array first so we can compare it with after the sort
+               DataPoint[] origWaypoints = new DataPoint[numPoints];
+               System.arraycopy(waypoints, 0, origWaypoints, 0, numPoints);
+
+               if (doSort && numWaypoints > 1)
+               {
+                       // Sort the waypoints array
+                       WaypointComparer comparer = new WaypointComparer(inSortOption);
+                       Arrays.sort(waypoints, comparer);
+                       final boolean sortDidNothing = areArraysSame(origWaypoints, waypoints);
+                       if (sortDidNothing && (numNonWaypoints == 0
+                                       || (wpsToStart && !wayAfterNon && nonAfterWay)
+                                       || (!wpsToStart && wayAfterNon && !nonAfterWay)))
+                       {
+                               return false;
+                       }
+               }
+
+               // Copy the arrays into an array in the specified order
+               DataPoint[] neworder = new DataPoint[numPoints];
+               if (wpsToStart)
+               {
+                       System.arraycopy(waypoints, 0, neworder, 0, numWaypoints);
+                       System.arraycopy(nonWaypoints, 0, neworder, numWaypoints, numNonWaypoints);
+               }
+               else
+               {
+                       System.arraycopy(nonWaypoints, 0, neworder, 0, numNonWaypoints);
+                       System.arraycopy(waypoints, 0, neworder, numNonWaypoints, numWaypoints);
+               }
+               // Give track the new point order
+               return track.replaceContents(neworder);
+       }
+
+       /**
+        * Compare two arrays of DataPoints and see if they're identical or not
+        * @param inOriginal original array of points
+        * @param inSorted array of points after sorting
+        * @return true if the two arrays have the same points in the same order
+        */
+       private static boolean areArraysSame(DataPoint[] inOriginal, DataPoint[] inSorted)
+       {
+               if (inOriginal == null && inSorted == null) return true;  // both null
+               if (inOriginal == null || inSorted == null) return false; // only one of them null
+               if (inOriginal.length != inSorted.length) return false;
+               // Loop over all points
+               for (int i=0; i<inOriginal.length; i++)
+               {
+                       DataPoint origPoint = inOriginal[i];
+                       DataPoint sortedPoint = inSorted[i];
+                       if ((origPoint != null || sortedPoint != null)
+                               && (origPoint != sortedPoint))
+                       {
+                               return false; // points different
+                       }
+               }
+               // Must be all the same
+               return true;
+       }
 }