]> gitweb.fperrin.net Git - GpsPrune.git/commitdiff
Version 17, September 2014
authoractivityworkshop <mail@activityworkshop.net>
Wed, 18 Feb 2015 20:49:14 +0000 (21:49 +0100)
committeractivityworkshop <mail@activityworkshop.net>
Wed, 18 Feb 2015 20:49:14 +0000 (21:49 +0100)
108 files changed:
tim/prune/App.java
tim/prune/FunctionLibrary.java
tim/prune/GpsPrune.java
tim/prune/UpdateMessageBroker.java
tim/prune/config/Config.java
tim/prune/data/AltitudeRange.java
tim/prune/data/Checker.java
tim/prune/data/Coordinate.java
tim/prune/data/FileInfo.java
tim/prune/data/GradientCalculator.java [new file with mode: 0644]
tim/prune/data/SourceInfo.java
tim/prune/data/SpeedCalculator.java
tim/prune/data/Timestamp.java
tim/prune/data/Track.java
tim/prune/data/sort/PhotoComparer.java [moved from tim/prune/function/PhotoComparer.java with 91% similarity]
tim/prune/data/sort/SortMode.java [new file with mode: 0644]
tim/prune/data/sort/WaypointComparer.java [new file with mode: 0644]
tim/prune/function/ChooseSingleParameter.java [new file with mode: 0644]
tim/prune/function/GetWikipediaFunction.java
tim/prune/function/InterpolateFunction.java
tim/prune/function/RearrangeFunction.java [new file with mode: 0644]
tim/prune/function/RearrangePhotosFunction.java
tim/prune/function/RearrangeWaypointsFunction.java
tim/prune/function/RemoveAudioFunction.java
tim/prune/function/RemovePhotoFunction.java
tim/prune/function/SaveConfig.java
tim/prune/function/SearchWikipediaNames.java
tim/prune/function/SelectSegmentFunction.java [new file with mode: 0644]
tim/prune/function/SetAltitudeTolerance.java [new file with mode: 0644]
tim/prune/function/SetColours.java
tim/prune/function/SetLineWidth.java
tim/prune/function/ShowThreeDFunction.java
tim/prune/function/SingleNumericParameterFunction.java [new file with mode: 0644]
tim/prune/function/charts/Charter.java
tim/prune/function/compress/CompressTrackFunction.java
tim/prune/function/compress/MarkAndDeleteFunction.java [new file with mode: 0644]
tim/prune/function/compress/MarkPointsInRectangleFunction.java
tim/prune/function/deletebydate/DateInfo.java [new file with mode: 0644]
tim/prune/function/deletebydate/DateInfoList.java [new file with mode: 0644]
tim/prune/function/deletebydate/DeleteByDateFunction.java [new file with mode: 0644]
tim/prune/function/deletebydate/DeletionTableModel.java [new file with mode: 0644]
tim/prune/function/distance/DistanceTableModel.java
tim/prune/gui/DetailsDisplay.java
tim/prune/gui/IconManager.java
tim/prune/gui/MenuManager.java
tim/prune/gui/TerrainDefinitionPanel.java
tim/prune/gui/WholeNumberField.java
tim/prune/gui/colour/AltitudeColourer.java [new file with mode: 0644]
tim/prune/gui/colour/ColourChooser.java [moved from tim/prune/gui/ColourChooser.java with 99% similarity]
tim/prune/gui/colour/ColourPatch.java [moved from tim/prune/gui/ColourPatch.java with 90% similarity]
tim/prune/gui/colour/ColourerCaretaker.java [new file with mode: 0644]
tim/prune/gui/colour/ColourerFactory.java [new file with mode: 0644]
tim/prune/gui/colour/ColourerSelectorPanel.java [new file with mode: 0644]
tim/prune/gui/colour/ContinuousPointColourer.java [new file with mode: 0644]
tim/prune/gui/colour/DateColourer.java [new file with mode: 0644]
tim/prune/gui/colour/DiscretePointColourer.java [new file with mode: 0644]
tim/prune/gui/colour/FileColourer.java [new file with mode: 0644]
tim/prune/gui/colour/GradientColourer.java [new file with mode: 0644]
tim/prune/gui/colour/PatchListener.java [new file with mode: 0644]
tim/prune/gui/colour/PointColourer.java [new file with mode: 0644]
tim/prune/gui/colour/ProfileDataColourer.java [new file with mode: 0644]
tim/prune/gui/colour/SegmentColourer.java [new file with mode: 0644]
tim/prune/gui/colour/SpeedColourer.java [new file with mode: 0644]
tim/prune/gui/colour/VertSpeedColourer.java [new file with mode: 0644]
tim/prune/gui/images/add_photo_icon.png [changed mode: 0755->0644]
tim/prune/gui/images/add_textfile_icon.png [changed mode: 0755->0644]
tim/prune/gui/images/window_icon.png [deleted file]
tim/prune/gui/images/window_icon_128.png [new file with mode: 0644]
tim/prune/gui/images/window_icon_16.png [new file with mode: 0644]
tim/prune/gui/images/window_icon_20.png [new file with mode: 0644]
tim/prune/gui/images/window_icon_32.png [new file with mode: 0644]
tim/prune/gui/images/window_icon_64.png [new file with mode: 0644]
tim/prune/gui/map/MapCanvas.java
tim/prune/gui/map/MapSource.java
tim/prune/gui/profile/GradientData.java [new file with mode: 0644]
tim/prune/gui/profile/SpeedData.java
tim/prune/gui/profile/VerticalSpeedData.java
tim/prune/lang/prune-texts_af.properties
tim/prune/lang/prune-texts_cz.properties
tim/prune/lang/prune-texts_da.properties
tim/prune/lang/prune-texts_de.properties
tim/prune/lang/prune-texts_de_CH.properties
tim/prune/lang/prune-texts_en.properties
tim/prune/lang/prune-texts_en_US.properties
tim/prune/lang/prune-texts_es.properties
tim/prune/lang/prune-texts_fa.properties
tim/prune/lang/prune-texts_fr.properties
tim/prune/lang/prune-texts_hu.properties
tim/prune/lang/prune-texts_it.properties
tim/prune/lang/prune-texts_ja.properties
tim/prune/lang/prune-texts_ko.properties
tim/prune/lang/prune-texts_nl.properties
tim/prune/lang/prune-texts_pl.properties
tim/prune/lang/prune-texts_pt.properties
tim/prune/lang/prune-texts_ro.properties
tim/prune/lang/prune-texts_ru.properties
tim/prune/lang/prune-texts_sv.properties
tim/prune/lang/prune-texts_tr.properties
tim/prune/lang/prune-texts_uk.properties
tim/prune/lang/prune-texts_zh.properties
tim/prune/load/JpegLoader.java
tim/prune/readme.txt
tim/prune/save/FileSaver.java
tim/prune/save/GpxExporter.java
tim/prune/save/ImageExporter.java
tim/prune/save/KmlExporter.java
tim/prune/save/PovExporter.java
tim/prune/undo/UndoDeleteOperation.java

index e57dcf6d5d15364fd6c5ede5f509d28728531404..c69d73e484463a722f68fcab2acde659e6337e73 100644 (file)
@@ -35,6 +35,8 @@ import tim.prune.gui.MenuManager;
 import tim.prune.gui.SidebarController;
 import tim.prune.gui.UndoManager;
 import tim.prune.gui.Viewport;
+import tim.prune.gui.colour.ColourerCaretaker;
+import tim.prune.gui.colour.PointColourer;
 import tim.prune.load.FileLoader;
 import tim.prune.load.JpegLoader;
 import tim.prune.load.MediaLinkInfo;
@@ -61,6 +63,7 @@ public class App
        private JpegLoader _jpegLoader = null;
        private FileSaver _fileSaver = null;
        private UndoStack _undoStack = null;
+       private ColourerCaretaker _colCaretaker = null;
        private boolean _mangleTimestampsConfirmed = false;
        private Viewport _viewport = null;
        private ArrayList<File> _dataFiles = null;
@@ -83,6 +86,9 @@ public class App
                _track = new Track();
                _trackInfo = new TrackInfo(_track);
                FunctionLibrary.initialise(this);
+               _colCaretaker = new ColourerCaretaker(this);
+               UpdateMessageBroker.addSubscriber(_colCaretaker);
+               _colCaretaker.setColourer(Config.getPointColourer());
        }
 
 
@@ -120,6 +126,24 @@ public class App
                return _undoStack;
        }
 
+       /**
+        * Update the system's point colourer using the one in the Config
+        */
+       public void updatePointColourer()
+       {
+               if (_colCaretaker != null) {
+                       _colCaretaker.setColourer(Config.getPointColourer());
+               }
+       }
+
+       /**
+        * @return colourer object, or null
+        */
+       public PointColourer getPointColourer()
+       {
+               if (_colCaretaker == null) {return null;}
+               return _colCaretaker.getColourer();
+       }
 
        /**
         * Show the specified tip if appropriate
@@ -319,8 +343,10 @@ public class App
                        int audioIndex = _trackInfo.getAudioList().getAudioIndex(currentPoint.getAudio());
                        DataPoint nextTrackPoint = _trackInfo.getTrack().getNextTrackPoint(pointIndex + 1);
                        // Construct Undo object
-                       UndoOperation undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex,
+                       UndoDeletePoint undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex,
                                audioIndex, nextTrackPoint != null && nextTrackPoint.getSegmentStart());
+                       undo.setAtBoundaryOfSelectedRange(pointIndex == _trackInfo.getSelection().getStart() ||
+                               pointIndex == _trackInfo.getSelection().getEnd());
                        // call track to delete point
                        if (_trackInfo.deletePoint())
                        {
@@ -498,12 +524,22 @@ public class App
         * @param inPoint point to add
         */
        public void createPoint(DataPoint inPoint)
+       {
+               createPoint(inPoint, true);
+       }
+
+       /**
+        * Create a new point at the end of the track
+        * @param inPoint point to add
+        * @param inNewSegment true for a single point, false for a continuation
+        */
+       public void createPoint(DataPoint inPoint, boolean inNewSegment)
        {
                // create undo object
                UndoCreatePoint undo = new UndoCreatePoint();
                _undoStack.add(undo);
                // add point to track
-               inPoint.setSegmentStart(true);
+               inPoint.setSegmentStart(inNewSegment);
                _track.appendPoints(new DataPoint[] {inPoint});
                // ensure track's field list contains point's fields
                _track.extendFieldList(inPoint.getFieldList());
index 9c51c8df9509276c53afb7dcee17f72a624b175c..a8d7e0e10b3cfa0c6e6f7198be672bc1256a7268 100644 (file)
@@ -6,6 +6,7 @@ import tim.prune.function.*;
 import tim.prune.function.charts.Charter;
 import tim.prune.function.compress.CompressTrackFunction;
 import tim.prune.function.compress.MarkPointsInRectangleFunction;
+import tim.prune.function.deletebydate.DeleteByDateFunction;
 import tim.prune.function.distance.DistanceFunction;
 import tim.prune.function.edit.PointNameEditor;
 import tim.prune.function.estimate.EstimateTime;
@@ -42,7 +43,8 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_IMPORTBABEL = null;
        public static GenericFunction FUNCTION_SAVECONFIG  = null;
        public static GenericFunction FUNCTION_EDIT_WAYPOINT_NAME = null;
-       public static RearrangeWaypointsFunction FUNCTION_REARRANGE_WAYPOINTS = null;
+       public static GenericFunction FUNCTION_REARRANGE_WAYPOINTS = null;
+       public static GenericFunction FUNCTION_SELECT_SEGMENT = null;
        public static GenericFunction FUNCTION_SPLIT_SEGMENTS = null;
        public static GenericFunction FUNCTION_SEW_SEGMENTS = null;
        public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null;
@@ -50,7 +52,8 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_DELETE_RANGE = null;
        public static GenericFunction FUNCTION_CROP_TRACK = null;
        public static GenericFunction FUNCTION_MARK_IN_RECTANGLE = null;
-       public static GenericFunction FUNCTION_INTERPOLATE = null;
+       public static GenericFunction FUNCTION_DELETE_BY_DATE = null;
+       public static SingleNumericParameterFunction FUNCTION_INTERPOLATE = null;
        public static GenericFunction FUNCTION_LOOKUP_SRTM = null;
        public static GenericFunction FUNCTION_DOWNLOAD_SRTM = null;
        public static GenericFunction FUNCTION_LOOKUP_WIKIPEDIA = null;
@@ -90,8 +93,9 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_SET_DISK_CACHE = null;
        public static GenericFunction FUNCTION_SET_PATHS  = null;
        public static GenericFunction FUNCTION_SET_COLOURS = null;
-       public static GenericFunction FUNCTION_SET_LINE_WIDTH = null;
+       public static SingleNumericParameterFunction FUNCTION_SET_LINE_WIDTH = null;
        public static GenericFunction FUNCTION_SET_LANGUAGE = null;
+       public static SingleNumericParameterFunction FUNCTION_SET_ALTITUDE_TOLERANCE = null;
        public static GenericFunction FUNCTION_HELP   = null;
        public static GenericFunction FUNCTION_SHOW_KEYS = null;
        public static GenericFunction FUNCTION_ABOUT  = null;
@@ -115,6 +119,7 @@ public abstract class FunctionLibrary
                FUNCTION_SAVECONFIG = new SaveConfig(inApp);
                FUNCTION_EDIT_WAYPOINT_NAME = new PointNameEditor(inApp);
                FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp);
+               FUNCTION_SELECT_SEGMENT = new SelectSegmentFunction(inApp);
                FUNCTION_SPLIT_SEGMENTS = new SplitSegmentsFunction(inApp);
                FUNCTION_SEW_SEGMENTS = new SewTrackSegmentsFunction(inApp);
                FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp);
@@ -122,6 +127,7 @@ public abstract class FunctionLibrary
                FUNCTION_DELETE_RANGE = new DeleteSelectedRangeFunction(inApp);
                FUNCTION_CROP_TRACK = new CropToSelection(inApp);
                FUNCTION_MARK_IN_RECTANGLE = new MarkPointsInRectangleFunction(inApp);
+               FUNCTION_DELETE_BY_DATE = new DeleteByDateFunction(inApp);
                FUNCTION_INTERPOLATE = new InterpolateFunction(inApp);
                FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp);
                FUNCTION_DOWNLOAD_SRTM = new DownloadSrtmFunction(inApp);
@@ -164,6 +170,7 @@ public abstract class FunctionLibrary
                FUNCTION_SET_COLOURS = new SetColours(inApp);
                FUNCTION_SET_LINE_WIDTH = new SetLineWidth(inApp);
                FUNCTION_SET_LANGUAGE = new SetLanguage(inApp);
+               FUNCTION_SET_ALTITUDE_TOLERANCE = new SetAltitudeTolerance(inApp);
                FUNCTION_HELP   = new HelpScreen(inApp);
                FUNCTION_SHOW_KEYS = new ShowKeysScreen(inApp);
                FUNCTION_ABOUT  = new AboutScreen(inApp);
index dd907653c10eec569035e6074d6c90df3565862f..e3821707f9b2328a38da12508fa1d99b5e10e725 100644 (file)
@@ -3,6 +3,7 @@ package tim.prune;
 import java.awt.event.WindowAdapter;
 import java.awt.BorderLayout;
 import java.awt.Component;
+import java.awt.Image;
 import java.awt.event.WindowEvent;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -35,9 +36,9 @@ import tim.prune.gui.profile.ProfileChart;
 public class GpsPrune
 {
        /** Version number of application, used in about screen and for version check */
-       public static final String VERSION_NUMBER = "16.3";
+       public static final String VERSION_NUMBER = "17";
        /** Build number, just used for about screen */
-       public static final String BUILD_NUMBER = "303c";
+       public static final String BUILD_NUMBER = "320";
        /** Static reference to App object */
        private static App APP = null;
 
@@ -228,11 +229,26 @@ public class GpsPrune
                // Avoid automatically shutting down if window closed
                frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
 
-               // set icon
-               try {
-                       frame.setIconImage(IconManager.getImageIcon(IconManager.WINDOW_ICON).getImage());
+               // set window icons of different resolutions (1.6+)
+               try
+               {
+                       ArrayList<Image> icons = new ArrayList<Image>();
+                       String[] resolutions = {"_16", "_20", "_32", "_64", "_128"};
+                       for (String r : resolutions) {
+                               icons.add(IconManager.getImageIcon(IconManager.WINDOW_ICON + r + ".png").getImage());
+                       }
+                       Class<?> d = java.awt.Window.class;
+                       // This is the same as frame.setIconImages(icons) but is compilable also for java1.5 where this isn't available
+                       d.getDeclaredMethod("setIconImages", new Class[]{java.util.List.class}).invoke(frame, icons);
+               }
+               catch (Exception e)
+               {
+                       // setting a list of icon images didn't work, so try with just one image instead
+                       try {
+                               frame.setIconImage(IconManager.getImageIcon(IconManager.WINDOW_ICON + "_16.png").getImage());
+                       }
+                       catch (Exception e2) {}
                }
-               catch (Exception e) {} // ignore
 
                // Set up drag-and-drop handler to accept dropped files
                frame.setTransferHandler(new FileDropHandler(APP));
index c03235905318b8ed4d4307f887365dae0e3b6b91..b584ed88384249eb5a639e8dfec993b6c61b4df8 100644 (file)
@@ -6,7 +6,7 @@ package tim.prune;
  */
 public abstract class UpdateMessageBroker
 {
-       private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 6;
+       private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 7;
        /** Array of all subscribers */
        private static DataSubscriber[] _subscribers = new DataSubscriber[MAXIMUM_NUMBER_SUBSCRIBERS];
        /** Counter of the number of subscribers added so far */
index 35f5a311cea8cf8d19cfdb5b825c620e38f48abb..ad3cc9c20169aa98ba781193721d9f5e31cbb0cf 100644 (file)
@@ -7,6 +7,8 @@ import java.util.Properties;
 import tim.prune.data.RecentFileList;
 import tim.prune.data.UnitSet;
 import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.colour.ColourerFactory;
+import tim.prune.gui.colour.PointColourer;
 import tim.prune.gui.map.MapSourceLibrary;
 
 
@@ -22,6 +24,8 @@ public abstract class Config
        private static Properties _configValues = null;
        /** Colour scheme object is also part of config */
        private static ColourScheme _colourScheme = new ColourScheme();
+       /** Point colourer object, if any */
+       private static PointColourer _pointColourer = null;
        /** Recently-used file list */
        private static RecentFileList _recentFiles = new RecentFileList();
        /** Current unit set */
@@ -73,6 +77,8 @@ public abstract class Config
        public static final String KEY_EXIFTOOL_PATH = "prune.exiftoolpath";
        /** Key for colour scheme */
        public static final String KEY_COLOUR_SCHEME = "prune.colourscheme";
+       /** Key for point colourer */
+       public static final String KEY_POINT_COLOURER = "prune.pointcolourer";
        /** Key for line width used for drawing */
        public static final String KEY_LINE_WIDTH = "prune.linewidth";
        /** Key for kml track colour */
@@ -85,6 +91,10 @@ public abstract class Config
        public static final String KEY_ESTIMATION_PARAMS = "prune.estimationparams";
        /** Key for 3D exaggeration factor */
        public static final String KEY_HEIGHT_EXAGGERATION = "prune.heightexaggeration";
+       /** Key for terrain grid size */
+       public static final String KEY_TERRAIN_GRID_SIZE = "prune.terraingridsize";
+       /** Key for altitude tolerance */
+       public static final String KEY_ALTITUDE_TOLERANCE = "prune.altitudetolerance";
 
 
        /** Initialise the default properties */
@@ -145,6 +155,7 @@ public abstract class Config
                // Save all properties from file
                _configValues.putAll(props);
                _colourScheme.loadFromHex(_configValues.getProperty(KEY_COLOUR_SCHEME));
+               _pointColourer = ColourerFactory.createColourer(_configValues.getProperty(KEY_POINT_COLOURER));
                _recentFiles = new RecentFileList(_configValues.getProperty(KEY_RECENT_FILES));
                _unitSet = UnitSetLibrary.getUnitSet(_configValues.getProperty(KEY_UNITSET_KEY));
                // Adjust map source index if necessary
@@ -176,6 +187,8 @@ public abstract class Config
                props.put(KEY_AUTOSAVE_SETTINGS, "0"); // autosave false by default
                props.put(KEY_UNITSET_KEY, "unitset.kilometres"); // metric by default
                props.put(KEY_HEIGHT_EXAGGERATION, "100"); // 100%, no exaggeration
+               props.put(KEY_TERRAIN_GRID_SIZE, "50");
+               props.put(KEY_ALTITUDE_TOLERANCE, "0"); // 0, all exact as before
                return props;
        }
 
@@ -237,6 +250,14 @@ public abstract class Config
                return _colourScheme;
        }
 
+       /**
+        * @return the current point colourer, if any
+        */
+       public static PointColourer getPointColourer()
+       {
+               return _pointColourer;
+       }
+
        /**
         * @return list of recently used files
         */
@@ -337,6 +358,16 @@ public abstract class Config
                setConfigString(KEY_COLOUR_SCHEME, _colourScheme.toString());
        }
 
+       /**
+        * Update the point colourer from the given colourer
+        * @param inColourer point colourer object, or null
+        */
+       public static void updatePointColourer(PointColourer inColourer)
+       {
+               _pointColourer = inColourer;
+               setConfigString(KEY_POINT_COLOURER, ColourerFactory.PointColourerToString(_pointColourer));
+       }
+
        /**
         * @return the current unit set
         */
@@ -344,6 +375,9 @@ public abstract class Config
                return _unitSet;
        }
 
+       /**
+        * @param inIndex index of unit set to select
+        */
        public static void selectUnitSet(int inIndex)
        {
                _unitSet = UnitSetLibrary.getUnitSet(inIndex);
index d53223e9ad09589e5ace7bb8726c8a68711ac6a0..17036311cd7558e561a20b4795e916e54547c482 100644 (file)
@@ -1,5 +1,7 @@
 package tim.prune.data;
 
+import tim.prune.config.Config;
+
 /**
  * Represents a range of altitudes, taking units into account.
  * Values assumed to be >= 0.
@@ -8,14 +10,18 @@ public class AltitudeRange
 {
        /** Range of altitudes in metres */
        private IntegerRange _range = new IntegerRange();
-       /** Empty flag */
-       private boolean _empty;
+       /** Flag for whether previous value exists or not */
+       private boolean _gotPreviousValue;
        /** Previous metric value */
-       private int _prevValue;
+       private int _previousValue;
        /** Total climb in metres */
-       private double _climb;
+       private int _climb;
        /** Total descent in metres */
-       private double _descent;
+       private int _descent;
+       /** Flags for whether minimum or maximum has been found */
+       private boolean _gotPreviousMinimum = false, _gotPreviousMaximum = false;
+       /** Integer values of previous minimum and maximum, if any */
+       private int     _previousExtreme = 0;
 
 
        /**
@@ -31,10 +37,11 @@ public class AltitudeRange
        public void clear()
        {
                _range.clear();
-               _climb = 0.0;
-               _descent = 0.0;
-               _empty = true;
-               _prevValue = 0;
+               _climb = _descent = 0;
+               _gotPreviousValue = false;
+               _previousValue = 0;
+               _gotPreviousMinimum = _gotPreviousMaximum = false;
+               _previousExtreme = 0;
        }
 
 
@@ -44,20 +51,91 @@ public class AltitudeRange
         */
        public void addValue(Altitude inAltitude)
        {
+               final int wiggleLimit = Config.getConfigInt(Config.KEY_ALTITUDE_TOLERANCE) / 100;
+
                if (inAltitude != null && inAltitude.isValid())
                {
                        int altValue = (int) inAltitude.getMetricValue();
                        _range.addValue(altValue);
                        // Compare with previous value if any
-                       if (!_empty)
+                       if (_gotPreviousValue)
+                       {
+                               if (altValue != _previousValue)
+                               {
+                                       // Got an altitude value which is different from the previous one
+                                       final boolean locallyUp = (altValue > _previousValue);
+                                       final boolean overallUp = _gotPreviousMinimum && _previousValue > _previousExtreme;
+                                       final boolean overallDn = _gotPreviousMaximum && _previousValue < _previousExtreme;
+                                       final boolean moreThanWiggle = Math.abs(altValue - _previousValue) > wiggleLimit;
+                                       // Do we know whether we're going up or down yet?
+                                       if (!_gotPreviousMinimum && !_gotPreviousMaximum)
+                                       {
+                                               // we don't know whether we're going up or down yet - check limit
+                                               if (moreThanWiggle)
+                                               {
+                                                       if (locallyUp) {_gotPreviousMinimum = true;}
+                                                       else {_gotPreviousMaximum = true;}
+                                                       _previousExtreme = _previousValue;
+                                                       _previousValue = altValue;
+                                                       _gotPreviousValue = true;
+                                               }
+                                       }
+                                       else if (overallUp)
+                                       {
+                                               if (locallyUp) {
+                                                       // we're still going up - do nothing
+                                                       _previousValue = altValue;
+                                               }
+                                               else if (moreThanWiggle)
+                                               {
+                                                       // we're going up but have dropped over a maximum
+                                                       // Add the climb from _previousExtreme up to _previousValue
+                                                       _climb += (_previousValue - _previousExtreme);
+                                                       _previousExtreme = _previousValue;
+                                                       _gotPreviousMinimum = false; _gotPreviousMaximum = true;
+                                                       _previousValue = altValue;
+                                                       _gotPreviousValue = true;
+                                               }
+                                       }
+                                       else if (overallDn)
+                                       {
+                                               if (locallyUp) {
+                                                       if (moreThanWiggle)
+                                                       {
+                                                               // we're going down but have climbed up from a minimum
+                                                               // Add the descent from _previousExtreme down to _previousValue
+                                                               _descent += (_previousExtreme - _previousValue);
+                                                               _previousExtreme = _previousValue;
+                                                               _gotPreviousMinimum = true; _gotPreviousMaximum = false;
+                                                               _previousValue = altValue;
+                                                               _gotPreviousValue = true;
+                                                       }
+                                               }
+                                               else {
+                                                       // we're still going down - do nothing
+                                                       _previousValue = altValue;
+                                                       _gotPreviousValue = true;
+                                               }
+                                       }
+                                       // TODO: Behaviour when WIGGLE_LIMIT == 0 should be same as before, all differences cumulated
+                               }
+                       }
+                       else
                        {
-                               if (altValue > _prevValue)
-                                       _climb += (altValue - _prevValue);
-                               else
-                                       _descent += (_prevValue - altValue);
+                               // we haven't got a previous value at all, so it's the start of a new segment
+                               _previousValue = altValue;
+                               _gotPreviousValue = true;
                        }
-                       _prevValue = altValue;
-                       _empty = false;
+
+//                     if (!_empty)
+//                     {
+//                             if (altValue > _previousValue)
+//                                     _climb += (altValue - _previousValue);
+//                             else
+//                                     _descent += (_previousValue - altValue);
+//                     }
+//                     _previousValue = altValue;
+//                     _empty = false;
                }
        }
 
@@ -67,9 +145,24 @@ public class AltitudeRange
         */
        public void ignoreValue(Altitude inAltitude)
        {
-               // If we set the empty flag to true, that has the same effect as restarting a segment
-               _empty = true;
-               addValue(inAltitude);
+               // Process the previous value, if any, to update climb/descent as that's the end of the previous segment
+               if (_gotPreviousValue && _gotPreviousMinimum && _previousValue > _previousExtreme) {
+                       _climb += (_previousValue - _previousExtreme);
+               }
+               else if (_gotPreviousValue && _gotPreviousMaximum && _previousValue < _previousExtreme) {
+                       _descent += (_previousExtreme - _previousValue);
+               }
+               // Eliminate the counting values to start the new segment
+               _gotPreviousMinimum = _gotPreviousMaximum = false;
+               _gotPreviousValue = false;
+               // Now process this value if there is one
+               if (inAltitude != null && inAltitude.isValid())
+               {
+                       final int altValue = (int) inAltitude.getMetricValue();
+                       _range.addValue(altValue);
+                       _previousValue = altValue;
+                       _gotPreviousValue = true;
+               }
        }
 
        /**
@@ -107,7 +200,12 @@ public class AltitudeRange
         */
        public int getClimb(Unit inUnit)
        {
-               return (int) (_climb * inUnit.getMultFactorFromStd());
+               // May need to add climb from last segment
+               int lastSegmentClimb = 0;
+               if (_gotPreviousValue && _gotPreviousMinimum && _previousValue > _previousExtreme) {
+                       lastSegmentClimb = _previousValue - _previousExtreme;
+               }
+               return (int) ((_climb + lastSegmentClimb) * inUnit.getMultFactorFromStd());
        }
 
        /**
@@ -116,7 +214,12 @@ public class AltitudeRange
         */
        public int getDescent(Unit inUnit)
        {
-               return (int) (_descent * inUnit.getMultFactorFromStd());
+               // May need to add descent from last segment
+               int lastSegmentDescent = 0;
+               if (_gotPreviousValue && _gotPreviousMaximum && _previousValue < _previousExtreme) {
+                       lastSegmentDescent = _previousExtreme - _previousValue;
+               }
+               return (int) ((_descent + lastSegmentDescent) * inUnit.getMultFactorFromStd());
        }
 
        /**
@@ -124,6 +227,6 @@ public class AltitudeRange
         */
        public double getMetricHeightDiff()
        {
-               return _climb - _descent;
+               return getClimb(UnitSetLibrary.UNITS_METRES) - getDescent(UnitSetLibrary.UNITS_METRES);
        }
 }
index 085f50f88dcff01f33bff213f453953cef30f4a7..9d84fabacec3f20e4bbb2ecded9170715410d5bf 100644 (file)
@@ -44,7 +44,7 @@ public abstract class Checker
        {
                int i = inIndex + 1;
                DataPoint point = null;
-               while ((point=inTrack.getPoint(i)) != null && !point.getSegmentStart()) {
+               while ((point=inTrack.getPoint(i)) != null && (point.isWaypoint() || !point.getSegmentStart())) {
                        i++;
                }
                return Math.min(i, inTrack.getNumPoints()-1);
@@ -60,9 +60,59 @@ public abstract class Checker
        {
                int i = inIndex - 1;
                DataPoint point = null;
-               while ((point=inTrack.getPoint(i)) != null && !point.getSegmentStart()) {
+               while ((point=inTrack.getPoint(i)) != null && (point.isWaypoint() || !point.getSegmentStart())) {
                        i--;
                }
-               return Math.max(i, 0);
+               // Have we gone past the beginning of the track?
+               i = Math.max(i, 0);
+               // count forwards past the waypoints if necessary
+               while ((point=inTrack.getPoint(i)) != null && point.isWaypoint()) {
+                       i++;
+               }
+               return i;
+       }
+
+       /**
+        * Find the index of the last track point in the current segment
+        * @param inTrack track object
+        * @param inIndex current index
+        * @return index of next segment end
+        */
+       public static int getNextSegmentEnd(Track inTrack, int inIndex)
+       {
+               // First, go to start of following segment, or the end of the track
+               int i = getNextSegmentStart(inTrack, inIndex);
+               // If it's the next segment, subtract one
+               DataPoint point = inTrack.getPoint(i);
+               if (point == null || point.getSegmentStart())
+               {
+                       i--;
+               }
+               // Now we may be on a waypoint, so count back to get the last track point
+               while ((point=inTrack.getPoint(i)) != null && point.isWaypoint()) {
+                       i--;
+               }
+               return Math.min(i, inTrack.getNumPoints()-1);
+       }
+
+
+       /**
+        * @param inTrack track object
+        * @return true if there is at least one waypoint with a timestamp
+        */
+       public static boolean haveWaypointsGotTimestamps(Track inTrack)
+       {
+               if (inTrack != null)
+               {
+                       for (int i=0; i<inTrack.getNumPoints(); i++)
+                       {
+                               DataPoint p = inTrack.getPoint(i);
+                               if (p != null && p.isWaypoint() && p.hasTimestamp())
+                               {
+                                       return true;
+                               }
+                       }
+               }
+               return false;
        }
 }
index 085c502b363a5c81dfe18d45ebd583a92a3360c2..e2b67a8554c996c57e93e40b9e72f6b0ceb6069e 100644 (file)
@@ -126,7 +126,7 @@ public abstract class Coordinate
                        // parse fields according to number found
                        _degrees = (int) fields[0];
                        _asDouble = _degrees;
-                       _originalFormat = hasCardinal?FORMAT_DEG:FORMAT_DEG_WITHOUT_CARDINAL;
+                       _originalFormat = hasCardinal ? FORMAT_DEG : FORMAT_DEG_WITHOUT_CARDINAL;
                        _fracDenom = 10;
                        if (numFields == 2)
                        {
@@ -150,6 +150,19 @@ public abstract class Coordinate
                                        _asDouble = 1.0 * _degrees + (_minutes / 60.0);
                                }
                        }
+                       // Check for exponential degrees like 1.3E-6
+                       else if (numFields == 3 && !otherDelims[1] && otherDelims[2] && isJustNumber(inString))
+                       {
+                               _originalFormat = FORMAT_DEG;
+                               _asDouble = Math.abs(Double.parseDouble(inString)); // must succeed if isJustNumber has given true
+                               // now we can ignore the fields and just use this double
+                               _degrees = (int) _asDouble;
+                               double numMins = (_asDouble - _degrees) * 60.0;
+                               _minutes = (int) numMins;
+                               double numSecs = (numMins - _minutes) * 60.0;
+                               _seconds = (int) numSecs;
+                               _fracs = (int) ((numSecs - _seconds) * 10);
+                       }
                        // Differentiate between d-m.f and d-m-s using . or ,
                        else if (numFields == 3 && !otherDelims[2])
                        {
@@ -184,10 +197,10 @@ public abstract class Coordinate
 
        /**
         * Get the cardinal from the given character
-        * @param inFirstChar first character from file
-        * @param inLastChar last character from file
+        * @param inFirstChar first character from string
+        * @param inLastChar last character from string
         */
-       protected int getCardinal(char inFirstChar, char inLastChar)
+       private int getCardinal(char inFirstChar, char inLastChar)
        {
                // Try leading character first
                int cardinal = getCardinal(inFirstChar);
index 1fd56b94b76aca28739c809bb11f3f78d48ce77f..ffcc95b04bcd2a5e7b34bb5fe0ad73eb973eac7b 100644 (file)
@@ -81,6 +81,21 @@ public class FileInfo
                return _sources.get(inIndex);
        }
 
+       /**
+        * Get the SourceInfo object (if any) for the given point
+        * @param inPoint point object
+        * @return SourceInfo object if there is one, otherwise null
+        */
+       public SourceInfo getSourceForPoint(DataPoint inPoint)
+       {
+               for (SourceInfo source : _sources) {
+                       if (source.getIndex(inPoint) >= 0) {
+                               return source;
+                       }
+               }
+               return null;
+       }
+
        /**
         * Clone contents of file info
         */
diff --git a/tim/prune/data/GradientCalculator.java b/tim/prune/data/GradientCalculator.java
new file mode 100644 (file)
index 0000000..97a691f
--- /dev/null
@@ -0,0 +1,53 @@
+package tim.prune.data;
+
+/**
+ * Abstract class to hold static calculation functions
+ * for gradient (like glide slope)
+ */
+public abstract class GradientCalculator
+{
+       /**
+        * Calculate the gradient value of the track at the specified index
+        * @param inTrack track object
+        * @param inIndex index of point to calculate gradient for
+        * @param inValue object in which to place result of calculation
+        */
+       public static void calculateGradient(Track inTrack, int inIndex, SpeedValue inValue)
+       {
+               inValue.setInvalid();
+               if (inTrack == null || inIndex < 0 || inValue == null)
+               {
+                       System.err.println("Cannot calculate gradient for index " + inIndex);
+                       return;
+               }
+
+               // If no altitude or it's a waypoint then no gradient either
+               DataPoint point = inTrack.getPoint(inIndex);
+               if (point == null || !point.hasAltitude() || point.isWaypoint()) {
+                       return;
+               }
+
+               // If the point has horizontal and vertical speeds already then just use those
+               if (point.hasHSpeed() && point.hasVSpeed()) {
+                       inValue.setValue(point.getVSpeed().getValueInMetresPerSec() / point.getHSpeed().getValueInMetresPerSec());
+               }
+               else if (!point.getSegmentStart())
+               {
+                       // Use the previous track point and the next track point
+                       DataPoint p = inTrack.getPreviousTrackPoint(inIndex-1);
+                       DataPoint q = inTrack.getNextTrackPoint(inIndex+1);
+                       if (p != null && q != null && !q.getSegmentStart()
+                               && p.hasAltitude() && q.hasAltitude())
+                       {
+                               final double horizRads = DataPoint.calculateRadiansBetween(p, point) +
+                                       DataPoint.calculateRadiansBetween(point,  q);
+                               final double horizDist = Distance.convertRadiansToDistance(horizRads, UnitSetLibrary.UNITS_METRES);
+                               final double heightDiff = q.getAltitude().getMetricValue() - p.getAltitude().getMetricValue();
+                               // Get gradient in radians
+                               final double gradient = Math.atan2(heightDiff, horizDist);
+                               inValue.setValue(gradient);
+                       }
+               }
+               // otherwise, just leave value as invalid
+       }
+}
index e78d5754cbb40a14594f25c73f87edb2b4ac9fd0..b9609f1ac76cf7a557466b8b836df2f17afb24a9 100644 (file)
@@ -122,8 +122,12 @@ public class SourceInfo
        public int getIndex(DataPoint inPoint)
        {
                int idx = -1;
-               for (int i=0; i<_points.length && (idx < 0); i++) {
-                       if (_points[i] == inPoint) {idx = i;}
+               for (int i=0; i<_points.length; i++)
+               {
+                       if (_points[i] == inPoint) {
+                               idx = i;
+                               break;
+                       }
                }
                if (idx == -1) {return idx;}             // point not found
                if (_pointIndices == null) {return idx;} // All points loaded
index f6f429c7d477bc13cb29a5a9bb3212e467e99fcf..e80e21498e6b3ffa04e3b30b3a959854a5d2563b 100644 (file)
@@ -16,11 +16,12 @@ public abstract class SpeedCalculator
         */
        public static void calculateSpeed(Track inTrack, int inIndex, SpeedValue inValue)
        {
-               if (inTrack == null || inIndex < 0 || inValue == null) {
+               inValue.setInvalid();
+               if (inTrack == null || inIndex < 0 || inValue == null)
+               {
                        System.err.println("Cannot calculate speed for index " + inIndex);
                        return;
                }
-               inValue.setInvalid();
 
                DataPoint point = inTrack.getPoint(inIndex);
                if (point == null) {return;}
index 623d80497eb995e36073e31e6067926192e9c626..d175c64b6eab8df98fbd7dfdd3e4e867532d2835 100644 (file)
@@ -18,11 +18,13 @@ public class Timestamp
        private boolean _valid = false;
        private long _milliseconds = 0L;
        private String _text = null;
-       private String _timeText = null;
 
-       private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateTimeInstance();
+       private static final DateFormat DEFAULT_DATETIME_FORMAT = DateFormat.getDateTimeInstance();
+       private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance();
        private static final DateFormat DEFAULT_TIME_FORMAT = DateFormat.getTimeInstance();
+       private static boolean MillisAddedToTimeFormat = false;
        private static final DateFormat ISO_8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+       private static final DateFormat ISO_8601_FORMAT_WITH_MILLIS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        private static final DateFormat ISO_8601_FORMAT_NOZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
        private static DateFormat[] ALL_DATE_FORMATS = null;
        private static Calendar CALENDAR = null;
@@ -38,12 +40,13 @@ public class Timestamp
        private static long TWENTY_YEARS_IN_SECS = 0L;
        private static final long GARTRIP_OFFSET = 631065600L;
 
-       /** Specifies original timestamp format */
-       public static final int FORMAT_ORIGINAL = 0;
-       /** Specifies locale-dependent timestamp format */
-       public static final int FORMAT_LOCALE = 1;
-       /** Specifies ISO 8601 timestamp format */
-       public static final int FORMAT_ISO_8601 = 2;
+       /** Possible formats for parsing and displaying timestamps */
+       public enum Format
+       {
+               ORIGINAL,
+               LOCALE,
+               ISO8601
+       }
 
        /** Identifier for the parsing strategy to use */
        private enum ParseType
@@ -58,13 +61,15 @@ public class Timestamp
                FIXED_FORMAT4,
                FIXED_FORMAT5,
                FIXED_FORMAT6,
+               FIXED_FORMAT7,
                GENERAL_STRING
        }
 
        /** Array of parse types to loop through (first one is changed to last successful type) */
        private static ParseType[] ALL_PARSE_TYPES = {ParseType.NONE, ParseType.ISO8601_FRACTIONAL, ParseType.LONG,
                ParseType.FIXED_FORMAT0, ParseType.FIXED_FORMAT1, ParseType.FIXED_FORMAT2, ParseType.FIXED_FORMAT3,
-               ParseType.FIXED_FORMAT4, ParseType.FIXED_FORMAT5, ParseType.FIXED_FORMAT6, ParseType.GENERAL_STRING};
+               ParseType.FIXED_FORMAT4, ParseType.FIXED_FORMAT5, ParseType.FIXED_FORMAT6, ParseType.FIXED_FORMAT7,
+               ParseType.GENERAL_STRING};
 
        // Static block to initialise offsets
        static
@@ -80,16 +85,21 @@ public class Timestamp
                TWENTY_YEARS_IN_SECS = (MSECS_SINCE_1970 - MSECS_SINCE_1990) / 1000L;
                // Set timezone for output
                ISO_8601_FORMAT.setTimeZone(gmtZone);
-               DEFAULT_DATE_FORMAT.setTimeZone(gmtZone);
+               ISO_8601_FORMAT_WITH_MILLIS.setTimeZone(gmtZone);
+               DEFAULT_DATETIME_FORMAT.setTimeZone(gmtZone);
                // Date formats
                ALL_DATE_FORMATS = new DateFormat[] {
-                       DEFAULT_DATE_FORMAT,
+                       DEFAULT_DATETIME_FORMAT,
                        new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy"),
                        new SimpleDateFormat("HH:mm:ss dd MMM yyyy"),
                        new SimpleDateFormat("dd MMM yyyy HH:mm:ss"),
+                       new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss"),
                        new SimpleDateFormat("yyyy MMM dd HH:mm:ss"),
                        ISO_8601_FORMAT, ISO_8601_FORMAT_NOZ
                };
+               for (DateFormat df : ALL_DATE_FORMATS) {
+                       df.setLenient(false);
+               }
        }
 
 
@@ -100,6 +110,7 @@ public class Timestamp
        public Timestamp(String inString)
        {
                _valid = false;
+               _text = null;
                if (inString != null && !inString.equals(""))
                {
                        // Try each of the parse types in turn
@@ -109,6 +120,7 @@ public class Timestamp
                                {
                                        ALL_PARSE_TYPES[0] = type;
                                        _valid = true;
+                                       _text = inString;
                                        return;
                                }
                        }
@@ -167,6 +179,7 @@ public class Timestamp
                        case FIXED_FORMAT4: return parseString(inString, ALL_DATE_FORMATS[4]);
                        case FIXED_FORMAT5: return parseString(inString, ALL_DATE_FORMATS[5]);
                        case FIXED_FORMAT6: return parseString(inString, ALL_DATE_FORMATS[6]);
+                       case FIXED_FORMAT7: return parseString(inString, ALL_DATE_FORMATS[7]);
 
                        case GENERAL_STRING:
                                if (inString.length() == 19)
@@ -201,7 +214,6 @@ public class Timestamp
         */
        private boolean parseString(String inString, DateFormat inDateFormat)
        {
-               inDateFormat.setLenient(false);
                ParsePosition pPos = new ParsePosition(0);
                Date date = inDateFormat.parse(inString, pPos);
                if (date != null && inString.length() == pPos.getIndex()) // require use of _all_ the string, not just the beginning
@@ -338,6 +350,13 @@ public class Timestamp
                return _valid;
        }
 
+       /**
+        * @return true if the timestamp has non-zero milliseconds
+        */
+       public boolean hasMilliseconds()
+       {
+               return isValid() && (_milliseconds % 1000L) > 0;
+       }
        /**
         * @param inOther other Timestamp
         * @return true if this one is at least a second after the other
@@ -432,38 +451,61 @@ public class Timestamp
         */
        public String getText()
        {
-               return getText(FORMAT_LOCALE);
+               return getText(Format.LOCALE);
        }
 
        /**
         * @param inFormat format of timestamp
         * @return Description of timestamp in required format
         */
-       public String getText(int inFormat)
+       public String getText(Format inFormat)
        {
                if (!_valid) {return "";}
-               if (inFormat == FORMAT_ISO_8601) {
-                       return format(ISO_8601_FORMAT);
-               }
-               if (_text == null) {
-                       _text = format(DEFAULT_DATE_FORMAT);
+               switch (inFormat)
+               {
+                       case ORIGINAL:
+                               if (_text != null) {return _text;}
+                               // otherwise fallthrough to default
+                               //$FALL-THROUGH$
+                       case LOCALE:
+                               return format(DEFAULT_DATETIME_FORMAT);
+                       case ISO8601:
+                               return format(hasMilliseconds() ? ISO_8601_FORMAT_WITH_MILLIS : ISO_8601_FORMAT);
                }
                return _text;
        }
 
+       /**
+        * @return date part of timestamp in locale-specific format
+        */
+       public String getDateText()
+       {
+               if (!_valid) return "";
+               return format(DEFAULT_DATE_FORMAT);
+       }
+
        /**
         * @return Description of time part of timestamp in locale-specific format
         */
        public String getTimeText()
        {
-               if (_timeText == null)
+               if (!_valid) return "";
+               // Maybe we should add milliseconds to this format?
+               if (hasMilliseconds() && !MillisAddedToTimeFormat)
                {
-                       if (_valid) {
-                               _timeText = format(DEFAULT_TIME_FORMAT);
+                       try
+                       {
+                               SimpleDateFormat sdf = (SimpleDateFormat) DEFAULT_TIME_FORMAT;
+                               String pattern = sdf.toPattern();
+                               if (pattern.indexOf("ss") > 0 && pattern.indexOf("SS") < 0)
+                               {
+                                       sdf.applyPattern(pattern.replaceFirst("s+", "$0.SSS"));
+                                       MillisAddedToTimeFormat = true;
+                               }
                        }
-                       else _timeText = "";
+                       catch (ClassCastException cce) {}
                }
-               return _timeText;
+               return format(DEFAULT_TIME_FORMAT);
        }
 
        /**
index 45810e2ef9e4b7ba1a036f423962cecc8a87d5a5..c56f9f5bcc08cba3c65096fca6299c3aafb1f103 100644 (file)
@@ -87,6 +87,11 @@ public class Track
                                _dataPoints[pointIndex] = point;
                                pointIndex++;
                        }
+                       else
+                       {
+                               // TODO: Maybe report this somehow?
+                               // System.out.println("point is not valid!");
+                       }
                }
                _numPoints = pointIndex;
                // Set first track point to be start of segment
@@ -363,61 +368,6 @@ public class Track
        }
 
 
-       /**
-        * Collect all waypoints to the start or end of the track
-        * @param inAtStart true to collect at start, false for end
-        * @return true if successful, false if no change
-        */
-       public boolean collectWaypoints(boolean inAtStart)
-       {
-               // Check for mixed data, numbers of waypoints & nons
-               int numWaypoints = 0, numNonWaypoints = 0;
-               boolean wayAfterNon = false, nonAfterWay = false;
-               DataPoint[] waypoints = new DataPoint[_numPoints];
-               DataPoint[] nonWaypoints = new DataPoint[_numPoints];
-               DataPoint point = null;
-               for (int i=0; i<_numPoints; i++)
-               {
-                       point = _dataPoints[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
-               if (numWaypoints == 0 || numNonWaypoints == 0
-                       || (inAtStart && !wayAfterNon && nonAfterWay)
-                       || (!inAtStart && wayAfterNon && !nonAfterWay))
-               {
-                       return false;
-               }
-
-               // Copy the arrays back into _dataPoints in the specified order
-               if (inAtStart)
-               {
-                       System.arraycopy(waypoints, 0, _dataPoints, 0, numWaypoints);
-                       System.arraycopy(nonWaypoints, 0, _dataPoints, numWaypoints, numNonWaypoints);
-               }
-               else
-               {
-                       System.arraycopy(nonWaypoints, 0, _dataPoints, 0, numNonWaypoints);
-                       System.arraycopy(waypoints, 0, _dataPoints, numNonWaypoints, numWaypoints);
-               }
-               // needs to be scaled again
-               _scaled = false;
-               UpdateMessageBroker.informSubscribers();
-               return true;
-       }
-
-
        /**
         * Interleave all waypoints by each nearest track point
         * @return true if successful, false if no change
similarity index 91%
rename from tim/prune/function/PhotoComparer.java
rename to tim/prune/data/sort/PhotoComparer.java
index b15d57c981653e884266124fd82ace0058cd1035..5a20a90492ea511653063e804ec40c811d5fd915 100644 (file)
@@ -1,20 +1,18 @@
-package tim.prune.function;
+package tim.prune.data.sort;
 
 import java.util.Comparator;
 
 import tim.prune.data.DataPoint;
 
+
 /**
  * Class for comparing photos to sort them by name or timestamp
  */
 public class PhotoComparer implements Comparator<DataPoint>
 {
-       public enum SortMode {
-               SORTBY_NAME, SORTBY_TIME
-       };
-
        /** Sort mode */
-       private SortMode _sortMode = SortMode.SORTBY_NAME;
+       private SortMode _sortMode;
+
 
        /**
         * Constructor
@@ -79,7 +77,7 @@ public class PhotoComparer implements Comparator<DataPoint>
                if (!inP2.hasTimestamp()) return -1;
                if (!inP1.hasTimestamp()) return 1;
                // Compare the timestamps
-               long secDiff = inP1.getPhoto().getTimestamp().getSecondsSince(inP2.getPhoto().getTimestamp());
+               long secDiff = inP1.getPhoto().getTimestamp().getMillisecondsSince(inP2.getPhoto().getTimestamp());
                return (secDiff<0?-1:(secDiff==0?0:1));
        }
 
diff --git a/tim/prune/data/sort/SortMode.java b/tim/prune/data/sort/SortMode.java
new file mode 100644 (file)
index 0000000..9816fee
--- /dev/null
@@ -0,0 +1,11 @@
+package tim.prune.data.sort;
+
+/**
+ * Enumeration for possible sort modes
+ */
+public enum SortMode
+{
+       DONT_SORT,
+       SORTBY_NAME,
+       SORTBY_TIME
+}
diff --git a/tim/prune/data/sort/WaypointComparer.java b/tim/prune/data/sort/WaypointComparer.java
new file mode 100644 (file)
index 0000000..b1c3e2a
--- /dev/null
@@ -0,0 +1,74 @@
+package tim.prune.data.sort;
+
+import java.util.Comparator;
+
+import tim.prune.data.DataPoint;
+
+
+/**
+ * Class for comparing waypoints to sort them by name or timestamp
+ */
+public class WaypointComparer implements Comparator<DataPoint>
+{
+       /** Sort mode */
+       private SortMode _sortMode;
+
+
+       /**
+        * Constructor
+        * @param inMode sort mode
+        */
+       public WaypointComparer(SortMode inMode)
+       {
+               _sortMode = inMode;
+       }
+
+       /**
+        * Main compare method
+        */
+       public int compare(DataPoint inP1, DataPoint inP2)
+       {
+               if (inP2 == null || !inP2.isWaypoint()) return -1; // all nulls at end
+               if (inP1 == null || !inP1.isWaypoint()) return 1;
+
+               // Sort by time, if requested
+               int result = 0;
+               if (_sortMode == SortMode.SORTBY_TIME) {
+                       result = compareTimes(inP1, inP2);
+               }
+               // check names if names requested or if times didn't work
+               if (result == 0) {
+                       result = inP1.getWaypointName().compareTo(inP2.getWaypointName());
+               }
+               // names and times equal, try longitude
+               if (result == 0) {
+                       result = inP1.getLongitude().getDouble() > inP2.getLongitude().getDouble() ? 1 : -1;
+               }
+               // and latitude
+               if (result == 0) {
+                       result = inP1.getLatitude().getDouble() > inP2.getLatitude().getDouble() ? 1 : -1;
+               }
+               return result;
+       }
+
+       /**
+        * Compare the timestamps of the two waypoints
+        * @param inP1 first point
+        * @param inP2 second point
+        * @return compare value (-1,0,1)
+        */
+       private int compareTimes(DataPoint inP1, DataPoint inP2)
+       {
+               // Points might not have timestamps
+               if (inP1.hasTimestamp() && !inP2.hasTimestamp()) return 1;
+               if (!inP1.hasTimestamp() && inP2.hasTimestamp()) return -1;
+               if (inP1.hasTimestamp() && inP2.hasTimestamp())
+               {
+                       // Compare the timestamps
+                       long secDiff = inP1.getTimestamp().getMillisecondsSince(inP2.getTimestamp());
+                       return (secDiff<0?-1:(secDiff==0?0:1));
+               }
+               // neither has a timestamp
+               return 0;
+       }
+}
diff --git a/tim/prune/function/ChooseSingleParameter.java b/tim/prune/function/ChooseSingleParameter.java
new file mode 100644 (file)
index 0000000..5e23d74
--- /dev/null
@@ -0,0 +1,158 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.gui.WholeNumberField;
+
+/**
+ * First step of functions which just require a single numeric
+ * parameter in order to run
+ */
+public class ChooseSingleParameter extends GenericFunction
+{
+       /** Parent function which needs this parameter */
+       private SingleNumericParameterFunction _parent = null;
+       /** dialog */
+       private JDialog _dialog = null;
+       /** label which might need to be changed */
+       private JLabel _descLabel = null;
+       /** entry field */
+       private WholeNumberField _numberField = null;
+       /** ok button */
+       private JButton _okButton = null;
+
+
+       /** Constructor */
+       public ChooseSingleParameter(App inApp, SingleNumericParameterFunction inFunction)
+       {
+               super(inApp);
+               _parent = inFunction;
+       }
+
+       @Override
+       public String getNameKey() {
+               return _parent.getNameKey();
+       }
+
+       @Override
+       public void begin()
+       {
+               // Make dialog window
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(_parent.getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
+               // refresh and show the dialog
+               _descLabel.setText(I18nManager.getText(_parent.getDescriptionKey()));
+               int param = _parent.getCurrentParamValue();
+               if (param > 0) {
+                       _numberField.setValue(param);
+               }
+               else {
+                       _numberField.setText("");
+               }
+               _dialog.setVisible(true);
+               enableOkButton();
+       }
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private JPanel makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout());
+               // label
+               _descLabel = new JLabel(I18nManager.getText(_parent.getDescriptionKey()));
+               dialogPanel.add(_descLabel, BorderLayout.NORTH);
+               // Centre panel with number entry field
+               JPanel centrePanel = new JPanel();
+               centrePanel.setLayout(new BorderLayout(8, 8));
+               _numberField = new WholeNumberField(4);
+               centrePanel.add(_numberField, BorderLayout.NORTH);
+               dialogPanel.add(centrePanel, BorderLayout.CENTER);
+
+               // Listener to enable/disable ok button
+               _numberField.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               enableOkButton();
+                       }
+               });
+               _numberField.addKeyListener(new KeyAdapter() {
+                       public void keyReleased(KeyEvent inE)
+                       {
+                               int eCode = inE.getKeyCode();
+                               if (eCode == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
+                               else if (eCode == KeyEvent.VK_ENTER) {finish();}
+                               super.keyReleased(inE);
+                       }
+               });
+
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               ActionListener okListener = new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               finish();
+                       }
+               };
+               _okButton.addActionListener(okListener);
+               _okButton.setEnabled(false);
+               buttonPanel.add(_okButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               return dialogPanel;
+       }
+
+
+       /**
+        * Enable or disable the OK button as appropriate
+        */
+       private void enableOkButton()
+       {
+               _okButton.setEnabled(_numberField.getValue() >= _parent.getMinAllowedValue()
+                       && _numberField.getValue() <= _parent.getMaxAllowedValue());
+       }
+
+       /**
+        * The OK button (or Enter) has been pressed
+        */
+       private void finish()
+       {
+               if (_numberField.getValue() >= _parent.getMinAllowedValue()
+                       && _numberField.getValue() <= _parent.getMaxAllowedValue())
+               {
+                       _parent.completeFunction(_numberField.getValue());
+                       _dialog.dispose();
+               }
+       }
+}
index 4c11bf9d13d4e0b393604939e1535d633499204f..3ffdd06817d87fe984b6063c7e7a63aa3e1ab4d9 100644 (file)
@@ -91,7 +91,7 @@ public class GetWikipediaFunction extends GenericDownloaderFunction
 
                // Set status label according to error or "none found", leave blank if ok
                if (_errorMessage == null && _trackListModel.isEmpty()) {
-                       _errorMessage = I18nManager.getText("dialog.gpsies.nonefound");
+                       _errorMessage = I18nManager.getText("dialog.wikipedia.nonefound");
                }
                _statusLabel.setText(_errorMessage == null ? "" : _errorMessage);
        }
index 84826144d94e787bdea53e457e2818e57ce34270..221fdaf989c6c62141d0dfbe7ac29cfb66b069f0 100644 (file)
@@ -3,7 +3,6 @@ package tim.prune.function;
 import javax.swing.JOptionPane;
 
 import tim.prune.App;
-import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Track;
@@ -12,14 +11,14 @@ import tim.prune.undo.UndoInterpolate;
 /**
  * Function to interpolate between the points in a range
  */
-public class InterpolateFunction extends GenericFunction
+public class InterpolateFunction extends SingleNumericParameterFunction
 {
        /**
         * Constructor
         * @param inApp app object
         */
        public InterpolateFunction(App inApp) {
-               super(inApp);
+               super(inApp, 1, 1000);
        }
 
        /** @return name key */
@@ -27,10 +26,28 @@ public class InterpolateFunction extends GenericFunction
                return "function.interpolate";
        }
 
+       /** @return description key for input parameter */
+       public String getDescriptionKey() {
+               return "dialog.interpolate.parameter.text";
+       }
+
+       /** @return current (or default) parameter value */
+       public int getCurrentParamValue() {
+               return 0;
+       }
+
        /**
         * Perform the operation
         */
        public void begin()
+       {
+               // not needed, we just use the completeFunction method instead
+       }
+
+       /**
+        * Complete the function after the input parameter has been chosen
+        */
+       public void completeFunction(int inParam)
        {
                // Firstly, work out whether the selected range only contains waypoints or not
                final int startIndex = _app.getTrackInfo().getSelection().getStart();
@@ -49,24 +66,12 @@ public class InterpolateFunction extends GenericFunction
                        betweenWaypoints = true;
                }
 
-               // Get number of points to add
-               Object numPointsStr = JOptionPane.showInputDialog(_parentFrame,
-                       I18nManager.getText("dialog.interpolate.parameter.text"),
-                       I18nManager.getText(getNameKey()),
-                       JOptionPane.QUESTION_MESSAGE, null, null, "");
-               if (numPointsStr == null) {return;}
-               int numToAdd = parseNumber(numPointsStr);
-               if (numToAdd <= 0 || numToAdd > 1000)
-               {
-                       _app.showErrorMessage(getNameKey(), "error.interpolate.invalidparameter");
-                       return;
-               }
-
                if (startIndex < 0 || endIndex < 0 || endIndex <= startIndex) {
                        return;
                }
 
                // construct new point array with the interpolated points
+               final int numToAdd = inParam;
                final Track track = _app.getTrackInfo().getTrack();
                final int maxToAdd = (endIndex-startIndex) * numToAdd;
                final int extendedSize = track.getNumPoints() + maxToAdd;
@@ -141,24 +146,4 @@ public class InterpolateFunction extends GenericFunction
                }
                return false;
        }
-
-       /**
-        * Helper method to parse an Object into an integer
-        * @param inObject object, eg from dialog
-        * @return int value given
-        */
-       private static int parseNumber(Object inObject)
-       {
-               int num = 0;
-               if (inObject != null)
-               {
-                       try
-                       {
-                               num = Integer.parseInt(inObject.toString());
-                       }
-                       catch (NumberFormatException nfe)
-                       {}
-               }
-               return num;
-       }
 }
diff --git a/tim/prune/function/RearrangeFunction.java b/tim/prune/function/RearrangeFunction.java
new file mode 100644 (file)
index 0000000..01cd1d1
--- /dev/null
@@ -0,0 +1,207 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.sort.SortMode;
+
+/**
+ * Abstract superclass for the functions which rearrange points,
+ * such as waypoints or photo points
+ */
+public abstract class RearrangeFunction extends GenericFunction
+{
+       /** Function dialog */
+       private JDialog _dialog = null;
+       /** Radio buttons for start/end/nearest */
+       private JRadioButton[] _positionRadios = null;
+       /** Radio buttons for sorting */
+       private JRadioButton[] _sortRadios = null;
+       /** Is the "nearest" option available? */
+       private boolean _nearestAvailable = false;
+
+
+       /** Enumeration for rearrange commands */
+       protected 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
+        * @param isNearestAvailable true if nearest option is visible
+        */
+       public RearrangeFunction(App inApp, boolean isNearestAvailable)
+       {
+               super(inApp);
+               _nearestAvailable = isNearestAvailable;
+       }
+
+       /**
+        * Begin the function by showing the dialog
+        */
+       public void begin()
+       {
+               // Make dialog window
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
+               // If sorting by time isn't available, then disable radio button
+               _sortRadios[2].setEnabled(isSortByTimeAllowed());
+               // Show dialog
+               _dialog.setVisible(true);
+       }
+
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private JPanel makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout());
+               JLabel descLabel = new JLabel(I18nManager.getText(getDescriptionKey()));
+               descLabel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+               dialogPanel.add(descLabel, BorderLayout.NORTH);
+               // Radios for position (start / end / nearest)
+               _positionRadios = new JRadioButton[3];
+               final String[] posNames = {"tostart", "toend", "tonearest"};
+               ButtonGroup posGroup = new ButtonGroup();
+               JPanel posPanel = new JPanel();
+               posPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
+               for (int i=0; i<posNames.length; i++)
+               {
+                       _positionRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrange." + posNames[i]));
+                       posGroup.add(_positionRadios[i]);
+                       posPanel.add(_positionRadios[i]);
+               }
+               _positionRadios[0].setSelected(true);
+               _positionRadios[2].setVisible(_nearestAvailable);
+
+               // Radios for sort (none / filename / time)
+               _sortRadios = new JRadioButton[3];
+               final String[] sortNames = {"nosort", getSortNameKey(), "sortbytime"};
+               ButtonGroup sortGroup = new ButtonGroup();
+               JPanel sortPanel = new JPanel();
+               sortPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
+               for (int i=0; i<3; i++)
+               {
+                       _sortRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrange." + sortNames[i]));
+                       sortGroup.add(_sortRadios[i]);
+                       sortPanel.add(_sortRadios[i]);
+               }
+               _sortRadios[0].setSelected(true);
+               // Use listener to disable all sort options if nearest type chosen, re-enable otherwise
+               ActionListener rearrListener = new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               final boolean sortAvailable = !_positionRadios[2].isSelected();
+                               for (int i=0; i<_sortRadios.length; i++) {
+                                       _sortRadios[i].setEnabled(sortAvailable);
+                               }
+                       }
+               };
+               for (int i=0; i<_positionRadios.length; i++) {
+                       _positionRadios[i].addActionListener(rearrListener);
+               }
+               // add to middle of dialog
+               JPanel centrePanel = new JPanel();
+               centrePanel.setLayout(new BoxLayout(centrePanel, BoxLayout.Y_AXIS));
+               centrePanel.add(posPanel);
+               centrePanel.add(sortPanel);
+               dialogPanel.add(centrePanel, BorderLayout.CENTER);
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton okButton = new JButton(I18nManager.getText("button.ok"));
+               okButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               finish();
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(okButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               return dialogPanel;
+       }
+
+       /**
+        * @return true if sorting by time is allowed, false otherwise
+        */
+       protected boolean isSortByTimeAllowed()
+       {
+               return true;
+       }
+
+       /**
+        * @return the selected rearrange option
+        */
+       protected Rearrange getRearrangeOption()
+       {
+               if (_positionRadios[0].isSelected()) {
+                       return Rearrange.TO_START;
+               }
+               if (_positionRadios[1].isSelected()) {
+                       return Rearrange.TO_END;
+               }
+               return Rearrange.TO_NEAREST;
+       }
+
+       /**
+        * @return the selected sort mode
+        */
+       protected SortMode getSortMode()
+       {
+               if (_sortRadios[0].isSelected()) {
+                       return SortMode.DONT_SORT;
+               }
+               if (_sortRadios[1].isSelected()) {
+                       return SortMode.SORTBY_NAME;
+               }
+               return SortMode.SORTBY_TIME;
+       }
+
+       /** @return key for description */
+       protected abstract String getDescriptionKey();
+
+       /** @return partial key for the sort by name radio */
+       protected abstract String getSortNameKey();
+
+       /**
+        * Perform the rearrange
+        */
+       protected abstract void finish();
+}
index c55928021fcaaa08af3f84fa516bdf8f927f1469..466cd668230a023705f56b001836a0638b405e3d 100644 (file)
 package tim.prune.function;
 
-import java.awt.BorderLayout;
-import java.awt.FlowLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.util.Arrays;
-
-import javax.swing.BoxLayout;
-import javax.swing.ButtonGroup;
-import javax.swing.JButton;
-import javax.swing.JDialog;
-import javax.swing.JLabel;
 import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-
 import tim.prune.App;
-import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Track;
+import tim.prune.data.sort.PhotoComparer;
+import tim.prune.data.sort.SortMode;
 import tim.prune.undo.UndoRearrangePhotos;
 
 /**
  * Class to provide the function for rearranging photo points
  */
-public class RearrangePhotosFunction extends GenericFunction
+public class RearrangePhotosFunction extends RearrangeFunction
 {
-       /** Function dialog */
-       private JDialog _dialog = null;
-       /** Radio buttons for start/end */
-       private JRadioButton[] _positionRadios = null;
-       /** Radio buttons for sorting */
-       private JRadioButton[] _sortRadios = null;
-
-
        /**
         * Constructor
         * @param inApp app object
         */
        public RearrangePhotosFunction(App inApp)
        {
-               super(inApp);
+               super(inApp, false);
        }
 
-       /** Begin the rearrange */
-       public void begin()
-       {
-               // Make dialog window
-               if (_dialog == null)
-               {
-                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
-                       _dialog.setLocationRelativeTo(_parentFrame);
-                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
-                       _dialog.getContentPane().add(makeDialogComponents());
-                       _dialog.pack();
-               }
-               // Reset dialog and show
-               _dialog.setVisible(true);
-       }
-
-       /** Get the name key (not needed) */
+       /** Get the name key */
        public String getNameKey() {
                return "function.rearrangephotos";
        }
 
+       /** Get the description key */
+       public String getDescriptionKey() {
+               return "dialog.rearrangephotos.desc";
+       }
 
-       /**
-        * Create dialog components
-        * @return Panel containing all gui elements in dialog
-        */
-       private JPanel makeDialogComponents()
-       {
-               JPanel dialogPanel = new JPanel();
-               dialogPanel.setLayout(new BorderLayout());
-               dialogPanel.add(new JLabel(I18nManager.getText("dialog.rearrangephotos.desc")), BorderLayout.NORTH);
-               // Radios for position (start / end)
-               _positionRadios = new JRadioButton[2];
-               final String[] posNames = {"tostart", "toend"};
-               ButtonGroup posGroup = new ButtonGroup();
-               JPanel posPanel = new JPanel();
-               posPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
-               for (int i=0; i<2; i++)
-               {
-                       _positionRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrangephotos." + posNames[i]));
-                       posGroup.add(_positionRadios[i]);
-                       posPanel.add(_positionRadios[i]);
-               }
-               _positionRadios[0].setSelected(true);
-               // Radios for sort (none / filename / time)
-               _sortRadios = new JRadioButton[3];
-               final String[] sortNames = {"nosort", "sortbyfilename", "sortbytime"};
-               ButtonGroup sortGroup = new ButtonGroup();
-               JPanel sortPanel = new JPanel();
-               sortPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
-               for (int i=0; i<3; i++)
-               {
-                       _sortRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrangephotos." + sortNames[i]));
-                       sortGroup.add(_sortRadios[i]);
-                       sortPanel.add(_sortRadios[i]);
-               }
-               _sortRadios[0].setSelected(true);
-               // add to middle of dialog
-               JPanel centrePanel = new JPanel();
-               centrePanel.setLayout(new BoxLayout(centrePanel, BoxLayout.Y_AXIS));
-               centrePanel.add(posPanel);
-               centrePanel.add(sortPanel);
-               dialogPanel.add(centrePanel, BorderLayout.CENTER);
-               // button panel at bottom
-               JPanel buttonPanel = new JPanel();
-               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
-               JButton okButton = new JButton(I18nManager.getText("button.ok"));
-               okButton.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               finish();
-                               _dialog.dispose();
-                       }
-               });
-               buttonPanel.add(okButton);
-               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
-               cancelButton.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               _dialog.dispose();
-                       }
-               });
-               buttonPanel.add(cancelButton);
-               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
-               return dialogPanel;
+       /** Sort by filename key */
+       protected String getSortNameKey() {
+               return "sortbyfilename";
        }
 
        /**
         * Perform the rearrange
         */
-       private void finish()
+       protected void finish()
        {
                Track track = _app.getTrackInfo().getTrack();
                UndoRearrangePhotos undo = new UndoRearrangePhotos(track);
@@ -145,33 +55,46 @@ public class RearrangePhotosFunction extends GenericFunction
                for (int i=0; i<numPoints; i++)
                {
                        DataPoint point = track.getPoint(i);
-                       if (point.getPhoto() != null) {
+                       if (point.getPhoto() != null)
+                       {
                                photos[numPhotos] = point;
                                numPhotos++;
                        }
-                       else {
+                       else
+                       {
                                nonPhotos[numNonPhotos] = point;
                                numNonPhotos++;
                        }
                }
-               // Sort photos if necessary
-               if (!_sortRadios[0].isSelected() && numPhotos > 1) {
-                       sortPhotos(photos, _sortRadios[1].isSelected());
-               }
-               // Put the non-photo points and photo points together
-               DataPoint[] neworder = new DataPoint[numPoints];
-               if (_positionRadios[0].isSelected()) {
-                       // photos at front
-                       System.arraycopy(photos, 0, neworder, 0, numPhotos);
-                       System.arraycopy(nonPhotos, 0, neworder, numPhotos, numNonPhotos);
-               }
-               else {
-                       // photos at end
-                       System.arraycopy(nonPhotos, 0, neworder, 0, numNonPhotos);
-                       System.arraycopy(photos, 0, neworder, numNonPhotos, numPhotos);
+               boolean pointsChanged = false;
+               if (numPhotos > 0)
+               {
+                       Rearrange rearrangeOption = getRearrangeOption();
+                       SortMode sortOption = getSortMode();
+                       // Sort photos if necessary
+                       if (sortOption != SortMode.DONT_SORT && numPhotos > 1) {
+                               sortPhotos(photos, sortOption);
+                       }
+                       // Put the non-photo points and photo points together
+                       DataPoint[] neworder = new DataPoint[numPoints];
+                       if (rearrangeOption == Rearrange.TO_START)
+                       {
+                               // photos at front
+                               System.arraycopy(photos, 0, neworder, 0, numPhotos);
+                               System.arraycopy(nonPhotos, 0, neworder, numPhotos, numNonPhotos);
+                       }
+                       else
+                       {
+                               // photos at end
+                               System.arraycopy(nonPhotos, 0, neworder, 0, numNonPhotos);
+                               System.arraycopy(photos, 0, neworder, numNonPhotos, numPhotos);
+                       }
+                       
+                       // Give track the new point order
+                       pointsChanged = track.replaceContents(neworder);
                }
-               // Give track the new point order
-               if (track.replaceContents(neworder))
+               // did anything change?
+               if (pointsChanged)
                {
                        _app.getTrackInfo().getSelection().clearAll();
                        _app.completeFunction(undo, I18nManager.getText("confirm.rearrangephotos"));
@@ -187,12 +110,12 @@ public class RearrangePhotosFunction extends GenericFunction
        /**
         * Sort the given photo list either by filename or by time
         * @param inPhotos array of DataPoint objects to sort
-        * @param inSortByFile true to sort by filename, false to sort by timestamp
+        * @param inSortOrder sort order
         * @return sorted array
         */
-       private static void sortPhotos(DataPoint[] inPhotos, boolean inSortByFile)
+       private static void sortPhotos(DataPoint[] inPhotos, SortMode inSortMode)
        {
-               PhotoComparer comparer = new PhotoComparer(inSortByFile ? PhotoComparer.SortMode.SORTBY_NAME : PhotoComparer.SortMode.SORTBY_TIME);
+               PhotoComparer comparer = new PhotoComparer(inSortMode);
                Arrays.sort(inPhotos, comparer);
        }
 }
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;
+       }
 }
index c4aa7200b3ba5e5f435fcfac4e4d9fab75c1f49a..4f3b0ce7a0bea78614968246b5630a0d3b2d2568 100644 (file)
@@ -48,8 +48,11 @@ public class RemoveAudioFunction extends GenericFunction
                        else
                        {
                                // point is attached, so need to confirm point deletion
+                               final int pointIndex = _app.getTrackInfo().getTrack().getPointIndex(currentAudio.getDataPoint());
                                undoAction = new UndoDeleteAudio(currentAudio, _app.getTrackInfo().getSelection().getCurrentAudioIndex(),
-                                       currentAudio.getDataPoint(), _app.getTrackInfo().getTrack().getPointIndex(currentAudio.getDataPoint()));
+                                       currentAudio.getDataPoint(), pointIndex);
+                               undoAction.setAtBoundaryOfSelectedRange(pointIndex == _app.getTrackInfo().getSelection().getStart() ||
+                                       pointIndex == _app.getTrackInfo().getSelection().getEnd());
                                int response = JOptionPane.showConfirmDialog(_app.getFrame(),
                                        I18nManager.getText("dialog.deleteaudio.deletepoint"),
                                        I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION);
index 8cdf7bcf3cc935bddf3c47a374b85e460db1db66..86f3ae80621281ad1ec8a1b3498b84be38875c1a 100644 (file)
@@ -48,8 +48,11 @@ public class RemovePhotoFunction extends GenericFunction
                        else
                        {
                                // point is attached, so need to confirm point deletion
+                               final int pointIndex = _app.getTrackInfo().getTrack().getPointIndex(currentPhoto.getDataPoint());
                                undoAction = new UndoDeletePhoto(currentPhoto, _app.getTrackInfo().getSelection().getCurrentPhotoIndex(),
-                                       currentPhoto.getDataPoint(), _app.getTrackInfo().getTrack().getPointIndex(currentPhoto.getDataPoint()));
+                                       currentPhoto.getDataPoint(), pointIndex);
+                               undoAction.setAtBoundaryOfSelectedRange(pointIndex == _app.getTrackInfo().getSelection().getStart() ||
+                                       pointIndex == _app.getTrackInfo().getSelection().getEnd());
                                int response = JOptionPane.showConfirmDialog(_app.getFrame(),
                                        I18nManager.getText("dialog.deletephoto.deletepoint"),
                                        I18nManager.getText("dialog.deletephoto.title"),
index 489486d64b1119775455a66cead0a47d02eea03b..7d730d1b5763d5f819a1dfdf5a1cda70ea0e4b3b 100644 (file)
@@ -158,6 +158,7 @@ public class SaveConfig extends GenericFunction
         */
        private void saveConfig(File inSaveFile)
        {
+               // TODO: Check for null inSaveFile, then just call finish() ?
                FileOutputStream outStream = null;
                try
                {
index 5712983394e24882d7b1098a1eeead49d92f41e8..5ab3950f83483ab1b4d4428f8734de14f7ce72e0 100644 (file)
@@ -98,7 +98,7 @@ public class SearchWikipediaNames extends GenericDownloaderFunction
 
                // Set status label according to error or "none found", leave blank if ok
                if (_errorMessage == null && _trackListModel.isEmpty()) {
-                       _errorMessage = I18nManager.getText("dialog.gpsies.nonefound");
+                       _errorMessage = I18nManager.getText("dialog.wikipedia.nonefound");
                }
                _statusLabel.setText(_errorMessage == null ? "" : _errorMessage);
        }
diff --git a/tim/prune/function/SelectSegmentFunction.java b/tim/prune/function/SelectSegmentFunction.java
new file mode 100644 (file)
index 0000000..661eee0
--- /dev/null
@@ -0,0 +1,47 @@
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.data.Checker;
+import tim.prune.data.DataPoint;
+
+/**
+ * Function to allow the selection of which tracks to load from the file / stream
+ */
+public class SelectSegmentFunction extends GenericFunction
+{
+
+       /**
+        * Constructor
+        * @param inApp app object to use for load
+        */
+       public SelectSegmentFunction(App inApp)
+       {
+               super(inApp);
+       }
+
+       /**
+        * Start the function
+        */
+       public void begin()
+       {
+               // If no point selected, or a waypoint is selected, then do nothing
+               DataPoint currPoint = _app.getTrackInfo().getCurrentPoint();
+               if (currPoint != null && !currPoint.isWaypoint())
+               {
+                       // Find indexes of segment start and end
+                       final int currIndex = _app.getTrackInfo().getSelection().getCurrentPointIndex();
+                       final int startIndex = Checker.getPreviousSegmentStart(_app.getTrackInfo().getTrack(), currIndex+1);
+                       final int endIndex   = Checker.getNextSegmentEnd(_app.getTrackInfo().getTrack(), currIndex);
+                       // Select this range if there is one
+                       if (endIndex > startIndex) {
+                               _app.getTrackInfo().getSelection().selectRange(startIndex, endIndex);
+                       }
+               }
+       }
+
+       /** @return name key */
+       public String getNameKey() {
+               return "function.selectsegment";
+       }
+}
diff --git a/tim/prune/function/SetAltitudeTolerance.java b/tim/prune/function/SetAltitudeTolerance.java
new file mode 100644 (file)
index 0000000..c51a56a
--- /dev/null
@@ -0,0 +1,75 @@
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
+import tim.prune.data.Unit;
+
+/**
+ * Function to set the tolerance for the altitude range calculations
+ */
+public class SetAltitudeTolerance extends SingleNumericParameterFunction
+{
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public SetAltitudeTolerance(App inApp) {
+               super(inApp, 0, 100);
+       }
+
+       /** @return name key */
+       public String getNameKey() {
+               return "function.setaltitudetolerance";
+       }
+
+       /**
+        * @return description key
+        */
+       public String getDescriptionKey()
+       {
+               // Two different keys for feet and metres
+               final boolean isMetres = Config.getUnitSet().getAltitudeUnit().isStandard();
+               return "dialog.setaltitudetolerance.text." + (isMetres ? "metres" : "feet");
+       }
+
+       /**
+        * @return the current value to display
+        */
+       public int getCurrentParamValue()
+       {
+               int configVal = Config.getConfigInt(Config.KEY_ALTITUDE_TOLERANCE);
+               // Convert this to feet if necessary
+               Unit altUnit = Config.getUnitSet().getAltitudeUnit();
+               if (altUnit.isStandard()) {
+                       return configVal / 100;
+               }
+               return (int) (configVal * altUnit.getMultFactorFromStd() / 100.0);
+       }
+
+       /**
+        * Run function
+        */
+       public void begin()
+       {
+               // Not required, because this function is started from a ChooseSingleParameter function
+               // and goes directly to the completeFunction method.
+       }
+
+       /**
+        * Complete the function using the given tolerance parameter
+        */
+       public void completeFunction(int inTolerance)
+       {
+               // Convert back from feet into metres again
+               Unit altUnit = Config.getUnitSet().getAltitudeUnit();
+               int configVal = inTolerance * 100;
+               if (!altUnit.isStandard()) {
+                       configVal = (int) (inTolerance * 100.0 / altUnit.getMultFactorFromStd());
+               }
+               Config.setConfigInt(Config.KEY_ALTITUDE_TOLERANCE, configVal);
+               UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
+       }
+}
index 1329d05fe7d9d54165161734c968eaf6d8e44e67..33b4357dbdd648880e0401d510eded55785cbff3 100644 (file)
@@ -1,15 +1,11 @@
 package tim.prune.function;
 
 import java.awt.BorderLayout;
-import java.awt.Color;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-
 import javax.swing.BorderFactory;
 import javax.swing.Box;
 import javax.swing.BoxLayout;
@@ -24,8 +20,11 @@ import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
 import tim.prune.config.ColourScheme;
 import tim.prune.config.Config;
-import tim.prune.gui.ColourChooser;
-import tim.prune.gui.ColourPatch;
+import tim.prune.gui.colour.ColourChooser;
+import tim.prune.gui.colour.ColourPatch;
+import tim.prune.gui.colour.ColourerSelectorPanel;
+import tim.prune.gui.colour.PatchListener;
+import tim.prune.gui.colour.PointColourer;
 
 /**
  * Class to show the popup window for setting the colours
@@ -36,6 +35,8 @@ public class SetColours extends GenericFunction
        private JButton _okButton = null;
        /** Array of 8 colour patches */
        private ColourPatch[] _patches = null;
+       /** colourer selection panel */
+       private ColourerSelectorPanel _colourerSelector = null;
        /** Single colour chooser */
        private ColourChooser _colourChooser = null;
 
@@ -49,25 +50,6 @@ public class SetColours extends GenericFunction
                ColourScheme.IDX_TEXT, ColourScheme.IDX_LINES
        };
 
-       /**
-        * Inner class to react to patch clicks
-        */
-       class PatchListener extends MouseAdapter
-       {
-               /** Associated patch */
-               private ColourPatch _patch = null;
-               /** Constructor */
-               public PatchListener(ColourPatch inPatch) {
-                       _patch = inPatch;
-               }
-               /** React to mouse clicks */
-               public void mouseClicked(MouseEvent e)
-               {
-                       _colourChooser.showDialog(_patch.getBackground());
-                       Color colour = _colourChooser.getChosenColour();
-                       if (colour != null) _patch.setColour(colour);
-               }
-       }
 
        /**
         * Constructor
@@ -94,11 +76,17 @@ public class SetColours extends GenericFunction
                JPanel mainPanel = new JPanel();
                mainPanel.setLayout(new BorderLayout(0, 10));
 
-               JLabel intro = new JLabel(I18nManager.getText("dialog.setcolours.intro"));
-               intro.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
-               mainPanel.add(intro, BorderLayout.NORTH);
+               JLabel introLabel = new JLabel(I18nManager.getText("dialog.setcolours.intro"));
+               introLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
+               mainPanel.add(introLabel, BorderLayout.NORTH);
+
+               // Panel in centre, to hold both the patch panel and the colourer panel (and maybe introLabel too?)
                JPanel centralPanel = new JPanel();
-               centralPanel.setLayout(new GridLayout());
+               centralPanel.setLayout(new BoxLayout(centralPanel, BoxLayout.Y_AXIS));
+
+               // Make panel for 8 colour patches
+               JPanel patchPanel = new JPanel();
+               patchPanel.setLayout(new GridLayout());
                _patches = new ColourPatch[8];
 
                ColourScheme scheme = Config.getColourScheme();
@@ -111,7 +99,7 @@ public class SetColours extends GenericFunction
                        // Top label and patch
                        colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2])));
                        patch = new ColourPatch(scheme.getColour(INDICES[i*2]));
-                       patch.addMouseListener(new PatchListener(patch));
+                       patch.addMouseListener(new PatchListener(patch, _colourChooser));
                        colPanel.add(patch);
                        _patches[i*2] = patch;
                        // separator
@@ -119,14 +107,23 @@ public class SetColours extends GenericFunction
                        // Bottom label and patch
                        colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2+1])));
                        patch = new ColourPatch(scheme.getColour(INDICES[i*2+1]));
-                       patch.addMouseListener(new PatchListener(patch));
+                       patch.addMouseListener(new PatchListener(patch, _colourChooser));
                        colPanel.add(patch);
                        _patches[i*2+1] = patch;
 
                        // Add column to panel
                        colPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
-                       centralPanel.add(colPanel);
+                       patchPanel.add(colPanel);
                }
+               patchPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               centralPanel.add(patchPanel);
+               centralPanel.add(Box.createVerticalStrut(15));
+
+               // now the colourer selector
+               _colourerSelector = new ColourerSelectorPanel(_colourChooser);
+               _colourerSelector.setAlignmentX(Component.LEFT_ALIGNMENT);
+               centralPanel.add(_colourerSelector);
+               // add the central panel to the main one
                mainPanel.add(centralPanel, BorderLayout.CENTER);
 
                // Buttons at the bottom
@@ -176,15 +173,17 @@ public class SetColours extends GenericFunction
                {
                        _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()));
                        _dialog.setLocationRelativeTo(_parentFrame);
+                       _colourChooser = new ColourChooser(_dialog);
                        _dialog.getContentPane().add(makeContents());
                        _dialog.pack();
-                       _colourChooser = new ColourChooser(_dialog);
                }
                // Reset colours to current ones
                ColourScheme scheme = Config.getColourScheme();
                for (int i=0; i<8; i++) {
                        _patches[i].setColour(scheme.getColour(INDICES[i]));
                }
+               PointColourer colourer = Config.getPointColourer();
+               _colourerSelector.init(colourer, scheme.getColour(ColourScheme.IDX_POINT));
                _dialog.setVisible(true);
                _okButton.requestFocus();
        }
@@ -200,6 +199,9 @@ public class SetColours extends GenericFunction
                        scheme.setColour(INDICES[i], _patches[i].getBackground());
                }
                Config.updateColourScheme();
+               PointColourer colourer = _colourerSelector.getSelectedColourer();
+               Config.updatePointColourer(colourer);
+               _app.updatePointColourer();
                UpdateMessageBroker.informSubscribers();
        }
 }
index 5ac64ae3e3a78a2f009f98078249f9d5e2b7880c..feb49c24958855a1f40c253703cf9f0827e07de0 100644 (file)
@@ -1,15 +1,14 @@
 package tim.prune.function;
 
-import javax.swing.JOptionPane;
-
 import tim.prune.App;
 import tim.prune.DataSubscriber;
-import tim.prune.GenericFunction;
-import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
 import tim.prune.config.Config;
 
-public class SetLineWidth extends GenericFunction
+/**
+ * Function to set the width with which lines are drawn
+ */
+public class SetLineWidth extends SingleNumericParameterFunction
 {
 
        /**
@@ -17,7 +16,7 @@ public class SetLineWidth extends GenericFunction
         * @param inApp App object
         */
        public SetLineWidth(App inApp) {
-               super(inApp);
+               super(inApp, 1, 4);
        }
 
        /** @return name key */
@@ -25,32 +24,35 @@ public class SetLineWidth extends GenericFunction
                return "function.setlinewidth";
        }
 
+       /** @return description key */
+       public String getDescriptionKey() {
+               return "dialog.setlinewidth.text";
+       }
+
+       /** @return the current value to display */
+       public int getCurrentParamValue() {
+               return Config.getConfigInt(Config.KEY_LINE_WIDTH);
+       }
 
        /**
         * Run function
         */
        public void begin()
        {
-               int currLineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH);
-               if (currLineWidth < 1 || currLineWidth > 4) {
-                       currLineWidth = 2;
-               }
-               Object lineWidthStr = JOptionPane.showInputDialog(_app.getFrame(),
-                       I18nManager.getText("dialog.setlinewidth.text"),
-                       I18nManager.getText(getNameKey()),
-                       JOptionPane.QUESTION_MESSAGE, null, null, "" + currLineWidth);
-               if (lineWidthStr != null)
+               // Not required, because this function is started from a ChooseSingleParameter function
+               // and goes directly to the completeFunction method.
+       }
+
+       /**
+        * Complete the function using the given line width parameter
+        */
+       public void completeFunction(int inLineWidth)
+       {
+               final int currLineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH);
+               if (inLineWidth >= 1 && inLineWidth <= 4 && inLineWidth != currLineWidth)
                {
-                       int lineWidth = 2;
-                       try {
-                               lineWidth = Integer.parseInt(lineWidthStr.toString());
-                               if (lineWidth >= 1 && lineWidth <= 4 && lineWidth != currLineWidth)
-                               {
-                                       Config.setConfigInt(Config.KEY_LINE_WIDTH, lineWidth);
-                                       UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
-                               }
-                       }
-                       catch (NumberFormatException nfe) {};
+                       Config.setConfigInt(Config.KEY_LINE_WIDTH, inLineWidth);
+                       UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
                }
        }
 }
index 40c4e5af9f2a6e2b0057c81fce60ebb29ec6ce51..c2e285b3fc2839cecbfc38debacb880817631dce 100644 (file)
@@ -156,8 +156,12 @@ public class ShowThreeDFunction extends GenericFunction
         */
        private void finish()
        {
-               // Store exaggeration factor in config
+               // Store exaggeration factor and grid size in config
                Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_exaggField.getValue() * 100));
+               int terrainGridSize = _terrainPanel.getGridSize();
+               if (terrainGridSize < 20) {terrainGridSize = 20;}
+               Config.setConfigInt(Config.KEY_TERRAIN_GRID_SIZE, terrainGridSize);
+
                ThreeDWindow window = WindowFactory.getWindow(_parentFrame);
                if (window != null)
                {
diff --git a/tim/prune/function/SingleNumericParameterFunction.java b/tim/prune/function/SingleNumericParameterFunction.java
new file mode 100644 (file)
index 0000000..3f6062b
--- /dev/null
@@ -0,0 +1,36 @@
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+
+/**
+ * Abstract superclass of Functions which just take a
+ * single numeric parameter
+ */
+public abstract class SingleNumericParameterFunction extends GenericFunction
+{
+       /** Minimum and maximum allowed values */
+       protected int _minAllowedValue, _maxAllowedValue;
+
+       /** Constructor */
+       public SingleNumericParameterFunction(App inApp, int inMinValue, int inMaxValue)
+       {
+               super(inApp);
+               _minAllowedValue = inMinValue;
+               _maxAllowedValue = inMaxValue;
+       }
+
+       /** Get the current value for display in the dialog */
+       public abstract int getCurrentParamValue();
+
+       /** Get the key for the description label */
+       public abstract String getDescriptionKey();
+
+       /** Callback to trigger the rest of the function once the parameter has been chosen */
+       public abstract void completeFunction(int inParam);
+
+       /** @return minimum allowed value */
+       public int getMinAllowedValue() {return _minAllowedValue;}
+       /** @return maximum allowed value */
+       public int getMaxAllowedValue() {return _maxAllowedValue;}
+}
index 842084da8b9d7b03ce14c84569e0164e1cf4c3d0..ef3cc955f61f260f6ce3ed724424631d6a2c7a53 100644 (file)
@@ -284,6 +284,9 @@ public class Charter extends GenericFunction
                                        + getSvgValue(_svgHeightField, DEFAULT_SVG_HEIGHT) + "\n");
                                writer.write("set out '" + svgFile.getAbsolutePath() + "'\n");
                        }
+                       else {
+                               // For screen output, gnuplot should use the default terminal (windows or x11 or wxt or something)
+                       }
                        if (numCharts > 1) {
                                writer.write("set multiplot layout " + numCharts + ",1\n");
                        }
@@ -374,7 +377,7 @@ public class Charter extends GenericFunction
                        break;
                }
                // Make a temporary data file for the output (one per subchart)
-               File tempFile = File.createTempFile("prunedata", null);
+               File tempFile = File.createTempFile("gpsprunedata", null);
                tempFile.deleteOnExit();
                // write out values for x and y to temporary file
                FileWriter tempFileWriter = null;
index ce0203cb9e221d3b8f00dd82e0d998629d6e1ee1..8e89360d7f79ba820f2743a001e319a42dd99a6c 100644 (file)
@@ -15,7 +15,6 @@ import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 
 import tim.prune.App;
-import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
 import tim.prune.data.DataPoint;
@@ -24,15 +23,13 @@ import tim.prune.data.Track;
 /**
  * Class to provide the function for track compression
  */
-public class CompressTrackFunction extends GenericFunction
+public class CompressTrackFunction extends MarkAndDeleteFunction
 {
        private Track _track = null;
        private JDialog _dialog = null;
        private JButton _okButton = null;
        private CompressionAlgorithm[] _algorithms = null;
        private SummaryLabel _summaryLabel = null;
-       /** flag to remember whether the automatic deletion has been set to always */
-       private boolean _automaticallyDelete = false;
 
 
        /**
@@ -187,23 +184,7 @@ public class CompressTrackFunction extends GenericFunction
                // Show confirmation dialog with OK button (not status bar message)
                if (numMarked > 0)
                {
-                       // Allow calling of delete function with one click
-                       final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
-                               I18nManager.getText("button.always")};
-                       int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
-                               JOptionPane.showOptionDialog(_parentFrame,
-                               I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked),
-                               I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
-                               JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
-                       if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
-                       if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
-                       {
-                               new Thread(new Runnable() {
-                                       public void run() {
-                                               _app.finishCompressTrack();
-                                       }
-                               }).start();
-                       }
+                       optionallyDeleteMarkedPoints(numMarked);
                }
                else
                {
diff --git a/tim/prune/function/compress/MarkAndDeleteFunction.java b/tim/prune/function/compress/MarkAndDeleteFunction.java
new file mode 100644 (file)
index 0000000..2189ecb
--- /dev/null
@@ -0,0 +1,51 @@
+package tim.prune.function.compress;
+
+import javax.swing.JOptionPane;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+
+/**
+ * Superclass of those functions which mark points for deletion
+ * (and optionally delete them automatically)
+ */
+public abstract class MarkAndDeleteFunction extends GenericFunction
+{
+       /** flag to remember whether the automatic deletion has been set to always */
+       private boolean _automaticallyDelete = false;
+
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public MarkAndDeleteFunction(App inApp)
+       {
+               super(inApp);
+       }
+
+       /**
+        * optionally delete the marked points
+        */
+       protected void optionallyDeleteMarkedPoints(int inNumMarked)
+       {
+               // Allow calling of delete function with one click
+               final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
+                       I18nManager.getText("button.always")};
+               int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
+                       JOptionPane.showOptionDialog(_parentFrame,
+                       I18nManager.getTextWithNumber("dialog.compress.confirm", inNumMarked),
+                       I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
+                       JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
+               if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
+               if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
+               {
+                       new Thread(new Runnable() {
+                               public void run() {
+                                       _app.finishCompressTrack();
+                               }
+                       }).start();
+               }
+       }
+}
index f6ffe208b9383e68ab7f22177060a85c8646d0c2..2ea0f8ca64c495486cec33d3a94d82598f5f2b5d 100644 (file)
@@ -1,24 +1,18 @@
 package tim.prune.function.compress;
 
-import javax.swing.JOptionPane;
-
 import tim.prune.App;
-import tim.prune.GenericFunction;
-import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
 import tim.prune.data.DataPoint;
 
 /**
  * Function to mark all the points in the selected rectangle
  */
-public class MarkPointsInRectangleFunction extends GenericFunction
+public class MarkPointsInRectangleFunction extends MarkAndDeleteFunction
 {
        /** Minimum and maximum latitude values of rectangle */
        private double _minLat = 0.0, _maxLat = 0.0;
        /** Minimum and maximum longitude values of rectangle */
        private double _minLon = 0.0, _maxLon = 0.0;
-       /** flag to remember whether the automatic deletion has been set to always */
-       private boolean _automaticallyDelete = false;
 
 
        /**
@@ -86,7 +80,7 @@ public class MarkPointsInRectangleFunction extends GenericFunction
                        final double pointLat = point.getLatitude().getDouble();
                        final boolean insideRect = (pointLon >= _minLon && pointLon <= _maxLon
                                && pointLat >= _minLat && pointLat <= _maxLat);
-                       // If so, then mark it
+                       // Mark it accordingly (also resetting points outside the rect to false)
                        point.setMarkedForDeletion(insideRect);
                        if (insideRect) {
                                numMarked++;
@@ -98,23 +92,7 @@ public class MarkPointsInRectangleFunction extends GenericFunction
                // Confirm message showing how many marked
                if (numMarked > 0)
                {
-                       // Allow calling of delete function with one click
-                       final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
-                               I18nManager.getText("button.always")};
-                       int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
-                               JOptionPane.showOptionDialog(_parentFrame,
-                               I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked),
-                               I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
-                               JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
-                       if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
-                       if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
-                       {
-                               new Thread(new Runnable() {
-                                       public void run() {
-                                               _app.finishCompressTrack();
-                                       }
-                               }).start();
-                       }
+                       optionallyDeleteMarkedPoints(numMarked);
                }
        }
 }
diff --git a/tim/prune/function/deletebydate/DateInfo.java b/tim/prune/function/deletebydate/DateInfo.java
new file mode 100644 (file)
index 0000000..0c2cedb
--- /dev/null
@@ -0,0 +1,112 @@
+package tim.prune.function.deletebydate;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+/**
+ * Class to hold the information about a date,
+ * including how many points correspond to the date
+ * and whether it has been selected for deletion or not
+ */
+public class DateInfo implements Comparable<DateInfo>
+{
+       /** Date, or null for no date - used for earlier/later comparison */
+       private Date _date = null;
+       /** String representation of date, for equality comparison */
+       private String _dateString = null;
+       /** Number of points with this date */
+       private int _numPoints = 0;
+       /** Flag for deletion or retention */
+       private boolean _toDelete = false;
+
+       // Doesn't really matter what format is used here, as long as dates are different
+       private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance();
+
+       /**
+        * Constructor
+        * @param inDate date object from timestamp
+        */
+       public DateInfo(Date inDate)
+       {
+               _date = inDate;
+               if (_date == null) {
+                       _dateString = "";
+               }
+               else {
+                       _dateString = DEFAULT_DATE_FORMAT.format(_date);
+               }
+               _numPoints = 0;
+               _toDelete = false;
+       }
+
+       /**
+        * @return true if this info is for dateless points (points without timestamp)
+        */
+       public boolean isDateless() {
+               return (_date == null);
+       }
+
+       /**
+        * @return date object, or null
+        */
+       public Date getDate() {
+               return _date;
+       }
+
+       /**
+        * Compare with a given Date object to see if they represent the same date
+        * @param inDate date to compare
+        * @return true if they're the same date
+        */
+       public boolean isSameDate(Date inDate)
+       {
+               if (inDate == null) {
+                       return (_date == null);
+               }
+               else if (_dateString == null) {
+                       return false;
+               }
+               String otherDateString = DEFAULT_DATE_FORMAT.format(inDate);
+               return _dateString.equals(otherDateString);
+       }
+
+       /**
+        * Increment the point count
+        */
+       public void incrementCount() {
+               _numPoints++;
+       }
+
+       /**
+        * @return point count
+        */
+       public int getPointCount() {
+               return _numPoints;
+       }
+
+       /**
+        * @param inFlag true to delete, false to keep
+        */
+       public void setDeleteFlag(boolean inFlag) {
+               _toDelete = inFlag;
+       }
+
+       /**
+        * @return true to delete, false to keep
+        */
+       public boolean getDeleteFlag() {
+               return _toDelete;
+       }
+
+       /**
+        * Compare with another DateInfo object for sorting
+        */
+       public int compareTo(DateInfo inOther)
+       {
+               // Dateless goes first
+               if (_date == null || _dateString == null) {return -1;}
+               if (inOther._date == null || inOther._dateString == null) {return 1;}
+               // Just compare dates
+               return _date.compareTo(inOther._date);
+       }
+}
diff --git a/tim/prune/function/deletebydate/DateInfoList.java b/tim/prune/function/deletebydate/DateInfoList.java
new file mode 100644 (file)
index 0000000..7628667
--- /dev/null
@@ -0,0 +1,121 @@
+package tim.prune.function.deletebydate;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * List of date info objects for use by the table model
+ */
+public class DateInfoList
+{
+       /** list of info about points according to date */
+       private List<DateInfo> _infoList = new ArrayList<DateInfo>();
+       /** previously used dateinfo object to reduce list searching */
+       private DateInfo _previousInfo = null;
+       /** true if the list has been sorted, false otherwise */
+       private boolean _hasBeenSorted = false;
+
+
+       /**
+        * Add a point to the corresponding dateinfo
+        * @param inDate date of current point, or null if no timestamp
+        */
+       public void addPoint(Date inDate)
+       {
+               if (_previousInfo != null && _previousInfo.isSameDate(inDate))
+               {
+                       // found it
+                       _previousInfo.incrementCount();
+               }
+               else
+               {
+                       // loop through list, seeing if date already present
+                       boolean foundDate = false;
+                       for (DateInfo info : _infoList)
+                       {
+                               if (info.isSameDate(inDate))
+                               {
+                                       info.incrementCount();
+                                       _previousInfo = info;
+                                       foundDate = true;
+                                       break;
+                               }
+                       }
+                       // create new info if necessary
+                       if (!foundDate)
+                       {
+                               _previousInfo = new DateInfo(inDate);
+                               _previousInfo.incrementCount();
+                               _infoList.add(_previousInfo);
+                               _hasBeenSorted = false;
+                       }
+               }
+       }
+
+       /**
+        * Clear the whole list
+        */
+       public void clearAll()
+       {
+               _infoList.clear();
+               _previousInfo = null;
+               _hasBeenSorted = true;
+       }
+
+       /**
+        * not used, can be removed
+        * @return true if any points without dates were found
+        */
+       public boolean hasDatelessPoints()
+       {
+               if (_infoList.isEmpty()) {return false;}
+               sort();
+               DateInfo firstInfo = _infoList.get(0);
+               return (firstInfo != null && firstInfo.isDateless() && firstInfo.getPointCount() > 0);
+       }
+
+       /**
+        * @return number of entries in the list, including dateless points
+        */
+       public int getNumEntries()
+       {
+               return _infoList.size();
+       }
+
+       /**
+        * @return the total number of points found, which should match the track size
+        */
+       public int getTotalNumPoints()
+       {
+               int total = 0;
+               for (DateInfo info : _infoList) {
+                       total += info.getPointCount();
+               }
+               return total;
+       }
+
+       /**
+        * Sort the info list by ascending timestamps
+        */
+       private void sort()
+       {
+               if (!_hasBeenSorted)
+               {
+                       Collections.sort(_infoList);
+                       _hasBeenSorted = true;
+               }
+       }
+
+       /**
+        * Get the DateInfo object at the given index
+        * @param inIndex index in (sorted) list
+        * @return corresponding object (may throw exception if out of range)
+        */
+       public DateInfo getDateInfo(int inIndex)
+       {
+               sort();
+               return _infoList.get(inIndex);
+       }
+}
diff --git a/tim/prune/function/deletebydate/DeleteByDateFunction.java b/tim/prune/function/deletebydate/DeleteByDateFunction.java
new file mode 100644 (file)
index 0000000..3085aa3
--- /dev/null
@@ -0,0 +1,215 @@
+package tim.prune.function.deletebydate;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.Date;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.DataPoint;
+import tim.prune.function.compress.MarkAndDeleteFunction;
+
+/**
+ * Function to select a date or dates,
+ * and delete the corresponding points
+ */
+public class DeleteByDateFunction extends MarkAndDeleteFunction
+{
+       /** dialog for selecting dates */
+       private JDialog _dialog = null;
+       /** Ok button */
+       private JButton _okButton = null;
+       /** date info list */
+       private DateInfoList _infoList = new DateInfoList();
+
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public DeleteByDateFunction(App inApp)
+       {
+               super(inApp);
+       }
+
+       @Override
+       public String getNameKey() {
+               return "function.deletebydate";
+       }
+
+       @Override
+       public void begin()
+       {
+               // Make a list of which dates are present in the track
+               _infoList.clearAll();
+               final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
+               for (int i=0; i<numPoints; i++)
+               {
+                       DataPoint point = _app.getTrackInfo().getTrack().getPoint(i);
+                       if (point != null)
+                       {
+                               if (point.hasTimestamp()) {
+                                       _infoList.addPoint(point.getTimestamp().getCalendar().getTime());
+                               }
+                               else {
+                                       _infoList.addPoint(null); // no timestamp available
+                               }
+                       }
+               }
+//             System.out.println("Debug: info list has dateless points? " + (_infoList.hasDatelessPoints() ? "yes":"no"));
+//             System.out.println("Debug: info list has " + _infoList.getNumEntries() + " different entries");
+//             System.out.println("Debug: info list has " + _infoList.getTotalNumPoints() + " total points");
+//             final boolean checkOk = (_infoList.getTotalNumPoints() == numPoints);
+//             System.out.println("Debug: which " + (checkOk?"IS":"ISN'T!") + " the same as track: " + numPoints);
+
+               // Loop over entries for debug
+//             if (!checkOk)
+//             {
+//                     for (int i=0; i<_infoList.getNumEntries(); i++)
+//                     {
+//                             DateInfo info = _infoList.getDateInfo(i);
+//                             System.out.println("   " + i + " (" + info.getPointCount() + " points) - " +
+//                                     (info.isDateless() ? "no date" : "date"));
+//                     }
+//             }
+
+               // Complain if there is only one entry in the list - this means all points are on the same day
+               if (_infoList.getNumEntries() < 2)
+               {
+                       _app.showErrorMessage(getNameKey(), "dialog.deletebydate.onlyonedate");
+               }
+               else
+               {
+                       // Create and build dialog if necessary
+                       if (_dialog == null)
+                       {
+                               _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                               _dialog.setLocationRelativeTo(_parentFrame);
+                               _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                               _dialog.getContentPane().add(makeDialogComponents());
+                               _dialog.pack();
+                       }
+                       // Show dialog
+                       _dialog.setVisible(true);
+               }
+       }
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout(5, 5));
+               // Label at top
+               JLabel topLabel = new JLabel(I18nManager.getText("dialog.deletebydate.intro"));
+               topLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+               dialogPanel.add(topLabel, BorderLayout.NORTH);
+
+               // close window if escape pressed
+               KeyAdapter escListener = new KeyAdapter() {
+                       public void keyReleased(KeyEvent inE) {
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
+                                       _dialog.dispose();
+                               }
+                       }
+               };
+
+               JTable infoTable = new JTable(new DeletionTableModel(_infoList));
+               JScrollPane pane = new JScrollPane(infoTable);
+               pane.setPreferredSize(new Dimension(300, 80));
+               pane.setBorder(BorderFactory.createEmptyBorder(2, 50, 2, 50));
+               dialogPanel.add(pane, BorderLayout.CENTER);
+
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               // OK button
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               _okButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               performDelete();
+                       }
+               });
+               buttonPanel.add(_okButton);
+               _okButton.addKeyListener(escListener);
+               // Cancel button
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               _dialog.dispose();
+                       }
+               });
+               cancelButton.addKeyListener(new KeyAdapter() {
+                       public void keyPressed(KeyEvent inE) {
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               return dialogPanel;
+       }
+
+       /**
+        * Do the actual point deletion according to the
+        * selected rows in the table
+        */
+       private void performDelete()
+       {
+               int numMarked = 0;
+               final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
+               final int numDates = _infoList.getNumEntries();
+               // Loop over all points to mark each one for deletion or not
+               for (int p=0; p<numPoints; p++)
+               {
+                       DataPoint point = _app.getTrackInfo().getTrack().getPoint(p);
+                       if (point != null)
+                       {
+                               Date date = (point.hasTimestamp() ? point.getTimestamp().getCalendar().getTime() : null);
+                               boolean pointMarked = false;
+                               // Try to match each of the date info objects in the list
+                               for (int d=0; d<numDates; d++)
+                               {
+                                       DateInfo info = _infoList.getDateInfo(d);
+                                       if ( (info.isDateless() && date == null) // matches dateless
+                                         || (!info.isDateless() && date != null && info.isSameDate(date)))
+                                       {
+                                               pointMarked = info.getDeleteFlag();
+                                               break;
+                                       }
+                               }
+                               point.setMarkedForDeletion(pointMarked);
+                               if (pointMarked) {
+                                       numMarked++;
+                               }
+                       }
+               }
+               // Now points have been marked, we can ask user to delete them (or delete automatically)
+               if (numMarked > 0) {
+                       optionallyDeleteMarkedPoints(numMarked);
+               }
+               else {
+                       // Do nothing   //System.out.println("Nothing selected to delete!");
+                       // delete flags might have been reset, so refresh display
+                       UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
+               }
+               _dialog.dispose();
+       }
+}
diff --git a/tim/prune/function/deletebydate/DeletionTableModel.java b/tim/prune/function/deletebydate/DeletionTableModel.java
new file mode 100644 (file)
index 0000000..202e01e
--- /dev/null
@@ -0,0 +1,140 @@
+package tim.prune.function.deletebydate;
+
+import java.text.DateFormat;
+import javax.swing.table.AbstractTableModel;
+import tim.prune.I18nManager;
+
+/**
+ * Table model for selecting which dates to delete
+ */
+public class DeletionTableModel extends AbstractTableModel
+{
+       /** info list, one for each row of table */
+       private DateInfoList _infoList = null;
+
+       /** Formatter, determining how dates appear in the table */
+       private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance();
+       /** Column heading for date */
+       private static final String COLUMN_HEADING_DATE = I18nManager.getText("fieldname.date");
+       /** Column heading for number of points */
+       private static final String COLUMN_HEADING_NUMPOINTS = I18nManager.getText("details.track.points");
+       /** Column heading for keep */
+       private static final String COLUMN_HEADING_KEEP = I18nManager.getText("dialog.deletebydate.column.keep");
+       /** Column heading for delete */
+       private static final String COLUMN_HEADING_DELETE = I18nManager.getText("dialog.deletebydate.column.delete");
+
+
+       /**
+        * Constructor
+        * @param inList date info list from function
+        */
+       public DeletionTableModel(DateInfoList inList)
+       {
+               _infoList = inList;
+       }
+
+       /**
+        * @return column count
+        */
+       public int getColumnCount()
+       {
+               return 4; // always fixed (date, numpoints, keep, delete)
+       }
+
+       /**
+        * @return row count
+        */
+       public int getRowCount()
+       {
+               if (_infoList == null) {return 0;} // shouldn't happen
+               return _infoList.getNumEntries();
+       }
+
+       /**
+        * Get the name of the column
+        * @param inColNum column number
+        * @return column name
+        */
+       public String getColumnName(int inColNum)
+       {
+               if (inColNum == 0) return COLUMN_HEADING_DATE;
+               else if (inColNum == 1) return COLUMN_HEADING_NUMPOINTS;
+               else if (inColNum == 2) return COLUMN_HEADING_KEEP;
+               else if (inColNum == 3) return COLUMN_HEADING_DELETE;
+               return "unknown column!";
+       }
+
+       /**
+        * Get the class of objects in the given column
+        * @see javax.swing.table.AbstractTableModel#getColumnClass(int)
+        */
+       public Class<?> getColumnClass(int inColumnIndex)
+       {
+               if (inColumnIndex == 1) {return Integer.class;}
+               if (inColumnIndex > 1) {return Boolean.class;}
+               return super.getColumnClass(inColumnIndex);
+       }
+
+       /**
+        * Get whether the given cell is editable
+        * @see javax.swing.table.AbstractTableModel#isCellEditable(int, int)
+        */
+       public boolean isCellEditable(int inRowIndex, int inColumnIndex)
+       {
+               return (inColumnIndex > 1);
+       }
+
+       /**
+        * Set the value at the given table cell
+        * @see javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object, int, int)
+        */
+       public void setValueAt(Object inValue, int inRowIndex, int inColumnIndex)
+       {
+               // can only edit the keep and delete columns
+               final boolean isKeep = (inColumnIndex == 2);
+               final boolean isDelete = (inColumnIndex == 3);
+               // ignore all events for other columns
+               if (isKeep || isDelete)
+               {
+                       try {
+                               boolean setFlag = ((Boolean) inValue).booleanValue();
+                               if (setFlag)
+                               {
+                                       _infoList.getDateInfo(inRowIndex).setDeleteFlag(isDelete);
+                                       // make sure the other cell (keep or delete) on the same row is updated too
+                                       fireTableCellUpdated(inRowIndex, 5 - inColumnIndex);
+                               }
+                       }
+                       catch (ClassCastException cce) {}
+               }
+       }
+
+       /**
+        * @return cell contents at the given row, column inded
+        */
+       public Object getValueAt(int inRowIndex, int inColIndex)
+       {
+               try {
+                       DateInfo info = _infoList.getDateInfo(inRowIndex);
+                       if (info != null)
+                       {
+                               switch (inColIndex)
+                               {
+                                       case 0: // date
+                                               if (info.isDateless()) {
+                                                       return I18nManager.getText("dialog.deletebydate.nodate");
+                                               }
+                                               return DEFAULT_DATE_FORMAT.format(info.getDate());
+                                       case 1: // number of points
+                                               return info.getPointCount();
+                                       case 2: // keep
+                                               return !info.getDeleteFlag();
+                                       case 3: // delete
+                                               return info.getDeleteFlag();
+                               }
+                       }
+               }
+               catch (IndexOutOfBoundsException obe) {} // ignore, fallthrough
+               return null;
+       }
+}
index faa059ea4b1bb2ada4ce6340b30bc6014a9ce65a..2c1d83d1337b56af1a8ab1fbad0a3cc938b3d5a2 100644 (file)
@@ -17,6 +17,8 @@ public class DistanceTableModel extends GenericTableModel
        private static final String _toColLabel = I18nManager.getText("dialog.distances.column.to");
        /** Column heading (depends on metric/imperial settings) */
        private String _distanceLabel = null;
+       /** Previous distance units */
+       private Unit _previousDistUnit = null;
 
        /**
         * @return column count
@@ -69,6 +71,9 @@ public class DistanceTableModel extends GenericTableModel
                Unit distUnit = Config.getUnitSet().getDistanceUnit();
                _distanceLabel = I18nManager.getText("fieldname.distance") + " (" +
                        I18nManager.getText(distUnit.getShortnameKey()) + ")";
+               final boolean distUnitsChanged = (distUnit != _previousDistUnit);
+               _previousDistUnit = distUnit;
+
                // Initialize array of distances
                int numRows = getRowCount();
                if (_distances == null || _distances.length != numRows) {
@@ -84,7 +89,12 @@ public class DistanceTableModel extends GenericTableModel
                                _distances[i] = Distance.convertRadiansToDistance(rads);
                        }
                }
-               // Let table know that it has to refresh data (and might as well refresh column headings too)
-               fireTableStructureChanged();
+               // Let table know that it has to refresh data, and maybe the whole table too
+               if (distUnitsChanged) {
+                       fireTableStructureChanged();
+               }
+               else {
+                       fireTableDataChanged();
+               }
        }
 }
index f66d99b3c469f7690400919faf909afba1ccbf67..fb868e8bf108619f48aefefff6c8dbabea2a688f 100644 (file)
@@ -32,6 +32,7 @@ import tim.prune.data.DataPoint;
 import tim.prune.data.Field;
 import tim.prune.data.Photo;
 import tim.prune.data.Selection;
+import tim.prune.data.SourceInfo;
 import tim.prune.data.SpeedCalculator;
 import tim.prune.data.SpeedValue;
 import tim.prune.data.TrackInfo;
@@ -49,10 +50,11 @@ public class DetailsDisplay extends GenericDisplay
        private JLabel _indexLabel = null;
        private JLabel _latLabel = null, _longLabel = null;
        private JLabel _altLabel = null;
-       private JLabel _timeLabel = null;
+       private JLabel _ptDateLabel = null, _ptTimeLabel = null;
        private JLabel _descLabel = null;
        private JLabel _speedLabel = null, _vSpeedLabel = null;
        private JLabel _nameLabel = null, _typeLabel = null;
+       private JLabel _filenameLabel = null;
 
        // Range details
        private JLabel _rangeLabel = null;
@@ -90,12 +92,14 @@ public class DetailsDisplay extends GenericDisplay
        private static final String LABEL_POINT_LATITUDE = I18nManager.getText("fieldname.latitude") + ": ";
        private static final String LABEL_POINT_LONGITUDE = I18nManager.getText("fieldname.longitude") + ": ";
        private static final String LABEL_POINT_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": ";
-       private static final String LABEL_POINT_TIMESTAMP = I18nManager.getText("fieldname.timestamp") + ": ";
+       private static final String LABEL_POINT_DATE     = I18nManager.getText("fieldname.date") + ": ";
+       private static final String LABEL_POINT_TIME     = I18nManager.getText("fieldname.timestamp") + ": ";
        private static final String LABEL_POINT_WAYPOINTNAME = I18nManager.getText("fieldname.waypointname") + ": ";
        private static final String LABEL_POINT_WAYPOINTTYPE = I18nManager.getText("fieldname.waypointtype") + ": ";
        private static final String LABEL_POINT_DESCRIPTION  = I18nManager.getText("fieldname.description") + ": ";
        private static final String LABEL_POINT_SPEED        = I18nManager.getText("fieldname.speed") + ": ";
        private static final String LABEL_POINT_VERTSPEED    = I18nManager.getText("fieldname.verticalspeed") + ": ";
+       private static final String LABEL_POINT_FILENAME     = I18nManager.getText("details.track.file") + ": ";
        private static final String LABEL_RANGE_SELECTED = I18nManager.getText("details.range.selected") + ": ";
        private static final String LABEL_RANGE_DURATION = I18nManager.getText("fieldname.duration") + ": ";
        private static final String LABEL_RANGE_DISTANCE = I18nManager.getText("fieldname.distance") + ": ";
@@ -131,9 +135,12 @@ public class DetailsDisplay extends GenericDisplay
                pointDetailsPanel.add(_longLabel);
                _altLabel = new JLabel("");
                pointDetailsPanel.add(_altLabel);
-               _timeLabel = new JLabel("");
-               _timeLabel.setMinimumSize(new Dimension(120, 10));
-               pointDetailsPanel.add(_timeLabel);
+               _ptDateLabel = new JLabel("");
+               _ptDateLabel.setMinimumSize(new Dimension(120, 10));
+               pointDetailsPanel.add(_ptDateLabel);
+               _ptTimeLabel = new JLabel("");
+               _ptTimeLabel.setMinimumSize(new Dimension(120, 10));
+               pointDetailsPanel.add(_ptTimeLabel);
                _descLabel = new JLabel("");
                pointDetailsPanel.add(_descLabel);
                _speedLabel = new JLabel("");
@@ -144,6 +151,8 @@ public class DetailsDisplay extends GenericDisplay
                pointDetailsPanel.add(_nameLabel);
                _typeLabel = new JLabel("");
                pointDetailsPanel.add(_typeLabel);
+               _filenameLabel = new JLabel("");
+               pointDetailsPanel.add(_filenameLabel);
                pointDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
 
                // range details panel
@@ -296,12 +305,14 @@ public class DetailsDisplay extends GenericDisplay
                        _latLabel.setText("");
                        _longLabel.setText("");
                        _altLabel.setText("");
-                       _timeLabel.setText("");
+                       _ptDateLabel.setText("");
+                       _ptTimeLabel.setText("");
                        _descLabel.setText("");
                        _nameLabel.setText("");
                        _typeLabel.setText("");
                        _speedLabel.setText("");
                        _vSpeedLabel.setText("");
+                       _filenameLabel.setText("");
                }
                else
                {
@@ -316,12 +327,12 @@ public class DetailsDisplay extends GenericDisplay
                                I18nManager.getText(altUnit.getShortnameKey()))
                                : "");
                        if (currentPoint.hasTimestamp()) {
-                               _timeLabel.setText(LABEL_POINT_TIMESTAMP + currentPoint.getTimestamp().getText());
-                               _timeLabel.setToolTipText(currentPoint.getTimestamp().getText());
+                               _ptDateLabel.setText(LABEL_POINT_DATE + currentPoint.getTimestamp().getDateText());
+                               _ptTimeLabel.setText(LABEL_POINT_TIME + currentPoint.getTimestamp().getTimeText());
                        }
                        else {
-                               _timeLabel.setText("");
-                               _timeLabel.setToolTipText("");
+                               _ptDateLabel.setText("");
+                               _ptTimeLabel.setText("");
                        }
                        // Maybe the point has a description?
                        String pointDesc = currentPoint.getFieldValue(Field.DESCRIPTION);
@@ -377,6 +388,25 @@ public class DetailsDisplay extends GenericDisplay
                                _typeLabel.setText(LABEL_POINT_WAYPOINTTYPE + type);
                        }
                        else _typeLabel.setText("");
+
+                       // File to which point belongs
+                       final int numFiles = _trackInfo.getFileInfo().getNumFiles();
+                       String filename = null;
+                       if (numFiles > 1)
+                       {
+                               final SourceInfo info = _trackInfo.getFileInfo().getSourceForPoint(currentPoint);
+                               if (info != null) {
+                                       filename = info.getName();
+                               }
+                       }
+                       if (filename != null) {
+                               _filenameLabel.setText(LABEL_POINT_FILENAME + filename);
+                               _filenameLabel.setToolTipText(filename);
+                       }
+                       else {
+                               _filenameLabel.setText("");
+                               _filenameLabel.setToolTipText("");
+                       }
                }
 
                // Update range details
@@ -447,7 +477,7 @@ public class DetailsDisplay extends GenericDisplay
                        String shortPath = shortenPath(fullPath);
                        _photoPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath);
                        _photoPathLabel.setToolTipText(currentPhoto.getFullPath());
-                       _photoTimestampLabel.setText(currentPhoto.hasTimestamp()?(LABEL_POINT_TIMESTAMP + currentPhoto.getTimestamp().getText()):"");
+                       _photoTimestampLabel.setText(currentPhoto.hasTimestamp()?(LABEL_POINT_TIME + currentPhoto.getTimestamp().getText()):"");
                        _photoConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": "
                                + (currentPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED ?
                                        I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes")));
@@ -483,7 +513,7 @@ public class DetailsDisplay extends GenericDisplay
                        String shortPath = shortenPath(fullPath);
                        _audioPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath);
                        _audioPathLabel.setToolTipText(fullPath == null ? "" : fullPath);
-                       _audioTimestampLabel.setText(currentAudio.hasTimestamp()?(LABEL_POINT_TIMESTAMP + currentAudio.getTimestamp().getText()):"");
+                       _audioTimestampLabel.setText(currentAudio.hasTimestamp()?(LABEL_POINT_TIME + currentAudio.getTimestamp().getText()):"");
                        int audioLength = currentAudio.getLengthInSeconds();
                        _audioLengthLabel.setText(audioLength < 0?"":LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(audioLength));
                        _audioConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": "
@@ -532,14 +562,23 @@ public class DetailsDisplay extends GenericDisplay
        {
                final int DECIMAL_PLACES = 7;
                if (inCoord == null) return "";
+               String result = inCoord;
                final int dotPos = Math.max(inCoord.lastIndexOf('.'), inCoord.lastIndexOf(','));
-               if (dotPos >= 0) {
+               if (dotPos >= 0)
+               {
                        final int chopPos = dotPos + DECIMAL_PLACES;
-                       if (chopPos < (inCoord.length()-1)) {
-                               return inCoord.substring(0, chopPos);
+                       if (chopPos < (inCoord.length()-1))
+                       {
+                               result = inCoord.substring(0, chopPos);
+                               // Maybe there's an exponential in there too which needs to be appended
+                               int expPos = inCoord.toUpperCase().indexOf("E",  chopPos);
+                               if (expPos > 0 && expPos < (inCoord.length()-1))
+                               {
+                                       result += inCoord.substring(expPos);
+                               }
                        }
                }
-               return inCoord;
+               return result;
        }
 
        /**
index 799e90f76b0c8bc2faf221ec12001fca0b0012b6..eff471417dd251d57909d4c24c241847fde14f11 100644 (file)
@@ -10,7 +10,7 @@ public abstract class IconManager
 {
 
        /** Icon for window */
-       public static final String WINDOW_ICON = "window_icon.png";
+       public static final String WINDOW_ICON = "window_icon";
 
        /** Icon for scalebar button on main map display */
        public static final String SCALEBAR_BUTTON = "scalebar.gif";
index d2be8adbe7544f4684a46ab479d20316b84abacd..741425dea2e9b2c63b321e47b15ce4b1c31a5c68 100644 (file)
@@ -21,6 +21,7 @@ import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
 import tim.prune.config.Config;
 import tim.prune.data.AudioClip;
+import tim.prune.data.DataPoint;
 import tim.prune.data.Field;
 import tim.prune.data.Photo;
 import tim.prune.data.RecentFile;
@@ -28,7 +29,7 @@ import tim.prune.data.RecentFileList;
 import tim.prune.data.Selection;
 import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;
-import tim.prune.function.RearrangeWaypointsFunction.Rearrange;
+import tim.prune.function.ChooseSingleParameter;
 import tim.prune.function.browser.UrlGenerator;
 
 /**
@@ -60,10 +61,12 @@ public class MenuManager implements DataSubscriber
        private JMenuItem _compressItem = null;
        private JMenuItem _markRectangleItem = null;
        private JMenuItem _deleteMarkedPointsItem = null;
+       private JMenuItem _deleteByDateItem = null;
        private JMenuItem _interpolateItem = null;
        private JMenuItem _averageItem = null;
        private JMenuItem _selectAllItem = null;
        private JMenuItem _selectNoneItem = null;
+       private JMenuItem _selectSegmentItem = null;
        private JMenuItem _selectStartItem = null;
        private JMenuItem _selectEndItem = null;
        private JMenuItem _findWaypointItem = null;
@@ -72,7 +75,7 @@ public class MenuManager implements DataSubscriber
        private JMenuItem _addTimeOffsetItem = null;
        private JMenuItem _addAltitudeOffsetItem = null;
        private JMenuItem _mergeSegmentsItem = null;
-       private JMenu     _rearrangeMenu = null;
+       private JMenuItem _rearrangeWaypointsItem = null;
        private JMenuItem _splitSegmentsItem = null;
        private JMenuItem _sewSegmentsItem = null;
        private JMenuItem _cutAndMoveItem = null;
@@ -309,35 +312,12 @@ public class MenuManager implements DataSubscriber
                });
                _deleteMarkedPointsItem.setEnabled(false);
                trackMenu.add(_deleteMarkedPointsItem);
+               _deleteByDateItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_BY_DATE, false);
+               trackMenu.add(_deleteByDateItem);
                trackMenu.addSeparator();
                // Rearrange waypoints
-               _rearrangeMenu = new JMenu(I18nManager.getText("menu.track.rearrange"));
-               _rearrangeMenu.setEnabled(false);
-               JMenuItem  rearrangeStartItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.start"));
-               rearrangeStartItem.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_START);
-                       }
-               });
-               rearrangeStartItem.setEnabled(true);
-               _rearrangeMenu.add(rearrangeStartItem);
-               JMenuItem rearrangeEndItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.end"));
-               rearrangeEndItem.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_END);
-                       }
-               });
-               rearrangeEndItem.setEnabled(true);
-               _rearrangeMenu.add(rearrangeEndItem);
-               JMenuItem rearrangeNearestItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.nearest"));
-               rearrangeNearestItem.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_NEAREST);
-                       }
-               });
-               rearrangeNearestItem.setEnabled(true);
-               _rearrangeMenu.add(rearrangeNearestItem);
-               trackMenu.add(_rearrangeMenu);
+               _rearrangeWaypointsItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS, false);
+               trackMenu.add(_rearrangeWaypointsItem);
                // Split track segments
                _splitSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SPLIT_SEGMENTS, false);
                trackMenu.add(_splitSegmentsItem);
@@ -369,6 +349,8 @@ public class MenuManager implements DataSubscriber
                        }
                });
                rangeMenu.add(_selectNoneItem);
+               _selectSegmentItem = makeMenuItem(FunctionLibrary.FUNCTION_SELECT_SEGMENT);
+               rangeMenu.add(_selectSegmentItem);
                rangeMenu.addSeparator();
                _selectStartItem = new JMenuItem(I18nManager.getText("menu.range.start"));
                _selectStartItem.setEnabled(false);
@@ -416,7 +398,7 @@ public class MenuManager implements DataSubscriber
                _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES, false);
                rangeMenu.add(_deleteFieldValuesItem);
                rangeMenu.addSeparator();
-               _interpolateItem = makeMenuItem(FunctionLibrary.FUNCTION_INTERPOLATE, false);
+               _interpolateItem = makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_INTERPOLATE), false);
                rangeMenu.add(_interpolateItem);
                _averageItem = new JMenuItem(I18nManager.getText("menu.range.average"));
                _averageItem.addActionListener(new ActionListener() {
@@ -657,9 +639,11 @@ public class MenuManager implements DataSubscriber
                // Set colours
                settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS));
                // Set line width used for drawing
-               settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LINE_WIDTH));
+               settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_LINE_WIDTH)));
                // Set language
                settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE));
+               // Set altitude tolerance
+               settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_ALTITUDE_TOLERANCE)));
                settingsMenu.addSeparator();
                // Save configuration
                settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SAVECONFIG));
@@ -877,7 +861,7 @@ public class MenuManager implements DataSubscriber
                _compressItem.setEnabled(hasData);
                _markRectangleItem.setEnabled(hasData);
                _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints());
-               _rearrangeMenu.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
+               _rearrangeWaypointsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
                _splitSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3);
                _sewSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3);
                _selectAllItem.setEnabled(hasData);
@@ -895,6 +879,8 @@ public class MenuManager implements DataSubscriber
                _findWaypointItem.setEnabled(hasData && _track.hasWaypoints());
                // have we got a cache?
                _downloadSrtmItem.setEnabled(hasData && Config.getConfigString(Config.KEY_DISK_CACHE) != null);
+               // have we got any timestamps?
+               _deleteByDateItem.setEnabled(hasData && _track.hasData(Field.TIMESTAMP));
 
                // is undo available?
                boolean hasUndo = !_app.getUndoStack().isEmpty();
@@ -902,7 +888,8 @@ public class MenuManager implements DataSubscriber
                _undoButton.setEnabled(hasUndo);
                _clearUndoItem.setEnabled(hasUndo);
                // is there a current point?
-               boolean hasPoint = (hasData && _selection.getCurrentPointIndex() >= 0);
+               DataPoint currPoint = _app.getTrackInfo().getCurrentPoint();
+               boolean hasPoint = (currPoint != null);
                _editPointItem.setEnabled(hasPoint);
                _editPointButton.setEnabled(hasPoint);
                _editWaypointNameItem.setEnabled(hasPoint);
@@ -913,6 +900,8 @@ public class MenuManager implements DataSubscriber
                _selectEndItem.setEnabled(hasPoint);
                _selectEndButton.setEnabled(hasPoint);
                _duplicatePointItem.setEnabled(hasPoint);
+               // is it a waypoint?
+               _selectSegmentItem.setEnabled(hasPoint && !currPoint.isWaypoint());
                // are there any photos?
                boolean anyPhotos = _app.getTrackInfo().getPhotoList().getNumPhotos() > 0;
                _saveExifItem.setEnabled(anyPhotos && _app.getTrackInfo().getPhotoList().hasMediaWithFile());
@@ -939,7 +928,7 @@ public class MenuManager implements DataSubscriber
                _selectNoAudioItem.setEnabled(hasAudio);
                _removeAudioItem.setEnabled(hasAudio);
                _connectAudioItem.setEnabled(hasAudio && hasPoint && currentAudio.getDataPoint() == null);
-               _disconnectAudioItem.setEnabled(hasAudio && _app.getTrackInfo().getCurrentAudio().getDataPoint() != null);
+               _disconnectAudioItem.setEnabled(hasAudio && currentAudio.getDataPoint() != null);
                _correlateAudiosItem.setEnabled(anyAudios && hasData);
                // is there a current range?
                boolean hasRange = (hasData && _selection.hasRangeSelected());
@@ -983,8 +972,9 @@ public class MenuManager implements DataSubscriber
                                for (int i=0; i<numRecentFiles; i++)
                                {
                                        JMenuItem item = _recentFileMenu.getItem(i);
-                                       item.setText(rfl.getFile(i)==null?"":rfl.getFile(i).getFile().getName());
-                                       item.setToolTipText(rfl.getFile(i)==null?null:rfl.getFile(i).getFile().getAbsolutePath());
+                                       RecentFile rf = rfl.getFile(i);
+                                       item.setText(rf==null?"":rf.getFile().getName());
+                                       item.setToolTipText(rf==null?null:rf.getFile().getAbsolutePath());
                                }
                        }
                        else
index 064aa119da12452de470eeb3c9c7b3a75576e964..ebb4b05283599b4c8d19f437986549a722ea5edf 100644 (file)
@@ -11,6 +11,7 @@ import javax.swing.JLabel;
 import javax.swing.JPanel;
 
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 import tim.prune.threedee.TerrainDefinition;
 
 /**
@@ -44,7 +45,7 @@ public class TerrainDefinitionPanel extends JPanel
                JLabel label = new JLabel(I18nManager.getText("dialog.3d.terraingridsize") + ": ");
                add(label);
                _gridSizeField = new WholeNumberField(4);
-               _gridSizeField.setValue(50); // default grid size
+               _gridSizeField.setValue(Config.getConfigInt(Config.KEY_TERRAIN_GRID_SIZE)); // default grid size
                _gridSizeField.setMaximumSize(new Dimension(100, 50));
                _gridSizeField.setEnabled(false);
                add(_gridSizeField);
index ba953fc67c02381bf246616c21bbacf19fe5b420..b12d299b626c960101a8aed1f917d4ead1da918e 100644 (file)
@@ -1,6 +1,8 @@
 package tim.prune.gui;
 
 import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 import javax.swing.text.AttributeSet;
 import javax.swing.text.BadLocationException;
 import javax.swing.text.PlainDocument;
@@ -59,6 +61,11 @@ public class WholeNumberField extends JTextField
        {
                super(inMaxDigits);
                setDocument(new WholeNumberDocument(inMaxDigits));
+               getDocument().addDocumentListener(new DocumentListener() {
+                       public void removeUpdate(DocumentEvent arg0) {fireActionPerformed();}
+                       public void insertUpdate(DocumentEvent arg0) {fireActionPerformed();}
+                       public void changedUpdate(DocumentEvent arg0) {fireActionPerformed();}
+               });
        }
 
        /**
diff --git a/tim/prune/gui/colour/AltitudeColourer.java b/tim/prune/gui/colour/AltitudeColourer.java
new file mode 100644 (file)
index 0000000..5ab79db
--- /dev/null
@@ -0,0 +1,70 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+import tim.prune.data.DataPoint;
+import tim.prune.data.Track;
+import tim.prune.data.TrackInfo;
+
+/**
+ * Colourer based on altitude values
+ */
+public class AltitudeColourer extends ContinuousPointColourer
+{
+       /**
+        * Constructor
+        * @param inStartColour start colour
+        * @param inEndColour end colour
+        */
+       public AltitudeColourer(Color inStartColour, Color inEndColour)
+       {
+               super(inStartColour, inEndColour);
+       }
+
+       @Override
+       public void calculateColours(TrackInfo inTrackInfo)
+       {
+               Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
+               final int numPoints = track == null ? 0 : track.getNumPoints();
+               DataPoint point = null;
+
+               // Figure out altitude range
+               double minAltitude = 0.0;
+               double maxAltitude = 0.0;
+               boolean altFound = false;
+               for (int i=0; i<numPoints; i++)
+               {
+                       point = track.getPoint(i);
+                       if (point != null && point.hasAltitude())
+                       {
+                               double altValue = point.getAltitude().getMetricValue();
+                               if (altValue < minAltitude || !altFound) {minAltitude = altValue;}
+                               if (altValue > maxAltitude || !altFound) {maxAltitude = altValue;}
+                               altFound = true;
+                       }
+               }
+
+               if ((maxAltitude - minAltitude) < 1.0)
+               {
+                       // not enough altitude range, set all to null
+                       init(0);
+               }
+               else
+               {
+                       // initialise the array to the right size
+                       init(numPoints);
+                       // loop over track points to calculate colours
+                       for (int i=0; i<numPoints; i++)
+                       {
+                               point = track.getPoint(i);
+                               if (point != null && point.hasAltitude() && !point.isWaypoint())
+                               {
+                                       double altValue = point.getAltitude().getMetricValue();
+                                       double fraction = (altValue - minAltitude) / (maxAltitude - minAltitude);
+                                       setColour(i, mixColour((float) fraction));
+                               }
+                               else setColour(i, null);
+                       }
+               }
+       }
+}
similarity index 99%
rename from tim/prune/gui/ColourChooser.java
rename to tim/prune/gui/colour/ColourChooser.java
index 93691accdb8db3f65781df1b66d4f0db459f67d0..3c40b4a86a3ee8d066250dd3435bcc2afe99f7f0 100644 (file)
@@ -1,4 +1,4 @@
-package tim.prune.gui;
+package tim.prune.gui.colour;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
similarity index 90%
rename from tim/prune/gui/ColourPatch.java
rename to tim/prune/gui/colour/ColourPatch.java
index d5b9fd29d1fd0a998d8eb1a54d3af92ea4689546..87bbfdf403776541c41372351d3b54e6d94b13af 100644 (file)
@@ -1,4 +1,4 @@
-package tim.prune.gui;
+package tim.prune.gui.colour;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -13,6 +13,7 @@ public class ColourPatch extends JPanel
 {
        /**
         * Constructor
+        * @param inColour starting colour
         */
        public ColourPatch(Color inColour)
        {
diff --git a/tim/prune/gui/colour/ColourerCaretaker.java b/tim/prune/gui/colour/ColourerCaretaker.java
new file mode 100644 (file)
index 0000000..facbb26
--- /dev/null
@@ -0,0 +1,58 @@
+package tim.prune.gui.colour;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+
+/**
+ * Caretaker of the current PointColourer, responsible for listening
+ * to data changes and updating the colourer
+ */
+public class ColourerCaretaker implements DataSubscriber
+{
+       /** App object for getting the track */
+       private App _app = null;
+       /** PointColourer object for passing details to */
+       private PointColourer _colourer = null;
+
+       /**
+        * Constructor
+        * @param inApp app object to use
+        */
+       public ColourerCaretaker(App inApp)
+       {
+               _app = inApp;
+       }
+
+       /**
+        * @param inColourer current colourer object
+        */
+       public void setColourer(PointColourer inColourer)
+       {
+               _colourer = inColourer;
+               dataUpdated(ALL);
+       }
+
+       /**
+        * @return point colourer, or null
+        */
+       public PointColourer getColourer()
+       {
+               return _colourer;
+       }
+
+       /**
+        * Data has been updated
+        */
+       public void dataUpdated(byte inUpdateType)
+       {
+               if ((inUpdateType &
+                       (DataSubscriber.DATA_ADDED_OR_REMOVED | DataSubscriber.DATA_EDITED)) > 0
+                       && _colourer != null)
+               {
+                       _colourer.calculateColours(_app.getTrackInfo());
+               }
+       }
+
+       /** Don't care about status */
+       public void actionCompleted(String inMessage) {}
+}
diff --git a/tim/prune/gui/colour/ColourerFactory.java b/tim/prune/gui/colour/ColourerFactory.java
new file mode 100644 (file)
index 0000000..767456e
--- /dev/null
@@ -0,0 +1,205 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+import tim.prune.config.ColourUtils;
+
+/**
+ * Factory for the creation of PointColourer objects
+ */
+public abstract class ColourerFactory
+{
+       /** Enumeration of colourer types */
+       public enum ColourerId
+       {
+               NONE,
+               BY_FILE,
+               BY_SEGMENT,
+               BY_ALTITUDE,
+               BY_SPEED,
+               BY_VSPEED,
+               BY_GRADIENT,
+               BY_DATE
+       }
+
+       /**
+        * Does the specified colourer need a field for maximum number of colours?
+        * @param inId id of colourer
+        * @return true if max colours required, false otherwise
+        */
+       public static boolean isMaxColoursRequired(ColourerId inId)
+       {
+               switch (inId)
+               {
+                       case NONE:        return false;
+                       case BY_FILE:     return FileColourer.isMaxColoursRequired();
+                       case BY_SEGMENT:  return SegmentColourer.isMaxColoursRequired();
+                       case BY_ALTITUDE: return AltitudeColourer.isMaxColoursRequired();
+                       case BY_SPEED:    return SpeedColourer.isMaxColoursRequired();
+                       case BY_VSPEED:   return VertSpeedColourer.isMaxColoursRequired();
+                       case BY_GRADIENT: return GradientColourer.isMaxColoursRequired();
+                       case BY_DATE:     return DateColourer.isMaxColoursRequired();
+               }
+               return false;
+       }
+
+       /**
+        * Does the specified colourer need fields for start and end colours?
+        * @param inId id of colourer
+        * @return true if colours required, false otherwise
+        */
+       public static boolean areColoursRequired(ColourerId inId)
+       {
+               // all of them except NONE need start and end colours
+               return inId != ColourerId.NONE;
+       }
+
+       /**
+        * @param inDesc Single character used as a code (in Config string)
+        * @return associated ColourerId
+        */
+       private static ColourerId getColourerId(char inDesc)
+       {
+               switch (inDesc)
+               {
+                       case 'f': return ColourerId.BY_FILE;
+                       case 's': return ColourerId.BY_SEGMENT;
+                       case 'a': return ColourerId.BY_ALTITUDE;
+                       case 'p': return ColourerId.BY_SPEED;
+                       case 'v': return ColourerId.BY_VSPEED;
+                       case 'g': return ColourerId.BY_GRADIENT;
+                       case 'd': return ColourerId.BY_DATE;
+               }
+               return ColourerId.NONE;
+       }
+
+       /**
+        * Create a new PointColourer object given the parameters
+        * @param inId id of colourer to create
+        * @param inStartColour start colour
+        * @param inEndColour end colour
+        * @param inMaxColours maximum number of colours
+        * @return PointColourer object, or null
+        */
+       public static PointColourer createColourer(ColourerId inId, Color inStartColour, Color inEndColour, String inMaxColours)
+       {
+               try
+               {
+                       switch (inId)
+                       {
+                               case NONE: return null;
+                               case BY_FILE: return new FileColourer(inStartColour, inEndColour, Integer.parseInt(inMaxColours));
+                               case BY_SEGMENT: return new SegmentColourer(inStartColour, inEndColour, Integer.parseInt(inMaxColours));
+                               case BY_ALTITUDE: return new AltitudeColourer(inStartColour, inEndColour);
+                               case BY_SPEED: return new SpeedColourer(inStartColour, inEndColour);
+                               case BY_VSPEED: return new VertSpeedColourer(inStartColour, inEndColour);
+                               case BY_GRADIENT: return new GradientColourer(inStartColour, inEndColour);
+                               case BY_DATE: return new DateColourer(inStartColour, inEndColour, Integer.parseInt(inMaxColours));
+                       }
+               }
+               catch (NumberFormatException nfe) {} // drop out to return null
+               return null;
+       }
+
+       /**
+        * Create a PointColourer object from the given description
+        * @param inString string from config
+        * @return PointColourer object, or null if string was invalid
+        */
+       public static PointColourer createColourer(String inString)
+       {
+               try
+               {
+                       String[] comps = inString.split(";");
+                       if (comps.length == 4)
+                       {
+                               ColourerId colourerType = getColourerId(comps[0].charAt(0));
+                               Color startColour = ColourUtils.colourFromHex(comps[1]);
+                               Color endColour   = ColourUtils.colourFromHex(comps[2]);
+                               String maxColours = comps[3];
+                               return createColourer(colourerType, startColour, endColour, maxColours);
+                       }
+               }
+               catch (NullPointerException npe) {}
+               catch (NumberFormatException nfe) {}
+               return null;
+       }
+
+       /**
+        * Convert the given PointColourer object into a string for the config
+        * @param inColourer PointColourer object
+        * @return string describing object (for later re-creation) or null
+        */
+       public static String PointColourerToString(PointColourer inColourer)
+       {
+               if (inColourer != null)
+               {
+                       final String startColour = ColourUtils.makeHexCode(inColourer.getStartColour());
+                       final String endColour = ColourUtils.makeHexCode(inColourer.getEndColour());
+                       final int maxColours = inColourer.getMaxColours();
+                       if (inColourer instanceof FileColourer) {
+                               return "f;" + startColour + ";" + endColour + ";" + maxColours;
+                       }
+                       else if (inColourer instanceof SegmentColourer) {
+                               return "s;" + startColour + ";" + endColour + ";" + maxColours;
+                       }
+                       else if (inColourer instanceof AltitudeColourer) {
+                               return "a;" + startColour + ";" + endColour + ";0";
+                       }
+                       else if (inColourer instanceof SpeedColourer) {
+                               return "p;" + startColour + ";" + endColour + ";0";
+                       }
+                       else if (inColourer instanceof VertSpeedColourer) {
+                               return "v;" + startColour + ";" + endColour + ";0";
+                       }
+                       else if (inColourer instanceof GradientColourer) {
+                               return "g;" + startColour + ";" + endColour + ";0";
+                       }
+                       else if (inColourer instanceof DateColourer) {
+                               return "d;" + startColour + ";" + endColour + ";" + maxColours;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Get the colourer-specific end of the description key for translation
+        * @param inId id of colourer
+        * @return end of description key for combobox text
+        */
+       public static String getDescriptionKey(ColourerId inId)
+       {
+               switch (inId)
+               {
+                       case NONE:        return "none";
+                       case BY_FILE:     return "byfile";
+                       case BY_SEGMENT:  return "bysegment";
+                       case BY_ALTITUDE: return "byaltitude";
+                       case BY_SPEED:    return "byspeed";
+                       case BY_VSPEED:   return "byvertspeed";
+                       case BY_GRADIENT: return "bygradient";
+                       case BY_DATE:     return "bydate";
+               }
+               return null;
+       }
+
+       /**
+        * Get the id of the given colourer, according to its class
+        * @param inColourer point colourer object, or null
+        * @return id, for example for selection in dropdown
+        */
+       public static ColourerId getId(PointColourer inColourer)
+       {
+               if (inColourer != null)
+               {
+                       if (inColourer instanceof FileColourer)      {return ColourerId.BY_FILE;}
+                       if (inColourer instanceof SegmentColourer)   {return ColourerId.BY_SEGMENT;}
+                       if (inColourer instanceof AltitudeColourer)  {return ColourerId.BY_ALTITUDE;}
+                       if (inColourer instanceof SpeedColourer)     {return ColourerId.BY_SPEED;}
+                       if (inColourer instanceof VertSpeedColourer) {return ColourerId.BY_VSPEED;}
+                       if (inColourer instanceof GradientColourer)  {return ColourerId.BY_GRADIENT;}
+                       if (inColourer instanceof DateColourer)      {return ColourerId.BY_DATE;}
+               }
+               return ColourerId.NONE;
+       }
+}
diff --git a/tim/prune/gui/colour/ColourerSelectorPanel.java b/tim/prune/gui/colour/ColourerSelectorPanel.java
new file mode 100644 (file)
index 0000000..85754a5
--- /dev/null
@@ -0,0 +1,230 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EtchedBorder;
+
+import tim.prune.I18nManager;
+import tim.prune.gui.GuiGridLayout;
+import tim.prune.gui.colour.ColourerFactory.ColourerId;
+
+/**
+ * Class to provide a gui panel for selecting a colourer
+ * including which colourer, the start/end colours and
+ * optionally the max number of colours
+ */
+public class ColourerSelectorPanel extends JPanel
+{
+       /** Combo box for selecting the type of colourer */
+       private JComboBox<String> _typeCombo = null;
+       /** Array of type ids as stored in combo box */
+       private ColourerId[] _typeIds = null;
+       /** Panel object holding the colour patches */
+       private JPanel _patchPanel = null;
+       /** Array of colour patches for start and end */
+       private ColourPatch[] _startEndPatches = null;
+       /** Panel holding the max colours selection */
+       JPanel _maxColoursPanel = null;
+       private JComboBox<String> _maxColoursCombo = null;
+
+       /** Array of label keys for the 2 patches */
+       private static final String[] LABEL_KEYS = {"start", "end"};
+
+
+       /**
+        * Constructor
+        * @param inColourChooser colour chooser to use (needs reference to parent dialog)
+        */
+       public ColourerSelectorPanel(ColourChooser inColourChooser)
+       {
+               _typeIds = new ColourerId[] {ColourerId.NONE, ColourerId.BY_FILE,
+                       ColourerId.BY_SEGMENT, ColourerId.BY_DATE, ColourerId.BY_ALTITUDE,
+                       ColourerId.BY_SPEED, ColourerId.BY_VSPEED, ColourerId.BY_GRADIENT};
+               makeGuiComponents(inColourChooser);
+       }
+
+
+       /**
+        * Create all the gui components and lay them out in the panel
+        * @param inColourChooser colour chooser to use
+        */
+       private void makeGuiComponents(ColourChooser inColourChooser)
+       {
+               // Etched border and vertical layout
+               setBorder(BorderFactory.createCompoundBorder(
+                       BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
+               );
+               setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+               // Label at the top
+               JLabel introLabel = new JLabel(I18nManager.getText("dialog.colourer.intro"));
+               introLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               add(introLabel);
+
+               // Combo box for selecting which colourer to use
+               JPanel typePanel = new JPanel();
+               GuiGridLayout grid = new GuiGridLayout(typePanel);
+               final String keyPrefix = "dialog.colourer.type.";
+               String[] colourerTypes = new String[_typeIds.length];
+               for (int i=0; i<colourerTypes.length; i++)
+               {
+                       colourerTypes[i] = I18nManager.getText(keyPrefix +
+                               ColourerFactory.getDescriptionKey(_typeIds[i]));
+               }
+               _typeCombo = new JComboBox<String>(colourerTypes);
+               _typeCombo.setAlignmentX(Component.LEFT_ALIGNMENT);
+               _typeCombo.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               onColourerTypeChanged();
+                       }
+               });
+               // Add to the panel
+               grid.add(new JLabel(I18nManager.getText("dialog.colourer.type")));
+               grid.add(_typeCombo);
+               typePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               add(typePanel);
+
+               // Make panel for colour patches
+               _patchPanel = new JPanel();
+               _patchPanel.setLayout(new GridLayout());
+               _startEndPatches = new ColourPatch[2];
+
+               // Blank column
+               JPanel blankColumn = new JPanel();
+               ColourPatch blankPatch = new ColourPatch(Color.BLACK);
+               blankPatch.setVisible(false);
+               blankColumn.add(blankPatch);
+               _patchPanel.add(blankColumn);
+
+               // Loop over two columns of patches
+               for (int i=0; i<2; i++)
+               {
+                       JPanel colPanel = new JPanel();
+                       colPanel.setLayout(new BoxLayout(colPanel, BoxLayout.Y_AXIS));
+                       // Top label and patch
+                       colPanel.add(new JLabel(I18nManager.getText("dialog.colourer." + LABEL_KEYS[i])));
+                       ColourPatch patch = new ColourPatch(Color.BLUE); // will be set by init() method shortly
+                       patch.addMouseListener(new PatchListener(patch, inColourChooser));
+                       colPanel.add(patch);
+                       _startEndPatches[i] = patch;
+
+                       // Add column to panel
+                       colPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+                       _patchPanel.add(colPanel);
+               }
+
+               // Blank column
+               blankColumn = new JPanel();
+               blankPatch = new ColourPatch(Color.BLACK);
+               blankPatch.setVisible(false);
+               blankColumn.add(blankPatch);
+               _patchPanel.add(blankColumn);
+
+               _patchPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               add(_patchPanel);
+
+               // Combo box for selecting max colours
+               _maxColoursPanel = new JPanel();
+               grid = new GuiGridLayout(_maxColoursPanel);
+               grid.add(new JLabel(I18nManager.getText("dialog.colourer.maxcolours")));
+               String[] colourOptions = new String[] {"2", "3", "5", "10", "15"};
+               _maxColoursCombo = new JComboBox<String>(colourOptions);
+               grid.add(_maxColoursCombo);
+               _maxColoursPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               add(_maxColoursPanel);
+       }
+
+       /**
+        * Init the colours from the colourer (if possible) or the default colour
+        * @param inColourer current colourer object, or null
+        * @param inDefaultColour current colour for points
+        */
+       public void init(PointColourer inColourer, Color inDefaultColour)
+       {
+               Color startColour = null, endColour = null;
+               if (inColourer != null)
+               {
+                       selectColourerType(ColourerFactory.getId(inColourer));
+                       startColour = inColourer.getStartColour();
+                       endColour   = inColourer.getEndColour();
+                       _maxColoursCombo.setSelectedItem("" + inColourer.getMaxColours());
+               }
+               else
+               {
+                       // no colourer, so default to 5 colours maximum
+                       _maxColoursCombo.setSelectedIndex(2);
+               }
+               if (startColour == null) {startColour = inDefaultColour;}
+               if (endColour   == null) {endColour = makeDefaultEndColour(inDefaultColour);}
+               if (startColour != null) {_startEndPatches[0].setBackground(startColour);}
+               if (endColour != null)   {_startEndPatches[1].setBackground(endColour);}
+               onColourerTypeChanged(); // make sure gui is updated
+       }
+
+       /**
+        * Make a default end colour if there isn't one already defined
+        * @param inStartColour start colour
+        * @return end colour, with the hue shifted by a third from the start
+        */
+       private static Color makeDefaultEndColour(Color inStartColour)
+       {
+               float[] defaultHSB = Color.RGBtoHSB(inStartColour.getRed(), inStartColour.getGreen(), inStartColour.getBlue(), null);
+               // add 120 degrees to the hue
+               defaultHSB[0] += (1.0f/3f);
+               return Color.getHSBColor(defaultHSB[0], defaultHSB[1], defaultHSB[2]);
+       }
+
+       /**
+        * React to the colourer type being changed
+        * by showing / hiding gui elements
+        */
+       private void onColourerTypeChanged()
+       {
+               final ColourerId id = _typeIds[_typeCombo.getSelectedIndex()];
+               // Set visibility of controls according to whether they're needed for the selected type
+               _patchPanel.setVisible(ColourerFactory.areColoursRequired(id));
+               _maxColoursPanel.setVisible(ColourerFactory.isMaxColoursRequired(id));
+       }
+
+       /**
+        * @return the selected colourer object, or null
+        */
+       public PointColourer getSelectedColourer()
+       {
+               final ColourerId id = _typeIds[_typeCombo.getSelectedIndex()];
+               return ColourerFactory.createColourer(id, _startEndPatches[0].getBackground(),
+                       _startEndPatches[1].getBackground(), _maxColoursCombo.getSelectedItem().toString());
+       }
+
+       /**
+        * Select the appropriate item in the dropdown
+        * @param inId id of colourer to choose
+        */
+       private void selectColourerType(ColourerId inId)
+       {
+               int selIndex = -1;
+               for (int i=0; i<_typeIds.length; i++)
+               {
+                       if (_typeIds[i] == inId) {
+                               selIndex = i;
+                               break;
+                       }
+               }
+               if (selIndex < 0) {
+                       System.err.println("Id " + inId + " not found in _typeIds!");
+               }
+               else {
+                       _typeCombo.setSelectedIndex(selIndex);
+               }
+       }
+}
diff --git a/tim/prune/gui/colour/ContinuousPointColourer.java b/tim/prune/gui/colour/ContinuousPointColourer.java
new file mode 100644 (file)
index 0000000..64a9583
--- /dev/null
@@ -0,0 +1,74 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+public abstract class ContinuousPointColourer extends PointColourer
+{
+       /** array of colours to use */
+       private Color[] _colours = null;
+
+       /**
+        * Constructor
+        * @param inStartColour start colour
+        * @param inEndColour end colour
+        */
+       public ContinuousPointColourer(Color inStartColour, Color inEndColour)
+       {
+               super(inStartColour, inEndColour);
+       }
+
+       /** Continuous colourers don't need a maximum count */
+       public static boolean isMaxColoursRequired() {
+               return false;
+       }
+
+       /**
+        * Initialise the array to the right size
+        * @param inNumPoints number of points in the track
+        */
+       protected void init(int inNumPoints)
+       {
+               if (_colours == null || _colours.length != inNumPoints)
+               {
+                       // Array needs to be created or resized
+                       if (inNumPoints > 0) {
+                               _colours = new Color[inNumPoints];
+                       }
+                       else {
+                               _colours = null;
+                       }
+               }
+       }
+
+       /**
+        * Set the colour at the given index
+        * @param inPointIndex point index
+        * @param inColour colour to use, or null
+        */
+       protected void setColour(int inPointIndex, Color inColour)
+       {
+               if (_colours != null && _colours.length > inPointIndex && inPointIndex >= 0)
+               {
+                       _colours[inPointIndex] = inColour;
+               }
+       }
+
+       /**
+        * Get the colour for the given point index
+        * @param inPointIndex index of point in track
+        * @return colour object
+        */
+       public Color getColour(int inPointIndex)
+       {
+               Color colour = null;
+               if (_colours != null && _colours.length > inPointIndex && inPointIndex >= 0)
+               {
+                       colour = _colours[inPointIndex];
+               }
+               if (colour == null) {
+                       // not found, use default
+                       colour = super.getDefaultColour();
+               }
+               return colour;
+       }
+}
diff --git a/tim/prune/gui/colour/DateColourer.java b/tim/prune/gui/colour/DateColourer.java
new file mode 100644 (file)
index 0000000..5a6306b
--- /dev/null
@@ -0,0 +1,111 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.TimeZone;
+
+import tim.prune.data.DataPoint;
+import tim.prune.data.Timestamp;
+import tim.prune.data.Track;
+import tim.prune.data.TrackInfo;
+
+/**
+ * Point colourer giving a different colour to each date
+ * Uses the system timezone so may give funny results for
+ * data from other timezones (eg far-away holidays)
+ */
+public class DateColourer extends DiscretePointColourer
+{
+       // Doesn't really matter what format is used here, as long as dates are different
+       private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance();
+
+       /**
+        * Constructor
+        * @param inStartColour start colour of scale
+        * @param inEndColour end colour of scale
+        * @param inWrapLength number of unique colours before wrap
+        */
+       public DateColourer(Color inStartColour, Color inEndColour, int inWrapLength)
+       {
+               super(inStartColour, inEndColour, inWrapLength);
+       }
+
+       /**
+        * Calculate the colours for each of the points in the given track
+        * @param inTrackInfo track info object
+        */
+       @Override
+       public void calculateColours(TrackInfo inTrackInfo)
+       {
+               // initialise the array to the right size
+               Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
+               final int numPoints = track == null ? 0 : track.getNumPoints();
+               init(numPoints);
+               // Make a hashmap of the already-used dates
+               HashMap<String, Integer> usedDates = new HashMap<String, Integer>(20);
+               // Also store the previous one, because they're probably consecutive
+               String prevDate = null;
+               int prevIndex = -1;
+
+               // loop over track points
+               int dayIndex = -1;
+               for (int i=0; i<numPoints; i++)
+               {
+                       DataPoint p = track.getPoint(i);
+                       if (p != null && !p.isWaypoint())
+                       {
+                               dayIndex = 0; // default index 0 will be used if no date found
+                               String date = getDate(p.getTimestamp());
+                               if (date != null)
+                               {
+                                       // Check if it's the previous one
+                                       if (prevDate != null && date.equals(prevDate)) {
+                                               dayIndex = prevIndex;
+                                       }
+                                       else
+                                       {
+                                               // Look up in the hashmap to see if it's been used before
+                                               Integer foundIndex = usedDates.get(date);
+                                               if (foundIndex == null)
+                                               {
+                                                       // not been used before, so add it
+                                                       dayIndex = usedDates.size() + 1;
+                                                       usedDates.put(date, dayIndex);
+                                               }
+                                               else
+                                               {
+                                                       // found it
+                                                       dayIndex = foundIndex;
+                                               }
+                                               // Remember what we've got for the next point
+                                               prevDate = date;
+                                               prevIndex = dayIndex;
+                                       }
+                               }
+                               // if date is null (no timestamp or invalid) then dayIndex remains 0
+                               setColour(i, dayIndex);
+                       }
+               }
+               // generate the colours needed
+               generateDiscreteColours(usedDates.size() + 1);
+       }
+
+
+       /**
+        * Find which date (in the system timezone) the given timestamp falls on
+        * @param inTimestamp timestamp
+        * @return String containing description of date, or null
+        */
+       private static String getDate(Timestamp inTimestamp)
+       {
+               if (inTimestamp == null || !inTimestamp.isValid()) {
+                       return null;
+               }
+               Calendar cal = inTimestamp.getCalendar();
+               // use system time zone
+               cal.setTimeZone(TimeZone.getDefault());
+               return DEFAULT_DATE_FORMAT.format(cal.getTime());
+       }
+}
diff --git a/tim/prune/gui/colour/DiscretePointColourer.java b/tim/prune/gui/colour/DiscretePointColourer.java
new file mode 100644 (file)
index 0000000..9549a93
--- /dev/null
@@ -0,0 +1,124 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+/**
+ * Abstract class to do the discrete colouring of points,
+ * using start and end colours and a wrapping index
+ */
+public abstract class DiscretePointColourer extends PointColourer
+{
+       /** array of discrete colours to use */
+       private Color[] _discreteColours = null;
+       /** array of colour indexes */
+       private int[] _colourIndexes = null;
+
+
+       /**
+        * Constructor
+        * @param inStartColour start colour of scale
+        * @param inEndColour end colour of scale
+        * @param inMaxColours number of unique colours before wrap
+        */
+       public DiscretePointColourer(Color inStartColour, Color inEndColour, int inMaxColours)
+       {
+               super(inStartColour, inEndColour, inMaxColours);
+       }
+
+       /** max number of colours is required here */
+       public static boolean isMaxColoursRequired() {
+               return true;
+       }
+
+       /**
+        * Initialise the array to the right size
+        * @param inNumPoints number of points in the track
+        */
+       protected void init(int inNumPoints)
+       {
+               if (_colourIndexes == null || _colourIndexes.length != inNumPoints)
+               {
+                       // Array needs to be created or resized
+                       if (inNumPoints > 0) {
+                               _colourIndexes = new int[inNumPoints];
+                       }
+                       else {
+                               _colourIndexes = null;
+                       }
+               }
+       }
+
+       /**
+        * Set the colour at the given index
+        * @param inPointIndex point index
+        * @param inColourIndex index of colour to use
+        */
+       protected void setColour(int inPointIndex, int inColourIndex)
+       {
+               if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0)
+               {
+                       _colourIndexes[inPointIndex] = inColourIndex;
+               }
+       }
+
+       /**
+        * Get the colour for the given point index
+        * @param inPointIndex index of point in track
+        * @return colour object
+        */
+       public Color getColour(int inPointIndex)
+       {
+               if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0 && getMaxColours() > 0)
+               {
+                       int colourIndex = _colourIndexes[inPointIndex] % getMaxColours();
+                       if (colourIndex >= 0 && _discreteColours != null && colourIndex < _discreteColours.length) {
+                               return _discreteColours[colourIndex];
+                       }
+               }
+               // not found, use default
+               return super.getDefaultColour();
+       }
+
+       /**
+        * Generate the set of discrete colours to use
+        * @param inNumCategories number of different categories found in the data
+        */
+       protected void generateDiscreteColours(int inNumCategories)
+       {
+               int maxColours = getMaxColours();
+               if (maxColours <= 1) {maxColours = 2;}
+               if (inNumCategories < 1) {inNumCategories = 1;}
+               else if (inNumCategories > maxColours) {inNumCategories = maxColours;}
+
+               // Use this number of categories to generate the colours
+               _discreteColours = new Color[inNumCategories];
+               for (int i=0; i<inNumCategories; i++) {
+                       _discreteColours[i] = mixColour(i, inNumCategories);
+               }
+       }
+
+       /**
+        * Mix the given colours together by interpolating H,S,B values
+        * @param inIndex index from 0 to inWrap-1
+        * @param inWrap wrap length
+        * @return mixed colour
+        */
+       private Color mixColour(int inIndex, int inWrap)
+       {
+               float fraction = inWrap < 2 ? 0.0f : (float) inIndex / (float) (inWrap - 1);
+               return mixColour(fraction);
+       }
+
+       /**
+        * @param inIndex specified colour index
+        * @return precalculated colour at the given index
+        */
+       protected Color getDiscreteColour(int inIndex)
+       {
+               if (_discreteColours == null || inIndex < 0 || getMaxColours() <= 1) {
+                       return getDefaultColour();
+               }
+               return _discreteColours[inIndex % getMaxColours()];
+       }
+
+}
diff --git a/tim/prune/gui/colour/FileColourer.java b/tim/prune/gui/colour/FileColourer.java
new file mode 100644 (file)
index 0000000..2a0e8cb
--- /dev/null
@@ -0,0 +1,70 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+import java.util.ArrayList;
+
+import tim.prune.data.DataPoint;
+import tim.prune.data.FileInfo;
+import tim.prune.data.SourceInfo;
+import tim.prune.data.TrackInfo;
+
+/**
+ * Colours points according to which file (or source) they came from
+ */
+public class FileColourer extends DiscretePointColourer
+{
+       /**
+        * Constructor
+        * @param inStartColour start colour of scale
+        * @param inEndColour end colour of scale
+        * @param inWrapLength number of unique colours before wrap
+        */
+       public FileColourer(Color inStartColour, Color inEndColour, int inWrapLength)
+       {
+               super(inStartColour, inEndColour, inWrapLength);
+       }
+
+       /**
+        * Calculate the colours for each of the points in the given track
+        * @param inTrack track object
+        */
+       @Override
+       public void calculateColours(TrackInfo inTrackInfo)
+       {
+               // initialise the array to the right size
+               final int numPoints = inTrackInfo == null ? 0 : inTrackInfo.getTrack().getNumPoints();
+               init(numPoints);
+
+               // loop over track points
+               FileInfo fInfo = inTrackInfo.getFileInfo();
+               ArrayList<SourceInfo> sourceList = new ArrayList<SourceInfo>();
+               for (int i=0; i<numPoints; i++)
+               {
+                       DataPoint p = inTrackInfo.getTrack().getPoint(i);
+                       if (p != null && !p.isWaypoint())
+                       {
+                               SourceInfo sInfo = fInfo.getSourceForPoint(p);
+                               // Is this info object already in the list?
+                               int foundIndex = -1;
+                               int sIndex = 0;
+                               for (SourceInfo si : sourceList) {
+                                       if (si == sInfo) {
+                                               foundIndex = sIndex;
+                                               break;
+                                       }
+                                       sIndex++;
+                               }
+                               // Add source info to list
+                               if (foundIndex < 0)
+                               {
+                                       sourceList.add(sInfo);
+                                       foundIndex = sourceList.size()-1;
+                               }
+                               // use this foundIndex to find the colour
+                               setColour(i, foundIndex);
+                       }
+               }
+               // generate the colours needed
+               generateDiscreteColours(sourceList.size());
+       }
+}
diff --git a/tim/prune/gui/colour/GradientColourer.java b/tim/prune/gui/colour/GradientColourer.java
new file mode 100644 (file)
index 0000000..9efb879
--- /dev/null
@@ -0,0 +1,32 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+import tim.prune.data.Track;
+import tim.prune.data.TrackInfo;
+import tim.prune.gui.profile.GradientData;
+
+/**
+ * Colourer based on gradient or glide slope values
+ */
+public class GradientColourer extends ProfileDataColourer
+{
+       /**
+        * Constructor
+        * @param inStartColour start colour
+        * @param inEndColour end colour
+        */
+       public GradientColourer(Color inStartColour, Color inEndColour)
+       {
+               super(inStartColour, inEndColour);
+       }
+
+       @Override
+       public void calculateColours(TrackInfo inTrackInfo)
+       {
+               Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
+               // Calculate gradient value for each point
+               GradientData data = new GradientData(track);
+               calculateColours(track, data);
+       }
+}
diff --git a/tim/prune/gui/colour/PatchListener.java b/tim/prune/gui/colour/PatchListener.java
new file mode 100644 (file)
index 0000000..732e543
--- /dev/null
@@ -0,0 +1,36 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+
+/**
+ * Listener class to react to patch clicks
+ */
+public class PatchListener extends MouseAdapter
+{
+       /** Associated patch */
+       private ColourPatch _patch = null;
+       /** Colour chooser object, shared between listeners */
+       private ColourChooser _colourChooser = null;
+
+       /**
+        * Constructor
+        * @param inPatch patch object to listen to
+        * @param inChooser colour chooser to use for selection
+        */
+       public PatchListener(ColourPatch inPatch, ColourChooser inChooser)
+       {
+               _patch = inPatch;
+               _colourChooser = inChooser;
+       }
+
+       /** React to mouse clicks */
+       public void mouseClicked(MouseEvent e)
+       {
+               _colourChooser.showDialog(_patch.getBackground());
+               Color colour = _colourChooser.getChosenColour();
+               if (colour != null) _patch.setColour(colour);
+       }
+}
diff --git a/tim/prune/gui/colour/PointColourer.java b/tim/prune/gui/colour/PointColourer.java
new file mode 100644 (file)
index 0000000..27e658c
--- /dev/null
@@ -0,0 +1,119 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+import tim.prune.data.TrackInfo;
+
+/**
+ * Abstract class to do the colouring of points,
+ * that is holding a colour for each track point
+ * in the current track
+ */
+public abstract class PointColourer
+{
+       /** default colour */
+       private Color _defaultColour = Color.BLUE;
+       /** start and end colours */
+       private Color _startColour = null, _endColour = null;
+       /** max number of unique colours before wrapping */
+       private int _maxColours = 1;
+
+
+       /**
+        * Constructor
+        * @param inStartColour start colour
+        * @param inEndColour end colour
+        * @param inMaxColours max number of colours
+        */
+       public PointColourer(Color inStartColour, Color inEndColour, int inMaxColours)
+       {
+               _startColour = inStartColour;
+               _endColour   = inEndColour;
+               _maxColours  = inMaxColours;
+       }
+
+       /**
+        * Constructor
+        * @param inStartColour start colour
+        * @param inEndColour end colour
+        */
+       public PointColourer(Color inStartColour, Color inEndColour)
+       {
+               this(inStartColour, inEndColour, -1);
+       }
+
+       /**
+        * Calculate the colours for each of the points in the given track
+        * @param inTrackInfo track info object
+        */
+       public abstract void calculateColours(TrackInfo inTrackInfo);
+
+       /**
+        * Get the colour for the given point index
+        * @param inPointIndex index of point in track
+        * @return colour object
+        */
+       public Color getColour(int inPointIndex)
+       {
+               return _defaultColour;
+       }
+
+       /**
+        * @param inColor default colour to use
+        */
+       protected void setDefaultColour(Color inColour)
+       {
+               if (inColour != null) {
+                       _defaultColour = inColour;
+               }
+       }
+
+       /**
+        * @return default colour
+        */
+       protected Color getDefaultColour() {
+               return _defaultColour;
+       }
+
+       /**
+        * @return start colour
+        */
+       protected Color getStartColour() {
+               return _startColour;
+       }
+
+       /**
+        * @return end colour
+        */
+       protected Color getEndColour() {
+               return _endColour;
+       }
+
+       /**
+        * @return maximum number of colours, or -1
+        */
+       protected int getMaxColours() {
+               return _maxColours;
+       }
+
+       /**
+        * Mix the given colours together using HSB values instead of interpolating RGB
+        * @param inFraction between 0.0 (start) and 1.0 (end)
+        * @return mixed colour
+        */
+       protected Color mixColour(float inFraction)
+       {
+               if (_startColour == null && _endColour == null) return getDefaultColour();
+               if (_startColour == null) return _endColour;
+               if (_endColour == null || inFraction < 0.0 || inFraction > 1.0) return _startColour;
+
+               // Convert both colours to hsb, and interpolate
+               float[] startHSB = Color.RGBtoHSB(_startColour.getRed(), _startColour.getGreen(), _startColour.getBlue(), null);
+               float[] endHSB = Color.RGBtoHSB(_endColour.getRed(), _endColour.getGreen(), _endColour.getBlue(), null);
+               // Note that if end hue is less than start hue, hue will go backwards rather than forwards with wrap around 0
+
+               return Color.getHSBColor(startHSB[0] + (endHSB[0]-startHSB[0]) * inFraction,
+                       startHSB[1] + (endHSB[1]-startHSB[1]) * inFraction,
+                       startHSB[2] + (endHSB[2]-startHSB[2]) * inFraction);
+       }
+}
diff --git a/tim/prune/gui/colour/ProfileDataColourer.java b/tim/prune/gui/colour/ProfileDataColourer.java
new file mode 100644 (file)
index 0000000..357f7f9
--- /dev/null
@@ -0,0 +1,57 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+import tim.prune.config.Config;
+import tim.prune.data.Track;
+import tim.prune.gui.profile.ProfileData;
+
+/**
+ * Colourer based on speed values
+ */
+public abstract class ProfileDataColourer extends ContinuousPointColourer
+{
+       /**
+        * Constructor
+        * @param inStartColour start colour
+        * @param inEndColour end colour
+        */
+       public ProfileDataColourer(Color inStartColour, Color inEndColour)
+       {
+               super(inStartColour, inEndColour);
+       }
+
+       /**
+        * Calculate the colours according to the track and the profile data
+        */
+       public void calculateColours(Track inTrack, ProfileData inData)
+       {
+               final int numPoints = inTrack == null ? 0 : inTrack.getNumPoints();
+
+               // Calculate values for each point
+               inData.init(Config.getUnitSet());
+               // Figure out speed range
+               double minValue = inData.getMinValue();
+               double maxValue = inData.getMaxValue();
+               if (!inData.hasData() || (maxValue - minValue) < 0.1)
+               {
+                       // not enough value range, set all to null
+                       init(0);
+               }
+               else
+               {
+                       // initialise the array to the right size
+                       init(numPoints);
+                       // loop over track points to calculate colours
+                       for (int i=0; i<numPoints; i++)
+                       {
+                               if (inData.hasData(i))
+                               {
+                                       double fraction = (inData.getData(i) - minValue) / (maxValue - minValue);
+                                       setColour(i, mixColour((float) fraction));
+                               }
+                               else setColour(i, null);
+                       }
+               }
+       }
+}
diff --git a/tim/prune/gui/colour/SegmentColourer.java b/tim/prune/gui/colour/SegmentColourer.java
new file mode 100644 (file)
index 0000000..9426222
--- /dev/null
@@ -0,0 +1,52 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+import tim.prune.data.DataPoint;
+import tim.prune.data.Track;
+import tim.prune.data.TrackInfo;
+
+/**
+ * Point colourer using the segment indices
+ */
+public class SegmentColourer extends DiscretePointColourer
+{
+       /**
+        * Constructor
+        * @param inStartColour start colour of scale
+        * @param inEndColour end colour of scale
+        * @param inWrapLength number of unique colours before wrap
+        */
+       public SegmentColourer(Color inStartColour, Color inEndColour, int inWrapLength)
+       {
+               super(inStartColour, inEndColour, inWrapLength);
+       }
+
+       /**
+        * Calculate the colours for each of the points in the given track
+        * @param inTrackInfo track info object
+        */
+       @Override
+       public void calculateColours(TrackInfo inTrackInfo)
+       {
+               // initialise the array to the right size
+               Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
+               final int numPoints = track == null ? 0 : track.getNumPoints();
+               init(numPoints);
+               // loop over track points
+               int c = -1; // first track point will increment this to 0
+               for (int i=0; i<numPoints; i++)
+               {
+                       DataPoint p = track.getPoint(i);
+                       if (p != null && !p.isWaypoint())
+                       {
+                               if (p.getSegmentStart()) {
+                                       c++;
+                               }
+                               setColour(i, c);
+                       }
+               }
+               // generate the colours needed
+               generateDiscreteColours(c+1);
+       }
+}
diff --git a/tim/prune/gui/colour/SpeedColourer.java b/tim/prune/gui/colour/SpeedColourer.java
new file mode 100644 (file)
index 0000000..5127a57
--- /dev/null
@@ -0,0 +1,32 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+import tim.prune.data.Track;
+import tim.prune.data.TrackInfo;
+import tim.prune.gui.profile.SpeedData;
+
+/**
+ * Colourer based on speed values
+ */
+public class SpeedColourer extends ProfileDataColourer
+{
+       /**
+        * Constructor
+        * @param inStartColour start colour
+        * @param inEndColour end colour
+        */
+       public SpeedColourer(Color inStartColour, Color inEndColour)
+       {
+               super(inStartColour, inEndColour);
+       }
+
+       @Override
+       public void calculateColours(TrackInfo inTrackInfo)
+       {
+               Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
+               // Calculate speed value for each point
+               SpeedData data = new SpeedData(track);
+               calculateColours(track, data);
+       }
+}
diff --git a/tim/prune/gui/colour/VertSpeedColourer.java b/tim/prune/gui/colour/VertSpeedColourer.java
new file mode 100644 (file)
index 0000000..a6f2923
--- /dev/null
@@ -0,0 +1,32 @@
+package tim.prune.gui.colour;
+
+import java.awt.Color;
+
+import tim.prune.data.Track;
+import tim.prune.data.TrackInfo;
+import tim.prune.gui.profile.VerticalSpeedData;
+
+/**
+ * Colourer based on vertical speed values
+ */
+public class VertSpeedColourer extends ProfileDataColourer
+{
+       /**
+        * Constructor
+        * @param inStartColour start colour
+        * @param inEndColour end colour
+        */
+       public VertSpeedColourer(Color inStartColour, Color inEndColour)
+       {
+               super(inStartColour, inEndColour);
+       }
+
+       @Override
+       public void calculateColours(TrackInfo inTrackInfo)
+       {
+               Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
+               // Calculate speed value for each point
+               VerticalSpeedData data = new VerticalSpeedData(track);
+               calculateColours(track, data);
+       }
+}
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
diff --git a/tim/prune/gui/images/window_icon.png b/tim/prune/gui/images/window_icon.png
deleted file mode 100644 (file)
index 76bbe94..0000000
Binary files a/tim/prune/gui/images/window_icon.png and /dev/null differ
diff --git a/tim/prune/gui/images/window_icon_128.png b/tim/prune/gui/images/window_icon_128.png
new file mode 100644 (file)
index 0000000..9e7afc4
Binary files /dev/null and b/tim/prune/gui/images/window_icon_128.png differ
diff --git a/tim/prune/gui/images/window_icon_16.png b/tim/prune/gui/images/window_icon_16.png
new file mode 100644 (file)
index 0000000..919f777
Binary files /dev/null and b/tim/prune/gui/images/window_icon_16.png differ
diff --git a/tim/prune/gui/images/window_icon_20.png b/tim/prune/gui/images/window_icon_20.png
new file mode 100644 (file)
index 0000000..4d020f1
Binary files /dev/null and b/tim/prune/gui/images/window_icon_20.png differ
diff --git a/tim/prune/gui/images/window_icon_32.png b/tim/prune/gui/images/window_icon_32.png
new file mode 100644 (file)
index 0000000..044336d
Binary files /dev/null and b/tim/prune/gui/images/window_icon_32.png differ
diff --git a/tim/prune/gui/images/window_icon_64.png b/tim/prune/gui/images/window_icon_64.png
new file mode 100644 (file)
index 0000000..f3b94a9
Binary files /dev/null and b/tim/prune/gui/images/window_icon_64.png differ
index a62b10855ce14d25299f41e891f1b432999c4aff..7a6bce81a5dc285050a8a1e97a5ed3db4b84db37 100644 (file)
@@ -1,5 +1,6 @@
 package tim.prune.gui.map;
 
+import java.awt.AlphaComposite;
 import java.awt.BasicStroke;
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -58,6 +59,7 @@ import tim.prune.function.compress.MarkPointsInRectangleFunction;
 import tim.prune.function.edit.FieldEdit;
 import tim.prune.function.edit.FieldEditList;
 import tim.prune.gui.IconManager;
+import tim.prune.gui.colour.PointColourer;
 import tim.prune.tips.TipManager;
 
 /**
@@ -84,6 +86,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        private MapTileManager _tileManager = new MapTileManager(this);
        /** Image to display */
        private BufferedImage _mapImage = null;
+       /** Second image for drawing track (only needed for alpha blending) */
+       private BufferedImage _trackImage = null;
        /** Slider for transparency */
        private JSlider _transparencySlider = null;
        /** Checkbox for scale bar */
@@ -484,7 +488,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                else if (px > (getWidth()-PAN_DISTANCE)) {
                        panX = AUTOPAN_DISTANCE + px - getWidth();
                }
-               if (py < PAN_DISTANCE) {
+               if (py < (2*PAN_DISTANCE)) {
                        panY = py - AUTOPAN_DISTANCE;
                }
                if (py > (getHeight()-PAN_DISTANCE)) {
@@ -580,24 +584,58 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        }
                }
 
-               // Paint the track points on top
-               int pointsPainted = 1;
-               try
+               // Work out track opacity according to slider
+               final float[] opacities = {1.0f, 0.75f, 0.5f, 0.3f, 0.15f, 0.0f};
+               float trackOpacity = 1.0f;
+               if (_transparencySlider.getValue() < 0) {
+                       trackOpacity = opacities[-1 - _transparencySlider.getValue()];
+               }
+
+               if (trackOpacity > 0.0f)
                {
-                       pointsPainted = paintPoints(g);
+                       // Paint the track points on top
+                       int pointsPainted = 1;
+                       try
+                       {
+                               if (trackOpacity > 0.9f)
+                               {
+                                       // Track is fully opaque, just draw it directly
+                                       pointsPainted = paintPoints(g);
+                                       _trackImage = null;
+                               }
+                               else
+                               {
+                                       // Track is partly transparent, so use a separate BufferedImage
+                                       if (_trackImage == null || _trackImage.getWidth() != getWidth() || _trackImage.getHeight() != getHeight())
+                                       {
+                                               _trackImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
+                                       }
+                                       // Clear to transparent
+                                       Graphics2D gTrack = _trackImage.createGraphics();
+                                       gTrack.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
+                                       gTrack.fillRect(0, 0, getWidth(), getHeight());
+                                       gTrack.setPaintMode();
+                                       // Draw the track onto this separate image
+                                       pointsPainted = paintPoints(gTrack);
+                                       ((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, trackOpacity));
+                                       g.drawImage(_trackImage, 0, 0, null);
+                               }
+                       }
+                       catch (NullPointerException npe) {} // ignore, probably due to data being changed during drawing
+                       catch (ArrayIndexOutOfBoundsException obe) {} // also ignore
+
+                       // Zoom to fit if no points found
+                       if (pointsPainted <= 0 && _checkBounds)
+                       {
+                               zoomToFit();
+                               _recalculate = true;
+                               repaint();
+                       }
                }
-               catch (NullPointerException npe) {} // ignore, probably due to data being changed during drawing
-               catch (ArrayIndexOutOfBoundsException obe) {} // also ignore
 
                // free g
                g.dispose();
 
-               // Zoom to fit if no points found
-               if (pointsPainted <= 0 && _checkBounds) {
-                       zoomToFit();
-                       _recalculate = true;
-                       repaint();
-               }
                _checkBounds = false;
                // enable / disable transparency slider
                _transparencySlider.setEnabled(showMap);
@@ -613,15 +651,12 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        {
                // Set up colours
                final ColourScheme cs = Config.getColourScheme();
-               final int[] opacities = {255, 190, 130, 80, 40, 0};
-               int opacity = 255;
-               if (_transparencySlider.getValue() < 0)
-                       opacity = opacities[-1 - _transparencySlider.getValue()];
-               final Color pointColour  = makeTransparentColour(cs.getColour(ColourScheme.IDX_POINT), opacity);
-               final Color rangeColour  = makeTransparentColour(cs.getColour(ColourScheme.IDX_SELECTION), opacity);
-               final Color currentColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_PRIMARY), opacity);
-               final Color secondColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SECONDARY), opacity);
-               final Color textColour   = makeTransparentColour(cs.getColour(ColourScheme.IDX_TEXT), opacity);
+               final Color pointColour  = cs.getColour(ColourScheme.IDX_POINT);
+               final Color rangeColour  = cs.getColour(ColourScheme.IDX_SELECTION);
+               final Color currentColour = cs.getColour(ColourScheme.IDX_PRIMARY);
+               final Color secondColour = cs.getColour(ColourScheme.IDX_SECONDARY);
+               final Color textColour   = cs.getColour(ColourScheme.IDX_TEXT);
+               final PointColourer pointColourer = _app.getPointColourer();
 
                final int winWidth  = getWidth();
                final int winHeight = getHeight();
@@ -659,26 +694,34 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        currPointVisible = px >= 0 && px < winWidth && py >= 0 && py < winHeight;
                        isWaypoint = _track.getPoint(i).isWaypoint();
                        anyWaypoints = anyWaypoints || isWaypoint;
-                       if (currPointVisible)
+                       if (!isWaypoint)
                        {
-                               if (!isWaypoint)
+                               if (currPointVisible || prevPointVisible)
                                {
-                                       // Draw rectangle for track point
+                                       // For track points, work out which colour to use
                                        if (_track.getPoint(i).getDeleteFlag()) {
                                                inG.setColor(currentColour);
                                        }
-                                       else {
+                                       else if (pointColourer != null)
+                                       {  // use the point colourer if there is one
+                                               Color trackColour = pointColourer.getColour(i);
+                                               inG.setColor(trackColour);
+                                       }
+                                       else
+                                       {
                                                inG.setColor(pointColour);
                                        }
-                                       inG.drawRect(px-2, py-2, 3, 3);
-                                       pointsPainted++;
+
+                                       // Draw rectangle for track point if it's visible
+                                       if (currPointVisible)
+                                       {
+                                               inG.drawRect(px-2, py-2, 3, 3);
+                                               pointsPainted++;
+                                       }
                                }
-                       }
-                       if (!isWaypoint)
-                       {
+
                                // Connect track points if either of them are visible
-                               if (connectPoints && (currPointVisible || prevPointVisible)
-                                && !(prevX == -1 && prevY == -1)
+                               if (connectPoints && !(prevX == -1 && prevY == -1)
                                 && !_track.getPoint(i).getSegmentStart())
                                {
                                        inG.drawLine(prevX, prevY, px, py);
@@ -859,18 +902,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                }
        }
 
-       /**
-        * Make a semi-transparent colour for drawing with
-        * @param inColour base colour (fully opaque)
-        * @param inOpacity opacity where 0=invisible and 255=full
-        * @return new colour object
-        */
-       private static Color makeTransparentColour(Color inColour, int inOpacity)
-       {
-               if (inOpacity > 240) return inColour;
-               return new Color(inColour.getRed(), inColour.getGreen(), inColour.getBlue(), inOpacity);
-       }
-
        /**
         * Inform that tiles have been updated and the map can be repainted
         * @param inIsOk true if data loaded ok, false for error
@@ -1050,8 +1081,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                        else if (_drawMode == MODE_DRAW_POINTS_CONT)
                                        {
                                                DataPoint point = createPointFromClick(inE.getX(), inE.getY());
-                                               _app.createPoint(point);
-                                               point.setSegmentStart(false);
+                                               _app.createPoint(point, false); // not a new segment
                                        }
                                }
                                else if (inE.getClickCount() == 2)
index d838ef34d99d47a7f7fe524b8a63e87cf98609b6..88ab85aa5e11eb4f45b22c4a0e4cc20ce7708a03 100644 (file)
@@ -87,6 +87,8 @@ public abstract class MapSource
                // check prefix
                try {
                        new URL(urlstr.replace('[', 'w').replace(']', 'w'));
+                       // There's a warning that this URL object isn't used, but it's enough to know the parse
+                       // was successful without an exception being thrown.
                }
                catch (MalformedURLException e)
                {
diff --git a/tim/prune/gui/profile/GradientData.java b/tim/prune/gui/profile/GradientData.java
new file mode 100644 (file)
index 0000000..78bf497
--- /dev/null
@@ -0,0 +1,66 @@
+package tim.prune.gui.profile;
+
+import tim.prune.I18nManager;
+import tim.prune.data.GradientCalculator;
+import tim.prune.data.SpeedValue;
+import tim.prune.data.Track;
+import tim.prune.data.UnitSet;
+
+/**
+ * Class to provide a source of gradient data for the profile chart
+ * or for the point colourer
+ */
+public class GradientData extends ProfileData
+{
+       /**
+        * Constructor
+        * @param inTrack track object
+        */
+       public GradientData(Track inTrack) {
+               super(inTrack);
+       }
+
+       /**
+        * Get the data and populate the instance arrays
+        */
+       public void init(UnitSet inUnitSet)
+       {
+               setUnitSet(inUnitSet);
+               initArrays();
+               _hasData = false;
+               _minValue = _maxValue = 0.0;
+               SpeedValue speed = new SpeedValue();
+               if (_track != null)
+               {
+                       for (int i=0; i<_track.getNumPoints(); i++)
+                       {
+                               // Get the gradient either from the speed values or from the distances and altitudes
+                               GradientCalculator.calculateGradient(_track, i, speed);
+                               if (speed.isValid())
+                               {
+                                       double speedValue = speed.getValue();
+                                       _pointValues[i] = speedValue;
+                                       if (speedValue < _minValue || !_hasData) {_minValue = speedValue;}
+                                       if (speedValue > _maxValue || !_hasData) {_maxValue = speedValue;}
+                                       _hasData = true;
+                               }
+                               _pointHasData[i] = speed.isValid();
+                       }
+               }
+       }
+
+       /**
+        * @return text description
+        */
+       public String getLabel()
+       {
+               return I18nManager.getText("fieldname.gradient");
+       }
+
+       /**
+        * @return key for message when no altitudes present
+        */
+       public String getNoDataKey() {
+               return "display.noaltitudes";
+       }
+}
index cd4d49d61c1e1d61793af57ed3f3f63e21fe50d8..4b0f350c58309a6a4b480de766942cc046b8ac78 100644 (file)
@@ -39,8 +39,8 @@ public class SpeedData extends ProfileData
                                {
                                        double speedValue = speed.getValue();
                                        _pointValues[i] = speedValue;
-                                       if (speedValue < _minValue || _minValue == 0.0) {_minValue = speedValue;}
-                                       if (speedValue > _maxValue) {_maxValue = speedValue;}
+                                       if (speedValue < _minValue || !_hasData) {_minValue = speedValue;}
+                                       if (speedValue > _maxValue || !_hasData) {_maxValue = speedValue;}
                                        _hasData = true;
                                }
                                _pointHasData[i] = speed.isValid();
index 510de8803b328ce8cb7669f7928afe74112a0ee7..6fe9bf7afc729ddffd33797b9099d71adb309e57 100644 (file)
@@ -40,8 +40,8 @@ public class VerticalSpeedData extends ProfileData
                                        // Store the value and maintain max and min values
                                        double speedValue = speed.getValue();
                                        _pointValues[i] = speedValue;
-                                       if (speedValue < _minValue || _minValue == 0.0) {_minValue = speedValue;}
-                                       if (speedValue > _maxValue) {_maxValue = speedValue;}
+                                       if (speedValue < _minValue || !_hasData) {_minValue = speedValue;}
+                                       if (speedValue > _maxValue || !_hasData) {_maxValue = speedValue;}
                                        _hasData = true;
                                }
                                _pointHasData[i] = speed.isValid();
index f208cf0ad41d8d54ca81f8e815ba845a52c23bb2..50d9c807170f6853014997044b609ce0c0f3a2f9 100644 (file)
@@ -10,10 +10,7 @@ menu.track=Spoor
 menu.track.undo=Herroep
 menu.track.clearundo=Herroep Lys Skoonmaak
 menu.track.deletemarked=Gemerkde Punt Uitvee
-menu.track.rearrange=Herrangskik bakens
-menu.track.rearrange.start=Bakens na begin van l\u00eaer
-menu.track.rearrange.end=Bakens na einde van l\u00eaer
-menu.track.rearrange.nearest=Beweeg elk na naaste spoor punt
+function.rearrangewaypoints=Herrangskik bakens
 menu.range=Reeks
 menu.range.all=Selekteer Alles
 menu.range.none=Selekteer Niks
@@ -275,11 +272,13 @@ dialog.correlate.correltimes=Vir korrelasie gebruik:
 dialog.correlate.timestamp.beginning=Begin
 dialog.correlate.timestamp.middle=Middel
 dialog.correlate.timestamp.end=Einde
-dialog.rearrangephotos.tostart=Beweeg na begin
-dialog.rearrangephotos.toend=Beweeg na einde
-dialog.rearrangephotos.nosort=Nie sorteer
-dialog.rearrangephotos.sortbyfilename=Sorteer volgens leernaam
-dialog.rearrangephotos.sortbytime=Sorteer volgens tyd
+dialog.rearrange.tostart=Beweeg na begin
+dialog.rearrange.toend=Beweeg na einde
+dialog.rearrange.tonearest=Beweeg elk na naaste spoor punt
+dialog.rearrange.nosort=Nie sorteer
+dialog.rearrange.sortbyfilename=Sorteer volgens leernaam
+dialog.rearrange.sortbyname=Sorteer volgens naam
+dialog.rearrange.sortbytime=Sorteer volgens tyd
 dialog.compress.closepoints.title=Naby punt verwydering
 dialog.compress.closepoints.paramdesc=Span faktor
 dialog.compress.wackypoints.title=Gekkige punt verwydering
index cab34c731463e498c3b98aa9ff606c85ad7132a5..2ac7ca669bd2516d7c579081c718d0c98879535a 100644 (file)
@@ -13,10 +13,7 @@ menu.track.undo=Undo
 menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo
 menu.track.markrectangle=Ozna\u010dit body v obd\u00e9ln\u00edku
 menu.track.deletemarked=Smazat ozna\u010den\u00e9 body
-menu.track.rearrange=P\u0159euspo\u0159\u00e1dat z\u00e1jmov\u00e9 body
-menu.track.rearrange.start=V\u0161e na po\u010d\u00e1tek
-menu.track.rearrange.end=V\u0161e na konec
-menu.track.rearrange.nearest=Zarovnat body na stopu
+function.rearrangewaypoints=P\u0159euspo\u0159\u00e1dat z\u00e1jmov\u00e9 body
 menu.range=Rozmez\u00ed
 menu.range.all=Vybrat v\u0161e
 menu.range.none=Zru\u0161it v\u00fdb\u011br
@@ -413,11 +410,13 @@ dialog.correlate.audioselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fd
 dialog.correlate.select.audioname=N\u00e1zev audionahr\u00e1vky
 dialog.correlate.select.audiolater=Audio pozd\u011bj\u0161\u00ed
 dialog.rearrangephotos.desc=Vyberte um\u00edst\u011bn\u00ed a uspo\u0159\u00e1d\u00e1n\u00ed bod\u016f fotografi\u00ed
-dialog.rearrangephotos.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek
-dialog.rearrangephotos.toend=P\u0159en\u00e9st na konec
-dialog.rearrangephotos.nosort=Neuspo\u0159\u00e1d\u00e1vat
-dialog.rearrangephotos.sortbyfilename=Uspo\u0159\u00e1dat dle n\u00e1zvu souboru
-dialog.rearrangephotos.sortbytime=Uspo\u0159\u00e1dat dle \u010dasu
+dialog.rearrange.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek
+dialog.rearrange.toend=P\u0159en\u00e9st na konec
+dialog.rearrange.tonearest=Zarovnat body na stopu
+dialog.rearrange.nosort=Neuspo\u0159\u00e1d\u00e1vat
+dialog.rearrange.sortbyfilename=Uspo\u0159\u00e1dat dle n\u00e1zvu souboru
+dialog.rearrange.sortbyname=Uspo\u0159\u00e1dat dle n\u00e1zvu
+dialog.rearrange.sortbytime=Uspo\u0159\u00e1dat dle \u010dasu
 dialog.compress.closepoints.title=Odstran\u011bn\u00ed bl\u00edzk\u00fdch bod\u016f
 dialog.compress.closepoints.paramdesc=Koeficient bl\u00edzkosti
 dialog.compress.wackypoints.title=Odstran\u011bn\u00ed ust\u0159elen\u00fdch bod\u016f
@@ -849,7 +848,6 @@ error.playaudiofailed=Nepoda\u0159ilo se p\u0159ehr\u00e1t zvukov\u00fd soubor.
 error.cache.notthere=Nepoda\u0159ilo se nal\u00e9zt adres\u00e1\u0159 s cache map.
 error.cache.empty=Adres\u00e1\u0159 s cache map je pr\u00e1zdn\u00fd.
 error.cache.cannotdelete=Nelze smazat soubory map.
-error.interpolate.invalidparameter=Po\u010det bod\u016f mus\u00ed b\u00fdt mezi 1 a 1000
 error.learnestimationparams.failed=Na z\u00e1klad\u011b t\u00e9to stopy nelze vypo\u010d\u00edtat parametr.\nZkuste jin\u00e9 stopy.
 error.tracksplit.nosplit=Stopu nen\u00ed mo\u017en\u00e9 rozd\u011blit
 error.downloadsrtm.nocache=Nepoda\u0159ilo se ulo\u017eit soubory.\nPros\u00edm ov\u011b\u0159te diskovou cache.
index ea394b97388e35d75b68355379b12ab6d4e8782c..8bcf9caca5acd628411c3c7e03ec235bc798a658 100644 (file)
@@ -10,8 +10,8 @@ menu.file.exit=Afslut
 menu.track.undo=Fortryd
 menu.track.clearundo=Nulstil fortrydelsesliste
 menu.track.deletemarked=Slet markerede punkter
-menu.track.rearrange=Omorganis\u00e9r waypoints
-menu.track.rearrange.nearest=Hvert waypoint til n\u00e6rmeste nabo
+function.rearrangewaypoints=Omorganis\u00e9r waypoints
+dialog.rearrange.tonearest=Hvert waypoint til n\u00e6rmeste nabo
 menu.range=Omr\u00e5de
 menu.range.all=V\u00e6lg alle
 menu.range.none=Ingen valgt
index 19b6b029fe1906183bc79c90b63a38ebce8f33cc..a85515eb3756491ee7b70813a6b0bb5d526183f8 100644 (file)
@@ -13,10 +13,7 @@ menu.track.undo=R\u00fcckg\u00e4ngig
 menu.track.clearundo=Liste der letzten \u00c4nderungen l\u00f6schen
 menu.track.markrectangle=Punkte im Viereck markieren
 menu.track.deletemarked=Markierte Punkte l\u00f6schen
-menu.track.rearrange=Wegpunkte neu anordnen
-menu.track.rearrange.start=Alle Wegpunkte zum Anfang
-menu.track.rearrange.end=Alle Wegpunkte ans Ende
-menu.track.rearrange.nearest=Jeden Wegpunkt zum n\u00e4chsten Trackpunkt verschieben
+function.rearrangewaypoints=Wegpunkte neu anordnen
 menu.range=Bereich
 menu.range.all=Alles markieren
 menu.range.none=Nichts markieren
@@ -87,11 +84,12 @@ function.exportgpx=GPX exportieren
 function.exportpov=POV exportieren
 function.exportsvg=SVG exportieren
 function.exportimage=Bild exportieren
-function.editwaypointname=Name des Punkts bearbeiten
+function.editwaypointname=Namen des Punkts bearbeiten
 function.compress=Track komprimieren
 function.deleterange=Bereich l\u00f6schen
 function.croptrack=Track zuschneiden
 function.interpolate=Punkte interpolieren
+function.deletebydate=Punkte nach Datum l\u00f6schen
 function.addtimeoffset=Zeitverschiebung aufrechnen
 function.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
 function.convertnamestotimes=Namen der Wegpunkte in Zeitstempel umwandeln
@@ -106,6 +104,7 @@ function.estimatetime=Zeit absch\u00e4tzen
 function.learnestimationparams=Zeitparameter erlernen
 function.setmapbg=Karte Hintergrund setzen
 function.setpaths=Programmpfade setzen
+function.selectsegment=Aktuellen Abschnitt markieren
 function.splitsegments=In Trackabschnitte schneiden
 function.sewsegments=Trackabschnitte zusammenf\u00fcgen
 function.getgpsies=Tracks bei GPSies.com herunterladen
@@ -141,6 +140,7 @@ function.saveconfig=Einstellungen speichern
 function.diskcache=Karten auf Festplatte speichern
 function.managetilecache=Kartenkacheln verwalten
 function.getweatherforecast=Wettervorhersage herunterladen
+function.setaltitudetolerance=Altitudtoleranz einstellen
 
 # Dialogs
 dialog.exit.confirm.title=GpsPrune beenden
@@ -379,6 +379,7 @@ dialog.gpsies.activity.sailing=Segeln
 dialog.gpsies.activity.skating=Inline-Skating
 dialog.wikipedia.column.name=Artikelname
 dialog.wikipedia.column.distance=Entfernung
+dialog.wikipedia.nonefound=Keine Punkte gefunden
 dialog.correlate.notimestamps=Die Punkte enthalten keine Zeitangaben, deshalb k\u00f6nnen die Fotos nicht zugeordnet werden.
 dialog.correlate.nouncorrelatedphotos=Alle Fotos sind schon zugeordnet.\nWollen Sie trotzdem fortfahren?
 dialog.correlate.nouncorrelatedaudios=Alle Audiodateien sind schon zugeordnet.\nWollen Sie trotzdem fortfahren?
@@ -412,12 +413,15 @@ dialog.correlate.timestamp.end=Ende
 dialog.correlate.audioselect.intro=W\u00e4hlen Sie eines dieser Audiodateien aus, um die Zeitdifferenz zu berechnen
 dialog.correlate.select.audioname=Audio Name
 dialog.correlate.select.audiolater=Audio sp\u00e4ter
+dialog.rearrangewaypoints.desc=Setzen Sie das Ziel und die Reihenfolge der Wegpunkte
 dialog.rearrangephotos.desc=Setzen Sie das Ziel und die Reihenfolge der Fotopunkte
-dialog.rearrangephotos.tostart=Am Anfang
-dialog.rearrangephotos.toend=Am Ende
-dialog.rearrangephotos.nosort=Nicht sortieren
-dialog.rearrangephotos.sortbyfilename=per Dateiname sortieren
-dialog.rearrangephotos.sortbytime=per Zeitstempel sortieren
+dialog.rearrange.tostart=Zum Anfang
+dialog.rearrange.toend=Zum Ende
+dialog.rearrange.tonearest=Zum n\u00e4chsten Trackpunkt
+dialog.rearrange.nosort=Nicht sortieren
+dialog.rearrange.sortbyfilename=nach Dateinamen sortieren
+dialog.rearrange.sortbyname=nach Namen sortieren
+dialog.rearrange.sortbytime=nach Zeitstempel sortieren
 dialog.compress.closepoints.title=Nahegelegene Punkte entfernen
 dialog.compress.closepoints.paramdesc=Span-Faktor
 dialog.compress.wackypoints.title=Ungew\u00f6hnliche Punkte entfernen
@@ -475,7 +479,7 @@ dialog.checkversion.releasedate1=Diese neue Version ist am
 dialog.checkversion.releasedate2=ver\u00f6ffentlicht worden.
 dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://gpsprune.activityworkshop.net/download.html.
 dialog.keys.intro=Anstelle der Maus k\u00f6nnen Sie folgende Tastenkombinationen nutzen
-dialog.keys.keylist=<table><tr><td>Pfeil Tasten</td><td>Karte verschieben</td></tr><tr><td>Strg + Links-, Rechts-Pfeil</td><td>Vorherigen oder n\u00e4chsten Punkt markieren</td></tr><tr><td>Strg + Auf-, Abw\u00e4rts-Pfeil</td><td>Ein- oder Auszoomen</td></tr><tr><td>Strg + Bild auf, ab</td><td>Vorheriges oder n\u00e4chstes Segment markieren</td></tr><tr><td>Strg + Pos1, Ende</td><td>Ersten oder letzten Punkt markieren</td></tr><tr><td>Entf</td><td>Aktuellen Punkt entfernen</td></tr></table>
+dialog.keys.keylist=<table><tr><td>Pfeil Tasten</td><td>Karte verschieben</td></tr><tr><td>Strg + Links-, Rechts-Pfeil</td><td>Vorherigen oder n\u00e4chsten Punkt markieren</td></tr><tr><td>Strg + Auf-, Abw\u00e4rts-Pfeil</td><td>Ein- oder Auszoomen</td></tr><tr><td>Strg + Bild auf, ab</td><td>Zum vorherigen oder n\u00e4chsten Abschnitt</td></tr><tr><td>Strg + Pos1, Ende</td><td>Ersten oder letzten Punkt markieren</td></tr><tr><td>Entf</td><td>Aktuellen Punkt entfernen</td></tr></table>
 dialog.keys.normalmodifier=Strg
 dialog.keys.macmodifier=Kommando
 dialog.saveconfig.desc=Die folgende Einstellungen k\u00f6nnen gespeichert werden:
@@ -515,6 +519,19 @@ dialog.colourchooser.title=Farbe ausw\u00e4hlen
 dialog.colourchooser.red=Rot
 dialog.colourchooser.green=Gr\u00fcn
 dialog.colourchooser.blue=Blau
+dialog.colourer.intro=F\u00e4rb die Trackpunkte unterschiedlich ein
+dialog.colourer.type=Unterschiedliche Farben
+dialog.colourer.type.none=Keine
+dialog.colourer.type.byfile=Nach Datei
+dialog.colourer.type.bysegment=Nach Abschnitt
+dialog.colourer.type.byaltitude=Nach H\u00f6henangaben
+dialog.colourer.type.byspeed=Nach Geschwindigkeit
+dialog.colourer.type.byvertspeed=Nach vertikalen Geschwindigkeit
+dialog.colourer.type.bygradient=Nach Gef\u00e4lle
+dialog.colourer.type.bydate=Nach Datum
+dialog.colourer.start=Anfangsfarbe
+dialog.colourer.end=Zielfarbe
+dialog.colourer.maxcolours=Maximal Anzahl Farben
 dialog.setlanguage.firstintro=Sie k\u00f6nnen entweder eine von den mitgelieferten Sprachen<p>oder eine Text-Datei ausw\u00e4hlen.
 dialog.setlanguage.secondintro=Sie m\u00fcssen Ihre Einstellungen speichern und dann<p>GpsPrune neu starten, um die Sprache zu \u00e4ndern.
 dialog.setlanguage.language=Sprache
@@ -564,6 +581,13 @@ dialog.weather.wind=Wind
 dialog.weather.temp=Temp
 dialog.weather.humidity=R.L.
 dialog.weather.creditnotice=Diese Daten wurden von openweathermap.org zur Verf\u00fcgung gestellt. Die Webseite hat mehr Information.
+dialog.deletebydate.onlyonedate=Die Punkte fallen alle am gleichen Tag.
+dialog.deletebydate.intro=F\u00fcr jeden gefundenen Datum, k\u00f6nnen Sie die Punkte behalten oder l\u00f6schen.
+dialog.deletebydate.nodate=Ohne Zeitangabe
+dialog.deletebydate.column.keep=Behalten
+dialog.deletebydate.column.delete=L\u00f6schen
+dialog.setaltitudetolerance.text.metres=Mindestabweichung (Meter) f\u00fcr H\u00f6henunterschiede
+dialog.setaltitudetolerance.text.feet=Mindestabweichung (Feet) f\u00fcr H\u00f6henunterschiede
 
 # 3d window
 dialog.3d.title=GpsPrune-3D-Ansicht
@@ -718,9 +742,10 @@ fieldname.longitude=L\u00e4ngengrad
 fieldname.altitude=H\u00f6he
 fieldname.timestamp=Zeitstempel
 fieldname.time=Zeit
+fieldname.date=Datum
 fieldname.waypointname=Name
 fieldname.waypointtype=Typ
-fieldname.newsegment=Segment
+fieldname.newsegment=Abschnitt
 fieldname.custom=Custom
 fieldname.prefix=Feld
 fieldname.distance=L\u00e4nge
@@ -846,7 +871,6 @@ error.playaudiofailed=Das Abspielen der Audiodatei ist fehlgeschlagen
 error.cache.notthere=Der Ordner wurde nicht gefunden
 error.cache.empty=Der Ordner ist leer
 error.cache.cannotdelete=Es konnte keine Kacheln gel\u00f6scht werden
-error.interpolate.invalidparameter=Die Anzahl der Punkte muss zwischen 1 und 1000 liegen
 error.learnestimationparams.failed=Mit diesem Track k\u00f6nnen die Parameter nicht berechnet werden.\nVersuchen Sie mit mehreren Tracks.
 error.tracksplit.nosplit=Der Track konnte nicht aufgesplittet werden.
 error.downloadsrtm.nocache=Die Dateien konnten nicht gespeichert werden.\nBitte pr\u00fcfen Sie den Kartenordner nach.
index 7e9b304cd6f233f897b576de1d18f91511b65f19..a0bad3e91ba69fbc1253ebe882b71713057fbf99 100644 (file)
@@ -13,10 +13,7 @@ menu.track.undo=Undo
 menu.track.clearundo=Undo-Liste l\u00f6sche
 menu.track.markrectangle=P\u00fcnkte inem Viereck markiere
 menu.track.deletemarked=Markierte P\u00fcnkte l\u00f6sche
-menu.track.rearrange=Waypoints reorganisiere
-menu.track.rearrange.start=Alli zum Aafang
-menu.track.rearrange.end=Alli zum \u00c4nde
-menu.track.rearrange.nearest=Jede zum n\u00f6chsti Trackpunkt
+function.rearrangewaypoints=Waypoints reorganisiere
 menu.range=Beriich
 menu.range.all=Alles selektiere
 menu.range.none=N\u00fc\u00fct selektiere
@@ -86,10 +83,11 @@ function.exportgpx=GPX exportier\u00e4
 function.exportpov=POV exportier\u00e4
 function.exportsvg=SVG exportier\u00e4
 function.exportimage=Bild exportier\u00e4
-function.editwaypointname=Waypoint Name editiere
+function.editwaypointname=Waypoint Namen editiere
 function.compress=Track komprimier\u00e4
 function.deleterange=Beriich l\u00f6sche
 function.croptrack=Track zuschniide
+function.deletebydate=P\u00fcnkte na Datum l\u00f6sche
 function.addtimeoffset=Ziitverschiebig zutue
 function.addaltitudeoffset=H\u00f6chiverschiebig zutue
 function.findwaypoint=Waypoint suech\u00e4
@@ -103,6 +101,7 @@ function.fullrangedetails=Zues\u00e4tzlichi Beriichinfos
 function.estimatetime=Ziit absch\u00e4tze
 function.learnestimationparams=Ziitparameter erlerne
 function.setmapbg=Karte Hintegrund setz\u00e4
+function.selectsegment=Aktuelli Segm\u00e4nt selektiere
 function.splitsegments=In Tracksegm\u00e4nte schniide
 function.sewsegments=Tracksegm\u00e4nte z\u00e4mef\u00fcge
 function.getgpsies=Gpsies Tracks hol\u00e4
@@ -136,6 +135,7 @@ function.saveconfig=Iistellige speichere
 function.diskcache=Karten uufem Disk speichere
 function.managetilecache=Kartebildli verwolte
 function.getweatherforecast=W\u00e4tterprognose abalade
+function.setaltitudetolerance=H\u00f6chitoleranz iistelle
 
 # Dialogs
 dialog.exit.confirm.title=GpsPrune be\u00e4nde
@@ -162,8 +162,8 @@ dialog.openoptions.deliminfo.records=Rekords, mit
 dialog.openoptions.deliminfo.fields=F\u00e4ldere
 dialog.openoptions.deliminfo.norecords=Kei Rekords
 dialog.openoptions.altitudeunits=H\u00f6chi Masseiheite
-dialog.openoptions.speedunits=Masseiheite f\u00fcr Geschwindigkeite
-dialog.openoptions.vertspeedunits=Masseiheite f\u00fcr vertikale Geschwindigkeite
+dialog.openoptions.speedunits=Masseiheite f\u00fcr Gschwindikeite
+dialog.openoptions.vertspeedunits=Masseiheite f\u00fcr vertikale Gschwindikeite
 dialog.openoptions.vspeed.positiveup=Positive bed\u00fc\u00fctet uufe
 dialog.openoptions.vspeed.positivedown=Positive bed\u00fc\u00fctet abe
 dialog.open.contentsdoubled=Dieses File h\u00e4t zwei Kopien von j\u00e4dem Punkt,\neimol als Waypoint und eimol als Trackpunkt.
@@ -374,6 +374,7 @@ dialog.gpsies.activity.sailing=Segle
 dialog.gpsies.activity.skating=Inline-Skate
 dialog.wikipedia.column.name=Artikelname
 dialog.wikipedia.column.distance=Entf\u00e4rnig
+dialog.wikipedia.nonefound=Kei Wiki-Iitr\u00e4ge gfunde
 dialog.correlate.notimestamps=Es h\u00e4t kei Ziitst\u00e4mpel inem Track inn\u00e4, so s'isch n\u00f6d m\u00f6glech die F\u00f6telis zu korrelier\u00e4.
 dialog.correlate.nouncorrelatedphotos=Alle F\u00f6telis sin scho korreliert.\nWend Sie trotzdem fortsetz\u00e4?
 dialog.correlate.nouncorrelatedaudios=Alle Audios sin scho korreliert.\nWend Sie trotzdem fortsetz\u00e4?
@@ -407,12 +408,15 @@ dialog.correlate.timestamp.end=\u00c4nde
 dialog.correlate.audioselect.intro=W\u00e4hlet Sie eini vo deren Audios uus, um die Ziitdiffer\u00e4nz zu ber\u00e4chn\u00e4
 dialog.correlate.select.audioname=Audio Name
 dialog.correlate.select.audiolater=Audio sp\u00f6ter
-dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von d P\u00fcnkte setze
-dialog.rearrangephotos.tostart=zum Aafang
-dialog.rearrangephotos.toend=zum \u00c4nde
-dialog.rearrangephotos.nosort=N\u00f6d sortiere
-dialog.rearrangephotos.sortbyfilename=per Filename sortiere
-dialog.rearrangephotos.sortbytime=per Ziit sortiere
+dialog.rearrangewaypoints.desc=Bitte Ziel und Reihefolge von d Waypoints setze
+dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von d F\u00f6telip\u00fcnkte setze
+dialog.rearrange.tostart=zum Aafang
+dialog.rearrange.toend=zum \u00c4nde
+dialog.rearrange.tonearest=zum n\u00f6chsti Trackpunkt
+dialog.rearrange.nosort=N\u00f6d sortiere
+dialog.rearrange.sortbyfilename=nachem Filename sortiere
+dialog.rearrange.sortbyname=nachem Name sortiere
+dialog.rearrange.sortbytime=nachdr Ziit sortiere
 dialog.compress.duplicates.title=Duplikate entf\u00e4rn\u00e4
 dialog.compress.closepoints.title=N\u00f6chigl\u00e4geni P\u00fcnkte entf\u00e4rn\u00e4
 dialog.compress.closepoints.paramdesc=Span Faktor
@@ -510,6 +514,19 @@ dialog.colourchooser.title=Farbe uusw\u00e4hle
 dialog.colourchooser.red=Rot
 dialog.colourchooser.green=Gr\u00fcen
 dialog.colourchooser.blue=Blau
+dialog.colourer.intro=F\u00e4rb die Trackp\u00fcnkte unterschiedlich ii
+dialog.colourer.type=Unterschiedliche Farbe
+dialog.colourer.type.none=Kei
+dialog.colourer.type.byfile=Nach Datei
+dialog.colourer.type.bysegment=Nach Segm\u00e4nt
+dialog.colourer.type.byaltitude=Nach H\u00f6chi
+dialog.colourer.type.byspeed=Nach Gschwindikeit
+dialog.colourer.type.byvertspeed=Nach uf/ab Gschwindikeit
+dialog.colourer.type.bygradient=Nach Gef\u00e4lle
+dialog.colourer.type.bydate=Nach Datum
+dialog.colourer.start=Aafangsfarb
+dialog.colourer.end=Zielfarb
+dialog.colourer.maxcolours=Maximal Anzahl Farbe
 dialog.setlanguage.firstintro=Sie k\u00f6nnet entweder eini vo den iigebouti Sproche<p>oder ne Text-Datei uusw\u00e4hle.
 dialog.setlanguage.secondintro=Sie m\u00fcnt Ihri Iistellige speichere und dann<p>GpsPrune wieder neustarte um die Sproch z'\u00e4ndere.
 dialog.setlanguage.language=Sproch
@@ -559,6 +576,13 @@ dialog.weather.wind=Wind
 dialog.weather.temp=Temp
 dialog.weather.humidity=R.L.
 dialog.weather.creditnotice=Diese Date sin vo openweathermap.org zur Verf\u00fcegig gestellt worde. Uf d Websiite h\u00e4ts no meh Infos.
+dialog.deletebydate.onlyonedate=Die P\u00fcnkte sin alli vom gliichen Tag.
+dialog.deletebydate.intro=F\u00fcr jeden Tag, k\u00f6nnet Sie d P\u00fcnkte behalte oder l\u00f6sche.
+dialog.deletebydate.nodate=Ohni Ziitaagab
+dialog.deletebydate.column.keep=Behalte
+dialog.deletebydate.column.delete=L\u00f6sche
+dialog.setaltitudetolerance.text.metres=Mindeschtabweichig (Meter) f\u00fcr H\u00f6chiunterschied
+dialog.setaltitudetolerance.text.feet=Mindeschtabweichig (Feet) f\u00fcr H\u00f6chinunterschied
 
 # 3d window
 dialog.3d.title=GpsPrune Dr\u00fc\u00fc-d Aasicht
@@ -612,7 +636,7 @@ tip.useamapcache=Mit lokali Kartekachle (Iistellige -> Karten uufem Disk speiche
 tip.learntimeparams=Wenn Sie Track -> Ziitparameter erlerne mit Ihren Tracks benutze\ndann werdet d ber\u00e4chneti Werte gnauer.
 tip.downloadsrtm=Sie k\u00f6nnet d Funktion beschleunige indem Sie\nOnline -> SRTM Files abalade uufrufe\num d Date lokal z'speichere.
 tip.usesrtmfor3d=Dere Track h\u00e4t kei H\u00f6chiinformation.\nSie k\u00f6nnet d SRTM Funktione verw\u00e4nde, um\nH\u00f6chiwerte abz'sch\u00e4tze.
-tip.manuallycorrelateone=Mit mindeschtens einem verbundenen Elem\u00e4nt kann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
+tip.manuallycorrelateone=Mit mindeschtens einem verbundenen Elem\u00e4nt chann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
 
 # Buttons
 button.ok=OK
@@ -713,6 +737,7 @@ fieldname.longitude=L\u00e4ngegrad
 fieldname.altitude=H\u00f6chi
 fieldname.timestamp=Ziitst\u00e4mpel
 fieldname.time=Ziit
+fieldname.date=Tag
 fieldname.waypointname=Name
 fieldname.waypointtype=Typ
 fieldname.newsegment=Segm\u00e4nt
@@ -841,7 +866,6 @@ error.playaudiofailed=S Abschpiele vonem File isch fehlgschlage
 error.cache.notthere=D Ordner isch n\u00f6d gfunde worde
 error.cache.empty=D Ordner h\u00e4t n\u00fc\u00fct drinne
 error.cache.cannotdelete=Es sin kei Kachle gl\u00f6scht worde
-error.interpolate.invalidparameter=D'Aazahl P\u00fcnkt muess zw\u00fcschet 1 und 1000 sii
 error.learnestimationparams.failed=Mit dere Track k\u00f6nnet die Parameter n\u00f6d br\u00e4chnet werde.\nVersuechet Sie mit mehreri Tracks.
 error.tracksplit.nosplit=Es isch n\u00f6d m\u00f6glech gsi, den Track uufz'schniide.
 error.downloadsrtm.nocache=Die Files k\u00f6nnet n\u00f6d gspeicheret werde.\nBitte pr\u00fcefet Sie den Kartenordner na.
index bfef0dfcd3c42b44cdfc23b2ad76e6cf3488aadb..447ce4dc1f6bf50b96d13db8f6129c5ce67d80e6 100644 (file)
@@ -13,10 +13,6 @@ menu.track.undo=Undo
 menu.track.clearundo=Clear undo list
 menu.track.markrectangle=Mark points in rectangle
 menu.track.deletemarked=Delete marked points
-menu.track.rearrange=Rearrange waypoints
-menu.track.rearrange.start=All to start of file
-menu.track.rearrange.end=All to end of file
-menu.track.rearrange.nearest=Each to nearest track point
 menu.range=Range
 menu.range.all=Select all
 menu.range.none=Select none
@@ -92,9 +88,11 @@ function.compress=Compress track
 function.deleterange=Delete range
 function.croptrack=Crop track
 function.interpolate=Interpolate points
+function.deletebydate=Delete points by date
 function.addtimeoffset=Add time offset
 function.addaltitudeoffset=Add altitude offset
 function.findwaypoint=Find waypoint
+function.rearrangewaypoints=Rearrange waypoints
 function.convertnamestotimes=Convert waypoint names to times
 function.deletefieldvalues=Delete field values
 function.pastecoordinates=Enter new coordinates
@@ -104,6 +102,7 @@ function.distances=Distances
 function.fullrangedetails=Full range details
 function.estimatetime=Estimate time
 function.learnestimationparams=Learn time estimation parameters
+function.selectsegment=Select current segment
 function.splitsegments=Split track into segments
 function.sewsegments=Sew track segments together
 function.getgpsies=Get Gpsies tracks
@@ -141,6 +140,7 @@ function.saveconfig=Save settings
 function.diskcache=Save maps to disk
 function.managetilecache=Manage tile cache
 function.getweatherforecast=Get weather forecast
+function.setaltitudetolerance=Set altitude tolerance
 
 # Dialogs
 dialog.exit.confirm.title=Exit GpsPrune
@@ -218,7 +218,7 @@ dialog.save.table.field=Field
 dialog.save.table.hasdata=Has data
 dialog.save.table.save=Save
 dialog.save.headerrow=Output header row
-dialog.save.coordinateunits=Coordinate units
+dialog.save.coordinateunits=Coordinate format
 dialog.save.altitudeunits=Altitude units
 dialog.save.timestampformat=Timestamp format
 dialog.save.overwrite.title=File already exists
@@ -379,6 +379,7 @@ dialog.gpsies.activity.sailing=Sailing
 dialog.gpsies.activity.skating=Skating
 dialog.wikipedia.column.name=Article name
 dialog.wikipedia.column.distance=Distance
+dialog.wikipedia.nonefound=No wikipedia entries found
 dialog.correlate.notimestamps=There are no timestamps in the data points, so there is nothing to correlate with the photos.
 dialog.correlate.nouncorrelatedphotos=There are no uncorrelated photos.\nAre you sure you want to continue?
 dialog.correlate.nouncorrelatedaudios=There are no uncorrelated audios.\nAre you sure you want to continue?
@@ -412,12 +413,15 @@ dialog.correlate.timestamp.end=End
 dialog.correlate.audioselect.intro=Select one of these correlated audios to use as the time offset
 dialog.correlate.select.audioname=Audio name
 dialog.correlate.select.audiolater=Audio later
+dialog.rearrangewaypoints.desc=Select the destination and sort order of the waypoints
 dialog.rearrangephotos.desc=Select the destination and sort order of the photo points
-dialog.rearrangephotos.tostart=Move to start
-dialog.rearrangephotos.toend=Move to end
-dialog.rearrangephotos.nosort=Don't sort
-dialog.rearrangephotos.sortbyfilename=Sort by filename
-dialog.rearrangephotos.sortbytime=Sort by time
+dialog.rearrange.tostart=Move to start
+dialog.rearrange.toend=Move to end
+dialog.rearrange.tonearest=Each to nearest track point
+dialog.rearrange.nosort=Don't sort
+dialog.rearrange.sortbyfilename=Sort by filename
+dialog.rearrange.sortbyname=Sort by name
+dialog.rearrange.sortbytime=Sort by time
 dialog.compress.duplicates.title=Duplicate removal
 dialog.compress.closepoints.title=Nearby point removal
 dialog.compress.closepoints.paramdesc=Span factor
@@ -515,6 +519,19 @@ dialog.colourchooser.title=Choose colour
 dialog.colourchooser.red=Red
 dialog.colourchooser.green=Green
 dialog.colourchooser.blue=Blue
+dialog.colourer.intro=A point colourer can give track points different colours
+dialog.colourer.type=Colourer type
+dialog.colourer.type.none=None
+dialog.colourer.type.byfile=By file
+dialog.colourer.type.bysegment=By segment
+dialog.colourer.type.byaltitude=By altitude
+dialog.colourer.type.byspeed=By speed
+dialog.colourer.type.byvertspeed=By vertical speed
+dialog.colourer.type.bygradient=By gradient
+dialog.colourer.type.bydate=By date
+dialog.colourer.start=Start colour
+dialog.colourer.end=End colour
+dialog.colourer.maxcolours=Maximum number of colours
 dialog.setlanguage.firstintro=You can either select one of the included languages,<p>or select a text file to use instead.
 dialog.setlanguage.secondintro=You need to save your settings and then<p>restart GpsPrune to change the language.
 dialog.setlanguage.language=Language
@@ -564,6 +581,13 @@ dialog.weather.wind=Wind
 dialog.weather.temp=Temp
 dialog.weather.humidity=Humidity
 dialog.weather.creditnotice=This data is made available by openweathermap.org. Their website has more details.
+dialog.deletebydate.onlyonedate=The points were all recorded on the same date.
+dialog.deletebydate.intro=For each date in the track, you can choose to delete or keep the points
+dialog.deletebydate.nodate=No timestamp
+dialog.deletebydate.column.keep=Keep
+dialog.deletebydate.column.delete=Delete
+dialog.setaltitudetolerance.text.metres=Limit (in metres) below which small climbs and descents will be ignored
+dialog.setaltitudetolerance.text.feet=Limit (in feet) below which small climbs and descents will be ignored
 
 # 3d window
 dialog.3d.title=GpsPrune Three-d view
@@ -718,6 +742,7 @@ fieldname.longitude=Longitude
 fieldname.altitude=Altitude
 fieldname.timestamp=Time
 fieldname.time=Time
+fieldname.date=Date
 fieldname.waypointname=Name
 fieldname.waypointtype=Type
 fieldname.newsegment=Segment
@@ -852,7 +877,6 @@ error.playaudiofailed=Failed to play audio clip
 error.cache.notthere=The tile cache directory was not found
 error.cache.empty=The tile cache directory is empty
 error.cache.cannotdelete=No tiles could be deleted
-error.interpolate.invalidparameter=The number of points must be between 1 and 1000
 error.learnestimationparams.failed=Cannot learn the parameters from this track.\nTry loading more tracks.
 error.tracksplit.nosplit=The track could not be split
 error.downloadsrtm.nocache=The files could not be saved.\nPlease check the disk cache.
index a28dd50c88ccbf20f9e6283a50cc3b5b4c74fb99..d6724aba79ac694a595f345e8f84bad3ab3e1a3f 100644 (file)
@@ -7,8 +7,16 @@ function.setcolours=Set colors
 # Dialogs
 dialog.exportkml.trackcolour=Track color
 dialog.saveconfig.prune.languagecode=Language code (EN_US)
+dialog.saveconfig.prune.colourscheme=Color scheme
+dialog.saveconfig.prune.kmltrackcolour=KML track color
 dialog.setcolours.intro=Click on a color patch to change the color
 dialog.colourchooser.title=Choose color
+dialog.colourer.intro=A point colorer can give track points different colors
+dialog.colourer.type=Colorer type
+dialog.colourer.start=Start color
+dialog.colourer.end=End color
+dialog.colourer.maxcolours=Maximum number of colors
+dialog.setaltitudetolerance.text.metres=Limit (in meters) below which small climbs and descents will be ignored
 
 # Measurement units
 units.metres=Meters
index b752c7cd1a295f9145ec425ec6a950ce30099325..63901b28ee364e15f6ee6f7c69b292241f096afd 100644 (file)
@@ -13,10 +13,7 @@ menu.track.undo=Deshacer
 menu.track.clearundo=Despejar la lista de deshacer
 menu.track.markrectangle=Marcar puntos dentro de un rect\u00e1ngulo
 menu.track.deletemarked=Eliminar puntos marcados
-menu.track.rearrange=Reorganizar waypoints
-menu.track.rearrange.start=Volver al comienzo
-menu.track.rearrange.end=Ir al final
-menu.track.rearrange.nearest=Ir al m\u00e1s pr\u00f3ximo
+function.rearrangewaypoints=Reorganizar waypoints
 menu.range=Rango
 menu.range.all=Seleccionar todo
 menu.range.none=No seleccionar nada
@@ -92,6 +89,7 @@ function.compress=Comprimir track
 function.deleterange=Eliminar rango
 function.croptrack=Truncar track
 function.interpolate=Interpolar puntos
+function.deletebydate=Eliminar puntos de acuerdo a la fecha
 function.addtimeoffset=A\u00f1adir compensar tiempo
 function.addaltitudeoffset=A\u00f1adir compensar altitud
 function.convertnamestotimes=Convertir los nombres de los "waypoints" a tiempo
@@ -105,6 +103,7 @@ function.fullrangedetails=Detalles adicionales de rango
 function.estimatetime=Estimar duraci\u00f3n
 function.setmapbg=Configurar fondo de mapa
 function.setpaths=Configurar rutas del programas
+function.selectsegment=Seleccionar segmento actual
 function.splitsegments=Segmentar el track
 function.sewsegments=Ensamblar los segmentos
 function.getgpsies=Bajar ruta de Gpsies
@@ -363,6 +362,7 @@ dialog.gpsies.activity.sailing=Vela
 dialog.gpsies.activity.skating=Patinaje
 dialog.wikipedia.column.name=Nombre del art\u00edculo
 dialog.wikipedia.column.distance=Distancia
+dialog.wikipedia.nonefound=No se encontraron puntos
 dialog.correlate.notimestamps=No hay informaci\u00f3n de tiempo para los puntos, as\u00ed que no hay nada que correlacionar con las fotos.
 dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar?
 dialog.correlate.nouncorrelatedaudios=No hay audios no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar?
@@ -397,11 +397,13 @@ dialog.correlate.audioselect.intro=Seleccione uno de estos audios correlacionado
 dialog.correlate.select.audioname=Nombre del audio
 dialog.correlate.select.audiolater=Audio m\u00e1s adelante
 dialog.rearrangephotos.desc=Seleccionar el destino y sortear el orden de los puntos de las fotos
-dialog.rearrangephotos.tostart=Mover al comienzo
-dialog.rearrangephotos.toend=Mover al final
-dialog.rearrangephotos.nosort=No sortear
-dialog.rearrangephotos.sortbyfilename=Sortear por nombre del archivo
-dialog.rearrangephotos.sortbytime=Sortear por tiempo
+dialog.rearrange.tostart=Mover al comienzo
+dialog.rearrange.toend=Mover al final
+dialog.rearrange.tonearest=Mover al punto m\u00e1s pr\u00f3ximo
+dialog.rearrange.nosort=No sortear
+dialog.rearrange.sortbyfilename=Sortear por nombre del archivo
+dialog.rearrange.sortbyname=Sortear por nombre
+dialog.rearrange.sortbytime=Sortear por tiempo
 dialog.compress.closepoints.title=remover puntos cercanos
 dialog.compress.closepoints.paramdesc=Factor de extensi\u00f3n
 dialog.compress.wackypoints.title=Eliminar puntos an\u00f3malos
@@ -499,6 +501,8 @@ dialog.colourchooser.title=Elegir color
 dialog.colourchooser.red=Rojo
 dialog.colourchooser.green=Verde
 dialog.colourchooser.blue=Azul
+dialog.colourer.start=Color de inicio
+dialog.colourer.end=Color final
 dialog.setlanguage.firstintro=Puede usted seleccionar algunos de los lenguajes incluidos,<p>o puede en lugar de esto seleccionar un archivo de texto
 dialog.setlanguage.secondintro=Usted necesita guardar sus preferencias y luego<p>reiniciar GpsPrune para cambiar el lenguaje
 dialog.setlanguage.language=Lenguaje
@@ -546,6 +550,9 @@ dialog.weather.day.sunday=Domingo
 dialog.weather.wind=Viento
 dialog.weather.temp=Temp
 dialog.weather.humidity=Humedad
+dialog.deletebydate.nodate=Sin marcas de tiempo
+dialog.deletebydate.column.keep=Mantener
+dialog.deletebydate.column.delete=Eliminar
 
 # 3d window
 dialog.3d.title=GpsPrune vista 3-D
@@ -693,6 +700,7 @@ fieldname.longitude=Longitud
 fieldname.altitude=Altitud
 fieldname.timestamp=Informaci\u00f3n de tiempo
 fieldname.time=Tiempo
+fieldname.date=Data
 fieldname.waypointname=Nombre
 fieldname.waypointtype=Tipo
 fieldname.newsegment=Segmento
@@ -827,7 +835,6 @@ error.playaudiofailed=Fallo reproduciendo archivo de audio
 error.cache.notthere=No se encontr\u00f3 la carpeta del cache de recuadros
 error.cache.empty=La carpeta del cache de recuadros esta vac\u00edo
 error.cache.cannotdelete=No se pudieron borrar recuadros
-error.interpolate.invalidparameter=El n\u00famero de puntos necesita ser entre 1 y 1000
 error.tracksplit.nosplit=Imposible segmentar el track
 error.downloadsrtm.nocache=Imposible guardar los archivos.\nPor favor, compruebe el cache.
 error.sewsegments.nothingdone=Imposible ensamblar los segmentos.\nEl track tiene ahora %d segmentos.
index 960330d4f8a44c25dbca8c9fb7df2e56799cb868..a70545ba43d9c84f50cda51c3c69de97e1ea72d1 100644 (file)
@@ -10,10 +10,10 @@ menu.track=\u0645\u0633\u064a\u0631
 menu.track.undo=\u0628\u0627\u0637\u0644 \u06a9\u0631\u062f\u0646 \u06a9\u0627\u0631 \u0642\u0628\u0644\u064a
 menu.track.clearundo=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0644\u064a\u0633\u062a \u06a9\u0627\u0631\u0647\u0627\u06cc \u0627\u0646\u062c\u0627\u0645 \u0634\u062f\u0647
 menu.track.deletemarked=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0646\u0642\u0627\u0637 \u0627\u0646\u062a\u062e\u0627\u0628 \u0634\u062f\u0647
-menu.track.rearrange=\u0628\u0627\u0632 \u0686\u064a\u0646\u06cc \u0646\u0642\u0627\u0637 \u0645\u0633\u064a\u0631
-menu.track.rearrange.start=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0628\u062a\u062f\u0627\u06cc \u0645\u0633\u064a\u0631
-menu.track.rearrange.end=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0646\u062a\u0647\u0627\u06cc \u0645\u0633\u064a\u0631
-menu.track.rearrange.nearest=\u0647\u0631 \u064a\u06a9 \u0628\u0647 \u0646\u0632\u062f\u064a\u06a9 \u0646\u0642\u0637\u0647 \u0645\u0633\u064a\u0631
+function.rearrangewaypoints=\u0628\u0627\u0632 \u0686\u064a\u0646\u06cc \u0646\u0642\u0627\u0637 \u0645\u0633\u064a\u0631
+dialog.rearrange.tostart=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0628\u062a\u062f\u0627\u06cc \u0645\u0633\u064a\u0631
+dialog.rearrange.toend=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0646\u062a\u0647\u0627\u06cc \u0645\u0633\u064a\u0631
+dialog.rearrange.tonearest=\u0647\u0631 \u064a\u06a9 \u0628\u0647 \u0646\u0632\u062f\u064a\u06a9 \u0646\u0642\u0637\u0647 \u0645\u0633\u064a\u0631
 menu.range=\u0686\u064a\u0646\u0634
 menu.range.all=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u0645\u0647 \u0646\u0642\u0627\u0637
 menu.range.none=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u064a\u0686 \u064a\u06a9 \u0627\u0632 \u0646\u0642\u0627\u0637
index d271c279a7531c2ae707d2ebfcdb6013c750d267..0e25497fdcf606b39b93d235ec07dcc79de0b71c 100644 (file)
@@ -13,10 +13,7 @@ menu.track.undo=Annuler
 menu.track.clearundo=Purger la liste d'annulation
 menu.track.markrectangle=S\u00e9lectionner les points dans un rectangle
 menu.track.deletemarked=Supprimer les points marqu\u00e9s
-menu.track.rearrange=R\u00e9arranger les waypoints
-menu.track.rearrange.start=Tous au d\u00e9but du fichier
-menu.track.rearrange.end=Tous \u00e0 la fin du fichier
-menu.track.rearrange.nearest=Chacun au point de trace le plus proche
+function.rearrangewaypoints=R\u00e9arranger les waypoints
 menu.range=\u00c9tendue
 menu.range.all=Tout s\u00e9lectionner
 menu.range.none=Rien s\u00e9lectionner
@@ -103,6 +100,7 @@ function.show3d=Montrer en 3D
 function.distances=Distances
 function.fullrangedetails=Montrer tous les d\u00e9tails
 function.estimatetime=Temps estim\u00e9
+function.learnestimationparams=Apprendre les param\u00e8tres pour l'estimation
 function.setmapbg=D\u00e9finir le fond de carte
 function.setpaths=D\u00e9finir les chemins des programmes
 function.splitsegments=S\u00e9pare les segments
@@ -360,6 +358,7 @@ dialog.gpsies.activity.sailing=Volle
 dialog.gpsies.activity.skating=Skating
 dialog.wikipedia.column.name=Nom de l'article
 dialog.wikipedia.column.distance=Distance
+dialog.wikipedia.nonefound=Aucune points trouv\u00e9e
 dialog.correlate.notimestamps=Les points n'ont pas d'indication de temps, il n'est pas possible de les corr\u00e9ler.
 dialog.correlate.nouncorrelatedphotos=Il n'y a pas de photos non-corr\u00e9l\u00e9es.\nVoulez-vous continuer ?
 dialog.correlate.nouncorrelatedaudios=Il n'y a pas d'audios non-corr\u00e9l\u00e9s.\nVoulez-vous continuer ?
@@ -394,11 +393,13 @@ dialog.correlate.audioselect.intro=Choisir un de ces fichiers audio corr\u00e9l\
 dialog.correlate.select.audioname=Nom du fichier audio
 dialog.correlate.select.audiolater=Audio apr\u00e8s
 dialog.rearrangephotos.desc=Choisissez la destination et l\u2019ordre des points des photos
-dialog.rearrangephotos.tostart=Aller au d\u00e9but
-dialog.rearrangephotos.toend=Aller \u00e0 la fin
-dialog.rearrangephotos.nosort=Ne pas trier
-dialog.rearrangephotos.sortbyfilename=Trier par nom de fichier
-dialog.rearrangephotos.sortbytime=Trier par horodatage
+dialog.rearrange.tostart=Au d\u00e9but
+dialog.rearrange.toend=\u00e0 la fin
+dialog.rearrange.tonearest=Au point de trace le plus proche
+dialog.rearrange.nosort=Ne pas trier
+dialog.rearrange.sortbyfilename=Trier par nom de fichier
+dialog.rearrange.sortbyname=Trier par nom
+dialog.rearrange.sortbytime=Trier par horodatage
 dialog.compress.closepoints.title=Suppression des points voisins
 dialog.compress.closepoints.paramdesc=Taille du voisinage
 dialog.compress.wackypoints.title=Suppression des points anormaux
@@ -545,6 +546,8 @@ dialog.weather.wind=Vent
 dialog.weather.temp=Temp
 dialog.weather.humidity=Humidit\u00e9
 dialog.weather.creditnotice=Ces donn\u00e9es sont fournies par openweathermap.org. Consultez la page pour plus de d\u00e9tails.
+dialog.deletebydate.column.keep=Conserver
+dialog.deletebydate.column.delete=Supprimer
 
 # 3d window
 dialog.3d.title=Vue 3D de GpsPrune
@@ -831,5 +834,4 @@ error.playaudiofailed=\u00c9chec de la lecture du fichier audio
 error.cache.notthere=Le dossier du cache n'a pas \u00e9t\u00e9 trouv\u00e9
 error.cache.empty=Le dossier du cache est vide
 error.cache.cannotdelete=Effacement des dalles impossible
-error.interpolate.invalidparameter=Le nombre de points doit \u00eatre compris entre 1 et 1000
 error.tracksplit.nosplit=Impossible de s\u00e9parer les segments
index a1bf2ec6677d5fbc4471922a39a6dd0426cd4ba0..b4a9fafd692a58374e3d88d39814daa9fc5e9584 100644 (file)
@@ -13,10 +13,8 @@ menu.track.undo=Visszavon\u00e1s
 menu.track.clearundo=Visszavon\u00e1si lista t\u00f6rl\u00e9se
 menu.track.markrectangle=N\u00e9gyzeten bel\u00fcli pontok megjel\u00f6l\u00e9se
 menu.track.deletemarked=Jel\u00f6lt pontok t\u00f6rl\u00e9se
-menu.track.rearrange=\u00datpontok \u00fajrarendez\u00e9se
-menu.track.rearrange.start=\u00d6sszes a f\u00e1jl elej\u00e9re
-menu.track.rearrange.end=\u00d6sszes a f\u00e1jl v\u00e9g\u00e9re
-menu.track.rearrange.nearest=Egyenk\u00e9nt a legk\u00f6zelebbi nyomponthoz
+function.rearrangewaypoints=\u00datpontok \u00fajrarendez\u00e9se
+dialog.rearrange.tonearest=Egyenk\u00e9nt a legk\u00f6zelebbi nyomponthoz
 menu.range=Tartom\u00e1ny
 menu.range.all=Mindet kijel\u00f6l
 menu.range.none=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
@@ -413,11 +411,11 @@ dialog.correlate.audioselect.intro=V\u00e1lasszon egyet ezek k\u00f6z\u00fcl a m
 dialog.correlate.select.audioname=Hang neve
 dialog.correlate.select.audiolater=Hang k\u00e9s\u0151bb
 dialog.rearrangephotos.desc=V\u00e1lassza ki a f\u00e9nyk\u00e9ppontok c\u00e9lj\u00e1t \u00e9s rendez\u00e9si sorrendj\u00e9t
-dialog.rearrangephotos.tostart=Mozgat\u00e1s a kezdet\u00e9hez
-dialog.rearrangephotos.toend=Mozgat\u00e1s a v\u00e9g\u00e9hez
-dialog.rearrangephotos.nosort=Ne rendezze
-dialog.rearrangephotos.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint
-dialog.rearrangephotos.sortbytime=Rendez\u00e9s id\u0151 szerint
+dialog.rearrange.tostart=Mozgat\u00e1s a kezdet\u00e9hez
+dialog.rearrange.toend=Mozgat\u00e1s a v\u00e9g\u00e9hez
+dialog.rearrange.nosort=Ne rendezze
+dialog.rearrange.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint
+dialog.rearrange.sortbytime=Rendez\u00e9s id\u0151 szerint
 dialog.compress.closepoints.title=K\u00f6zeli pontok elt\u00e1vol\u00edt\u00e1sa
 dialog.compress.closepoints.paramdesc=Hat\u00f3t\u00e1vols\u00e1g
 dialog.compress.wackypoints.title=Kisz\u00e1m\u00edthatatlan pontok elt\u00e1vol\u00edt\u00e1sa
@@ -848,7 +846,6 @@ error.playaudiofailed=A hangf\u00e1jl lej\u00e1tsz\u00e1sa nem siker\u00fclt
 error.cache.notthere=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem tal\u00e1lhat\u00f3
 error.cache.empty=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra \u00fcres
 error.cache.cannotdelete=Nincs t\u00f6r\u00f6lhet\u0151 csempe
-error.interpolate.invalidparameter=A pontok sz\u00e1ma 1 \u00e9s 1000 k\u00f6z\u00f6tt kell legyen
 error.learnestimationparams.failed=Nem lehet param\u00e9tereket tanulni ebb\u0151l a nyomvonalb\u00f3l.\nT\u00f6lts be t\u00f6bb nyomvonalat.
 error.tracksplit.nosplit=A nyomvonal nem elv\u00e1ghat\u00f3
 error.downloadsrtm.nocache=A f\u00e1jlokat nem siker\u00fclt meneni.\nK\u00e9rlek, ellen\u0151rizd a gyors\u00edt\u00f3t\u00e1rat.
index 14df8169a16abae54d82793a0e452175679537fc..c437011bfadf72d87f6198f4609a56906ea69fac 100644 (file)
@@ -13,10 +13,7 @@ menu.track.undo=Annulla
 menu.track.clearundo=Cancella lista annulla
 menu.track.markrectangle=Segnare i punti nel rettangolo
 menu.track.deletemarked=Cancella punti marcati
-menu.track.rearrange=Riorganizza waypoint
-menu.track.rearrange.start=Tutti all'inizio del file
-menu.track.rearrange.end=Tutti alla fine del file
-menu.track.rearrange.nearest=Sul punto pi\u00f9 vicino
+function.rearrangewaypoints=Riorganizza waypoint
 menu.range=Serie
 menu.range.all=Seleziona tutto
 menu.range.none=Deseleziona tutto
@@ -92,6 +89,7 @@ function.compress=Comprimi la traccia
 function.deleterange=Cancella la serie
 function.croptrack=Cima la traccia
 function.interpolate=Interpola i punti
+function.deletebydate=Cancella punti secondo la data
 function.addtimeoffset=Aggiungi uno scarto temporale
 function.addaltitudeoffset=Aggiungi uno scarto di altitudine
 function.convertnamestotimes=Converti nomi dei waypoint in orari
@@ -106,6 +104,7 @@ function.estimatetime=Stima durata
 function.learnestimationparams=Apprendi parametri di stima
 function.setmapbg=Configura sfondo mappa
 function.setpaths=Configura percorsi programmi
+function.selectsegment=Seleziona segmento corrente
 function.splitsegments=Dividi traccia in segmenti
 function.sewsegments=Riorganizza segmenti insieme
 function.getgpsies=Ottieni tracce da Gpsies
@@ -141,6 +140,7 @@ function.saveconfig=Salva configurazione
 function.diskcache=Salva mappe su disco
 function.managetilecache=Gestione del cache di tasselli
 function.getweatherforecast=Ottieni previsioni del tempo
+function.setaltitudetolerance=Configura tolleranza di altitudini
 
 # Dialogs
 dialog.exit.confirm.title=Esci da GpsPrune
@@ -379,6 +379,7 @@ dialog.gpsies.activity.sailing=Navigazione
 dialog.gpsies.activity.skating=Pattinaggio
 dialog.wikipedia.column.name=Titolo articolo
 dialog.wikipedia.column.distance=Distanza
+dialog.wikipedia.nonefound=Nessuna punti trovata
 dialog.correlate.notimestamps=Non ci sono informazioni temporali tra i dati dei punti, non c'\u00e8 niente per collegarli con le foto.
 dialog.correlate.nouncorrelatedphotos=Non ci sono foto non correlate.\nSei sicuro di voler continuare?
 dialog.correlate.nouncorrelatedaudios=Non ci sono audio non correlati. \nSei sicuro di voler continuare?
@@ -413,11 +414,14 @@ dialog.correlate.audioselect.intro=Seleziona una di queste riprese audio correla
 dialog.correlate.select.audioname=Nome ripresa audio
 dialog.correlate.select.audiolater=Ripresa audio successiva
 dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei punti foto
-dialog.rearrangephotos.tostart=Sposta all'inizio
-dialog.rearrangephotos.toend=Sposta alla fine
-dialog.rearrangephotos.nosort=Non mettere in ordine
-dialog.rearrangephotos.sortbyfilename=Metti in ordine di nome del file
-dialog.rearrangephotos.sortbytime=Metti in ordine di tempo
+dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei waypoint
+dialog.rearrange.tostart=Sposta all'inizio
+dialog.rearrange.toend=Sposta alla fine
+dialog.rearrange.tonearest=Sul punto pi\u00f9 vicino
+dialog.rearrange.nosort=Non mettere in ordine
+dialog.rearrange.sortbyfilename=Metti in ordine di nome del file
+dialog.rearrange.sortbyname=Metti in ordine di nome
+dialog.rearrange.sortbytime=Metti in ordine di tempo
 dialog.compress.closepoints.title=Cancella punti vicini
 dialog.compress.closepoints.paramdesc=Fattore vicinanza
 dialog.compress.wackypoints.title=Cancella punti strani
@@ -475,7 +479,7 @@ dialog.checkversion.releasedate1=Questa nuova versione \u00e8 stata rilasciata i
 dialog.checkversion.releasedate2=.
 dialog.checkversion.download=Per scaricare la nuova versione vai a http://gpsprune.activityworkshop.net/download.html.
 dialog.keys.intro=Puoi utilizzare i seguenti tast di scelta rapida al posto del mouse
-dialog.keys.keylist=<table><tr><td>Tasti freccia</td><td>Muovi mappa destra, sinistra, su, giu'</td></tr><tr><td>Ctrl + freccia destra, sinistra</td><td>Selezione punto successivo o precedente</td></tr><tr><td>Ctrl + freccia su, giu'</td><td>Zoom in o out</td></tr><tr><td>Del</td><td>Cancella punto attuale</td></tr></table>
+dialog.keys.keylist=<table><tr><td>Tasti freccia</td><td>Muovi mappa destra, sinistra, su, giu'</td></tr><tr><td>Ctrl + freccia destra, sinistra</td><td>Selezione punto successivo o precedente</td></tr><tr><td>Ctrl + freccia su, giu'</td><td>Zoom in o out</td></tr><tr><td>Ctrl + pagina su, giu'</td><td>Segmento successivo o precedente</tr><tr><td>Ctrl + Home, End</td><td>Punto primo o ultimo</td></tr><tr><td>Del</td><td>Cancella punto attuale</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Comando
 dialog.saveconfig.desc=Le configurazioni seguenti possono essere salvati in un file di configurazione:
@@ -515,6 +519,16 @@ dialog.colourchooser.title=Scegli colore
 dialog.colourchooser.red=Rosso
 dialog.colourchooser.green=Verde
 dialog.colourchooser.blue=Blu
+dialog.colourer.type.byfile=Per file
+dialog.colourer.type.bysegment=Per segmento
+dialog.colourer.type.byaltitude=Per altitud
+dialog.colourer.type.byspeed=Per velocit\u00e0
+dialog.colourer.type.byvertspeed=Per velocit\u00e0 verticale
+dialog.colourer.type.bygradient=Per gradiente
+dialog.colourer.type.bydate=Per data
+dialog.colourer.start=Colore iniziale
+dialog.colourer.end=Colore finale
+dialog.colourer.maxcolours=Numero massimo di colori
 dialog.setlanguage.firstintro=Puoi selezionare una delle lingue incluse,<p>oppure selezionare un file di testo.
 dialog.setlanguage.secondintro=Devi salvare le impostazioni e<p> riavviare GpsPrune per cambiare la lingua.
 dialog.setlanguage.language=Lingua
@@ -564,6 +578,9 @@ dialog.weather.wind=Vento
 dialog.weather.temp=Temp
 dialog.weather.humidity=Umidit\u00e0
 dialog.weather.creditnotice=Queste informazioni sono rese disponibili da openweathermap.org. Il loro sito web contiene ulteriori dettagli.
+dialog.deletebydate.nodate=Senza dati temporali
+dialog.deletebydate.column.keep=Tieni
+dialog.deletebydate.column.delete=Cancella
 
 # 3d window
 dialog.3d.title=Visione GpsPrune in 3D
@@ -718,6 +735,7 @@ fieldname.longitude=Longitudine
 fieldname.altitude=Altitudine
 fieldname.timestamp=Dati temporali
 fieldname.time=Tempo
+fieldname.date=Data
 fieldname.waypointname=Nome
 fieldname.waypointtype=Tipo
 fieldname.newsegment=Segmento
@@ -852,7 +870,6 @@ error.playaudiofailed=Ripresa audio non riprodotta
 error.cache.notthere=Directory del cache di tasselli non trovato
 error.cache.empty=Directory del cache di tasselli \u00e8 vuoto
 error.cache.cannotdelete=Impossibile cancellare tasselli
-error.interpolate.invalidparameter=Il numero di punti deve essere tra 1 e 1000
 error.learnestimationparams.failed=Non \u00e8 possibile apprendere i parametri da questa traccia.\nProva a caricare pi\u00f9 tracce.
 error.tracksplit.nosplit=La traccia non pu\u00f2 essere divisa
 error.downloadsrtm.nocache=Non \u00e8 stato possibile salvare i file.\nControlla la cache del disco.
index e581c18b51e2fd2246225ba3248126b908424a95..371eec53da3e4e2cb3eb0d6d506a146edf4f6ccb 100644 (file)
@@ -12,10 +12,7 @@ menu.track.undo=\u30a2\u30f3\u30c9\u30a5
 menu.track.clearundo=\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3059\u308b
 menu.track.markrectangle=\u56db\u89d2\u306e\u4e2d\u306b\u5370\u3092\u3064\u3051\u308b
 menu.track.deletemarked=\u5370\u306e\u4ed8\u3044\u305f\u70b9\u3092\u524a\u9664
-menu.track.rearrange=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u4e26\u3079\u66ff\u3048
-menu.track.rearrange.start=\u5168\u3066\u3092\u30d5\u30a1\u30a4\u30eb\u306e\u59cb\u70b9\u306b
-menu.track.rearrange.end=\u5168\u3066\u3092\u30d5\u30a1\u30a4\u30eb\u306e\u7d42\u70b9\u306b
-menu.track.rearrange.nearest=\u305d\u308c\u305e\u308c\u3092\u6700\u8fd1\u306e\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306b
+function.rearrangewaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u4e26\u3079\u66ff\u3048
 menu.range=\u7bc4\u56f2(R)
 menu.range.all=\u5168\u3066\u9078\u629e
 menu.range.none=\u9078\u629e\u89e3\u9664
@@ -317,11 +314,12 @@ dialog.correlate.timestamp.middle=\u4e2d\u9593\u70b9
 dialog.correlate.timestamp.end=\u7d42\u70b9
 dialog.correlate.select.audioname=\u30aa\u30fc\u30c7\u30a3\u30aa\u540d
 dialog.rearrangephotos.desc=\u5411\u304b\u3046\u5148\u3092\u9078\u629e\u3057\u3066\u3001\u5199\u771f\u306e\u70b9\u3092\u4e26\u3079\u76f4\u3059\u3002
-dialog.rearrangephotos.tostart=\u79fb\u52d5\u958b\u59cb
-dialog.rearrangephotos.toend=\u79fb\u52d5\u7d42\u4e86
-dialog.rearrangephotos.nosort=\u4e26\u3079\u66ff\u3048\u306a\u3044
-dialog.rearrangephotos.sortbyfilename=\u30d5\u30a1\u30a4\u30eb\u540d\u3067\u4e26\u3079\u66ff\u3048
-dialog.rearrangephotos.sortbytime=\u6642\u9593\u3067\u4e26\u3079\u66ff\u3048
+dialog.rearrange.tostart=\u79fb\u52d5\u958b\u59cb
+dialog.rearrange.toend=\u79fb\u52d5\u7d42\u4e86
+dialog.rearrange.tonearest=\u305d\u308c\u305e\u308c\u3092\u6700\u8fd1\u306e\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306b
+dialog.rearrange.nosort=\u4e26\u3079\u66ff\u3048\u306a\u3044
+dialog.rearrange.sortbyfilename=\u30d5\u30a1\u30a4\u30eb\u540d\u3067\u4e26\u3079\u66ff\u3048
+dialog.rearrange.sortbytime=\u6642\u9593\u3067\u4e26\u3079\u66ff\u3048
 dialog.compress.closepoints.title=\u8fd1\u508d\u70b9\u3092\u524a\u9664
 dialog.compress.closepoints.paramdesc=\u8fd1\u508d\u4fc2\u6570
 dialog.compress.wackypoints.title=\u304a\u304b\u3057\u306a\u70b9\u306e\u524a\u9664
index b14b0a5b6fa09761f499602e84e8d4e2e47455cd..69722c941cb9abcb10694c0e9162acd4c73d04bf 100644 (file)
@@ -10,10 +10,7 @@ menu.track=\ud2b8\ub799
 menu.track.undo=\uc2e4\ud589\ucde8\uc18c
 menu.track.clearundo=\uc2e4\ud589\ucde8\uc18c \ubaa9\ub85d \uc0ad\uc81c
 menu.track.deletemarked=\ud45c\uc2dc\ub41c \uc9c0\uc810 \uc9c0\uc6b0\uae30
-menu.track.rearrange=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c
-menu.track.rearrange.start=\uc2dc\uc791 \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
-menu.track.rearrange.end=\ub05d \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
-menu.track.rearrange.nearest=\uac00\uae4c\uc6b4 \ud2b8\ub799\uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
+function.rearrangewaypoints=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c
 menu.range=\uc5f0\uacb0\uc120
 menu.range.all=\ubaa8\ub450 \uc120\ud0dd
 menu.range.none=\uc120\ud0dd\ud558\uc9c0 \uc54a\uae30
@@ -320,11 +317,12 @@ dialog.correlate.audioselect.intro=\ud0c0\uc784\uc624\ud504\uc14b\uc73c\ub85c \u
 dialog.correlate.select.audioname=\uc18c\ub9ac \uc774\ub984
 dialog.correlate.select.audiolater=\uc18c\ub9ac \ud6c4\uc5d0
 dialog.rearrangephotos.desc=\uc0ac\uc9c4 \uc9c0\uc810\ub4e4\uc744 \uc5b4\ub5bb\uac8c \uc815\ub82c\ud560 \uac74\uc9c0 \uc120\ud0dd\ud574\uc8fc\uc138\uc694.
-dialog.rearrangephotos.tostart=\uc2dc\uc791\uc73c\ub85c
-dialog.rearrangephotos.toend=\ub05d\uc73c\ub85c
-dialog.rearrangephotos.nosort=\uc815\ub82c\ud558\uc9c0 \uc54a\uae30
-dialog.rearrangephotos.sortbyfilename=\ud30c\uc77c \uc774\ub984\uc73c\ub85c \uc815\ub82c
-dialog.rearrangephotos.sortbytime=\uc2dc\uac04\uc73c\ub85c \uc815\ub82c
+dialog.rearrange.tostart=\uc2dc\uc791\uc73c\ub85c
+dialog.rearrange.toend=\ub05d\uc73c\ub85c
+dialog.rearrange.tonearest=\uac00\uae4c\uc6b4 \ud2b8\ub799\uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
+dialog.rearrange.nosort=\uc815\ub82c\ud558\uc9c0 \uc54a\uae30
+dialog.rearrange.sortbyfilename=\ud30c\uc77c \uc774\ub984\uc73c\ub85c \uc815\ub82c
+dialog.rearrange.sortbytime=\uc2dc\uac04\uc73c\ub85c \uc815\ub82c
 dialog.deletemarked.nonefound=\uc9c0\uc810 \ub370\uc774\ud130\uac00 \uc81c\uac70\ub420 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.
 dialog.compress.closepoints.title=\uc8fc\ubcc0 \ud3ec\uc778\ud2b8 \uc81c\uac70
 dialog.compress.closepoints.paramdesc=\ud655\uc7a5 \uacc4\uc218
index a077bc95e45a2ed9f476aa697d72712fb5f83b35..bcfa3f1a732b4c1b556aad5d422fb3f0e8ef9814 100644 (file)
@@ -13,10 +13,6 @@ menu.track.undo=Ongedaan maken
 menu.track.clearundo=Ongedaan-maken lijst wissen
 menu.track.markrectangle=Makeer alle punten in een vierkant
 menu.track.deletemarked=Verwijderen gemarkeerde punten
-menu.track.rearrange=Rangschikken waypoints
-menu.track.rearrange.start=Alles naar begin route
-menu.track.rearrange.end=Alles naar einde route
-menu.track.rearrange.nearest=Alles naar dichtstbijzijnde routepunt
 menu.range=Reeks
 menu.range.all=Selecteer alles
 menu.range.none=Selecteer geen
@@ -92,8 +88,10 @@ function.compress=Route comprimeren
 function.deleterange=Verwijder reeks
 function.croptrack=Route bijknippen
 function.interpolate=Interpoleer punten
+function.deletebydate=Verwijder punten op datum
 function.addtimeoffset=Tijdsverschil toevoegen
 function.addaltitudeoffset=Hoogteverschil toevoegen
+function.rearrangewaypoints=Rangschikken waypoints
 function.convertnamestotimes=Converteer waypointnamen naar tijden
 function.deletefieldvalues=Verwijder veldwaarden
 function.findwaypoint=Zoek waypoint
@@ -106,6 +104,7 @@ function.estimatetime=Geschatte tijd
 function.learnestimationparams=Parameters voor geschatte tijd
 function.setmapbg=Instellen kaart achtergrond
 function.setpaths=Instellen programmapaden
+function.selectsegment=Selecteer huidige segment
 function.splitsegments=Splits route in segmenten
 function.sewsegments=Voeg segmenten samen
 function.getgpsies=Routes van Gpsies ophalen
@@ -141,6 +140,7 @@ function.saveconfig=Instellingen opslaan
 function.diskcache=Kaart opslaan op schijf
 function.managetilecache=Beheer tegelcache
 function.getweatherforecast=Ophalen weersvoorspelling
+function.setaltitudetolerance=Instellen hoogtetolerantie
 
 # Dialogs
 dialog.exit.confirm.title=GpsPrune afsluiten
@@ -379,6 +379,7 @@ dialog.gpsies.activity.sailing=Zeilen
 dialog.gpsies.activity.skating=Skating
 dialog.wikipedia.column.name=Artikelnaam
 dialog.wikipedia.column.distance=Afstand
+dialog.wikipedia.nonefound=Geen punten gevonden
 dialog.correlate.notimestamps=Er zit geen tijdinformatie in de punten, dus kunnen ze niet aan foto's gekoppeld worden.
 dialog.correlate.nouncorrelatedphotos=Er zijn geen ongekoppelde foto's.\nWeet u zeker dat u wilt doorgaan?
 dialog.correlate.nouncorrelatedaudios=Er zijn geen ongekoppelde geluidsbestanden.\nWeet u zeker dat u wilt doorgaan?
@@ -410,14 +411,17 @@ dialog.correlate.timestamp.beginning=Begin
 dialog.correlate.timestamp.middle=Midden
 dialog.correlate.timestamp.end=Einde
 dialog.correlate.audioselect.intro=Gebruk \u00e9\u00e9n van deze gecorreleerde audiobestanden als tijdsveschil
-dialog.correlate.select.audioname=Naam audiobestsnd
+dialog.correlate.select.audioname=Naam audiobestand
 dialog.correlate.select.audiolater=Audio later
+dialog.rearrangewaypoints.desc=Selecteer doel en sorteervolgorde van de waypoints
 dialog.rearrangephotos.desc=Selecteer doel en sorteervolgorde van de foto punten
-dialog.rearrangephotos.tostart=Naar begin
-dialog.rearrangephotos.toend=Naar einde
-dialog.rearrangephotos.nosort=Niet sorteren
-dialog.rearrangephotos.sortbyfilename=Sorteren op bestandsnaam
-dialog.rearrangephotos.sortbytime=Sorteren op tijd
+dialog.rearrange.tostart=Naar begin
+dialog.rearrange.toend=Naar einde
+dialog.rearrange.tonearest=Naar dichtstbijzijnde routepunt
+dialog.rearrange.nosort=Niet sorteren
+dialog.rearrange.sortbyfilename=Sorteren op bestandsnaam
+dialog.rearrange.sortbyname=Sorteren op naam
+dialog.rearrange.sortbytime=Sorteren op tijd
 dialog.compress.closepoints.title=Verwijder nabijliggende punten
 dialog.compress.closepoints.paramdesc=Bereik
 dialog.compress.wackypoints.title=Vreemde punten verwijderen
@@ -515,6 +519,19 @@ dialog.colourchooser.title=Selecteer een kleur
 dialog.colourchooser.red=Rood
 dialog.colourchooser.green=Groen
 dialog.colourchooser.blue=Blauw
+dialog.colourer.intro=Een puntinkleuring geeft routepunten verschillende kleuren
+dialog.colourer.type=Type inkleuring
+dialog.colourer.type.none=Geen
+dialog.colourer.type.byfile=Op bestand
+dialog.colourer.type.bysegment=Op segment
+dialog.colourer.type.byaltitude=Op hoogte
+dialog.colourer.type.byspeed=Op snelheid
+dialog.colourer.type.byvertspeed=Op verticale snelheid
+dialog.colourer.type.bygradient=Op stijgingspercentage
+dialog.colourer.type.bydate=Op datum
+dialog.colourer.start=Beginkleur
+dialog.colourer.end=Eindkleur
+dialog.colourer.maxcolours=Maximum aantal kleuren
 dialog.setlanguage.firstintro=U kunt kiezen uit \u00e9\u00e9n van de meegeleverde talen,<p>of selecteer een tekstbestand.
 dialog.setlanguage.secondintro=U dient uw instellingen op te slaan en<p>GpsPrune te herstarten om de taal te kunnen wijzigen
 dialog.setlanguage.language=Taal
@@ -560,8 +577,17 @@ dialog.weather.day.thursday=Donderdag
 dialog.weather.day.friday=Vrijdag
 dialog.weather.day.saturday=Zaterdag
 dialog.weather.day.sunday=Zondag
+dialog.weather.wind=Wind
+dialog.weather.temp=Temp
 dialog.weather.humidity=Luchtvocht.
 dialog.weather.creditnotice=Deze gegevens worden beschikbaar gesteld door openweathermap.org. Hun website heeft meer details.
+dialog.deletebydate.onlyonedate=Alle punten werden op dezelfde datum opgenomen.
+dialog.deletebydate.intro=Je kan voor iedere datum in de route kiezen of je de punten wilt verwijderen of behouden
+dialog.deletebydate.nodate=Geen tijden
+dialog.deletebydate.column.keep=Behouden
+dialog.deletebydate.column.delete=Verwijderen
+dialog.setaltitudetolerance.text.metres=Grens (in meters) waaronder kleine klimmen en afdalingen worden genegeerd
+dialog.setaltitudetolerance.text.feet=Grens (in feet) waaronder kleine klimmen en afdalingen worden genegeerd
 
 # 3d window
 dialog.3d.title=GpsPrune in 3D
@@ -716,6 +742,7 @@ fieldname.longitude=Lengtegraad
 fieldname.altitude=Hoogte
 fieldname.timestamp=Tijd
 fieldname.time=Tijd
+fieldname.date=Datum
 fieldname.waypointname=Naam
 fieldname.waypointtype=Type
 fieldname.newsegment=Segment
@@ -757,7 +784,9 @@ units.degmin=Grd-min
 units.deg=Graden
 units.iso8601=ISO 8601
 units.degreescelsius=Celsius
+units.degreescelsius.short=\u00baC
 units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00baF
 
 # How to combine conditions, such as filters
 logic.and=en
@@ -848,7 +877,6 @@ error.playaudiofailed=Kon audiobestand niet afspelen
 error.cache.notthere=De tegelcache map niet gevonden
 error.cache.empty=De tegelcache map is leeg
 error.cache.cannotdelete=Er konden geen tegels verwijderd worden
-error.interpolate.invalidparameter=Aantal punten moet tussen 1 en 1000 liggen
 error.learnestimationparams.failed=Kan geen parameters bepalen van deze route.\nProbeer meer routes te laden.
 error.tracksplit.nosplit=Deze route kon niet opgedeeld worden
 error.downloadsrtm.nocache=De bestanden konden niet worden opgeslagen.\nControleer de schijfcache.
index 80a67c149e994878a750ba8777ab9090a52a3768..e730fb177dd6e9a857bd9efa4f390564602f1d53 100644 (file)
@@ -7,15 +7,12 @@ menu.file.addphotos=Dodaj zdj\u0119cia
 menu.file.recentfiles=Ostatnio u\u017cywane
 menu.file.save=Zapisz
 menu.file.exit=Zako\u0144cz
+menu.online=Online
 menu.track=\u015acie\u017cka
 menu.track.undo=Cofnij
 menu.track.clearundo=Wyczy\u015b\u0107 list\u0119 zmian
 menu.track.markrectangle=Zaznaczenie prostok\u0105tne
 menu.track.deletemarked=Usu\u0144 zaznaczone punkty
-menu.track.rearrange=Zmie\u0144 kolejno\u015b\u0107 punkt\u00f3w po\u015brednich
-menu.track.rearrange.start=Wszystkie na pocz\u0105tek \u015bcie\u017cki
-menu.track.rearrange.end=Wszystkie na koniec \u015bcie\u017cki
-menu.track.rearrange.nearest=Do najbli\u017cszego punktu
 menu.range=Zakres
 menu.range.all=Zaznacz wszystko
 menu.range.none=Usu\u0144 zaznaczenie
@@ -23,7 +20,7 @@ menu.range.start=Zaznacz pocz\u0105tek zakresu
 menu.range.end=Zaznacz koniec zakresu
 menu.range.average=U\u015brednij zaznaczenie
 menu.range.reverse=Odwr\u00f3\u0107 zakres
-menu.range.mergetracksegments=Po\u0142\u0105cz fragmenty \u015bcie\u017cek
+menu.range.mergetracksegments=Scal fragmenty \u015bcie\u017cek
 menu.range.cutandmove=Wytnij i przesu\u0144 zaznaczenie
 menu.point=Punkt
 menu.point.editpoint=Edytuj punkt
@@ -57,6 +54,7 @@ menu.map.editmode=Tryb edycji
 
 # Alt keys for menus
 altkey.menu.file=P
+altkey.menu.online=O
 altkey.menu.track=C
 altkey.menu.range=Z
 altkey.menu.point=U
@@ -90,8 +88,10 @@ function.compress=Kompresuj \u015bcie\u017ck\u0119
 function.deleterange=Usu\u0144 zakres
 function.croptrack=Przytnij \u015bcie\u017ck\u0119
 function.interpolate=Wstaw pomi\u0119dzy punkty
+function.deletebydate=Usu\u0144 punkty wed\u0142ug daty
 function.addtimeoffset=Dodaj przesuni\u0119cie czasu
 function.addaltitudeoffset=Dodaj przesuni\u0119cie wysoko\u015bci
+function.rearrangewaypoints=Zmie\u0144 kolejno\u015b\u0107 punkt\u00f3w po\u015brednich
 function.convertnamestotimes=Zamie\u0144 nazwy punkt\u00f3w na czas
 function.deletefieldvalues=Usu\u0144 warto\u015bci
 function.findwaypoint=Znajd\u017a punkt po\u015bredni
@@ -104,6 +104,9 @@ function.estimatetime=Przewidywany czas
 function.learnestimationparams=Skoryguj wsp\u00f3\u0142czynniki szacowania czasu
 function.setmapbg=Wybierz map\u0119 t\u0142a
 function.setpaths=Ustaw \u015bcie\u017cki do program\u00f3w
+function.selectsegment=Wybierz bie\u017c\u0105cy fragment
+function.splitsegments=Podziel \u015bcie\u017ck\u0119 na fragmenty
+function.sewsegments=Po\u0142\u0105cz fragmenty
 function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies
 function.uploadgpsies=Wy\u015blij \u015bcie\u017cki do Gpsies
 function.lookupsrtm=Pobierz wysoko\u015bci z SRTM
@@ -136,7 +139,8 @@ function.checkversion=Sprawd\u017a czy jest nowa wersja
 function.saveconfig=Zapisz ustawienia
 function.diskcache=Zapisz mapy na dysk
 function.managetilecache=Zarz\u0105dzaj keszem p\u0142ytek
-function.getweatherforecast=Pobierz prognoza pogody
+function.getweatherforecast=Pobierz prognoz\u0119 pogody
+function.setaltitudetolerance=Ustaw tolerancj\u0119 wysoko\u015bci
 
 # Dialogs
 dialog.exit.confirm.title=Zako\u0144cz GpsPrune
@@ -244,6 +248,8 @@ dialog.exportpov.modelstyle=Styl modelu
 dialog.exportpov.ballsandsticks=Kule i pa\u0142ki
 dialog.exportpov.tubesandwalls=Rurki i \u015bciany
 dialog.3d.warningtracksize=Ta \u015bcie\u017cka ma bardzo wiele punkt\u00f3w, kt\u00f3rych Java3D mo\u017ce nie wy\u015bwietli\u0107.\nCzy chcesz kontynuowa\u0107?
+dialog.3d.useterrain=Poka\u017c teren
+dialog.3d.terraingridsize=Rozmiar siatki
 dialog.exportpov.baseimage=Obraz podk\u0142adu
 dialog.exportpov.cannotmakebaseimage=Nie mo\u017cna zapisa\u0107 obrazu podk\u0142adu
 dialog.baseimage.title=Obrazu podk\u0142adu
@@ -373,6 +379,7 @@ dialog.gpsies.activity.sailing=\u017beglarstwo
 dialog.gpsies.activity.skating=Wrotki/rolki
 dialog.wikipedia.column.name=Tytu\u0142 artyku\u0142u
 dialog.wikipedia.column.distance=Odleg\u0142o\u015b\u0107
+dialog.wikipedia.nonefound=Brak wpis\u00f3w w wikipedii
 dialog.correlate.notimestamps=Punkty nie maj\u0105 znacznik\u00f3w czasu, nie mo\u017cna ich powi\u0105za\u0107 ze zdj\u0119ciami.
 dialog.correlate.nouncorrelatedphotos=Nie ma nie powi\u0105zanych zdj\u0119\u0107.\nCzy na pewno chcesz kontynuowa\u0107?
 dialog.correlate.nouncorrelatedaudios=Nie ma nie powi\u0105zanych plik\u00f3w audio.\nCzy na pewno chcesz kontynuowa\u0107?
@@ -406,12 +413,15 @@ dialog.correlate.timestamp.end=ko\u0144ca
 dialog.correlate.audioselect.intro=Wybierz jeden z powi\u0105zanych plik\u00f3w audio i u\u017cyj go jako wzorca do przesuni\u0119cia czasu
 dialog.correlate.select.audioname=nazwa pliku audio
 dialog.correlate.select.audiolater=p\u00f3\u017aniejszy plik audio
+dialog.rearrangewaypoints.desc=Wybierz przeznaczenie i porz\u0105dek sortowania punkt\u00f3w po\u015brednich
 dialog.rearrangephotos.desc=Wybierz przeznaczenie i porz\u0105dek sortowania punkt\u00f3w ze zdj\u0119ciami
-dialog.rearrangephotos.tostart=Przesu\u0144 na pocz\u0105tek
-dialog.rearrangephotos.toend=Przesu\u0144 na koniec
-dialog.rearrangephotos.nosort=Nie sortuj
-dialog.rearrangephotos.sortbyfilename=Sortuj po nazwie pliku
-dialog.rearrangephotos.sortbytime=Sortuj wed\u0142ug czasu
+dialog.rearrange.tostart=Przesu\u0144 na pocz\u0105tek
+dialog.rearrange.toend=Przesu\u0144 na koniec
+dialog.rearrange.tonearest=Do najbli\u017cszego punktu
+dialog.rearrange.nosort=Nie sortuj
+dialog.rearrange.sortbyfilename=Sortuj po nazwie pliku
+dialog.rearrange.sortbyname=Sortuj po nazwie
+dialog.rearrange.sortbytime=Sortuj wed\u0142ug czasu
 dialog.compress.closepoints.title=Usuwanie bliskich sobie punkt\u00f3w
 dialog.compress.closepoints.paramdesc=Wsp\u00f3\u0142czynnik rozpi\u0119to\u015bci
 dialog.compress.wackypoints.title=Usuwanie dziwacznych punkt\u00f3w
@@ -509,6 +519,19 @@ dialog.colourchooser.title=Wybierz kolor
 dialog.colourchooser.red=Czerwony
 dialog.colourchooser.green=Zielony
 dialog.colourchooser.blue=Niebieski
+dialog.colourer.intro=Koloryzer punkt\u00f3w, zmienia kolor punkt\u00f3w \u015bcie\u017cki
+dialog.colourer.type=Tryb koloryzera
+dialog.colourer.type.none=\u017baden
+dialog.colourer.type.byfile=wed\u0142ug pliku
+dialog.colourer.type.bysegment=wed\u0142ug segmentu
+dialog.colourer.type.byaltitude=wed\u0142ug wysoko\u015bci
+dialog.colourer.type.byspeed=wed\u0142ug pr\u0119dko\u015bci
+dialog.colourer.type.byvertspeed=wed\u0142ug pr\u0119dko\u015bci pionowej
+dialog.colourer.type.bygradient=wed\u0142ug nachylenia
+dialog.colourer.type.bydate=wed\u0142ug daty
+dialog.colourer.start=Kolor pocz\u0105tkowy
+dialog.colourer.end=Kolor ko\u0144cowy
+dialog.colourer.maxcolours=Maksymalna liczba kolor\u00f3w
 dialog.setlanguage.firstintro=Mo\u017cesz wybra\u0107 jeden z do\u0142\u0105czonych j\u0119zyk\u00f3w<p>Albo wybra\u0107 wybrany przez siebie plik tekstowy.
 dialog.setlanguage.secondintro=B\u0119dziesz musia\u0142 zapisa\u0107 ustawienia<p>i zrestartowa\u0107 GpsPrune by zmieni\u0107 j\u0119zyk.
 dialog.setlanguage.language=J\u0119zyk
@@ -536,16 +559,35 @@ dialog.deletefieldvalues.nofields=Brak p\u00f3l do skasowania dla tego zakresu
 dialog.setlinewidth.text=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek
 dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM:
 dialog.searchwikipedianames.search=Szukaj
+dialog.weather.location=Pozycja
+dialog.weather.update=Prognoza zaktualizowana
+dialog.weather.sunrise=Wsch\u00f3d s\u0142o\u0144ca
+dialog.weather.sunset=Zach\u00f3d s\u0142o\u0144ca
+dialog.weather.temperatureunits=Jednostki temperatury
+dialog.weather.currentforecast=Bie\u017c\u0105ca pogoda
+dialog.weather.dailyforecast=Prognoza dobowa
+dialog.weather.3hourlyforecast=Prognoza na trzy godziny
 dialog.weather.day.now=Aktualny
 dialog.weather.day.today=Dzisiaj
 dialog.weather.day.tomorrow=Jutro
 dialog.weather.day.monday=Poniedzia\u0142ek
 dialog.weather.day.tuesday=Wtorek
-dialog.weather.day.wednesday=\u015Aroda
+dialog.weather.day.wednesday=\u015aroda
 dialog.weather.day.thursday=Czwartek
 dialog.weather.day.friday=Pi\u0105tek
 dialog.weather.day.saturday=Sobota
 dialog.weather.day.sunday=Niedziela
+dialog.weather.wind=Wiatr
+dialog.weather.temp=Temp
+dialog.weather.humidity=Wilgotno\u015b\u0107
+dialog.weather.creditnotice=Dane na podstawie openweathermap.org. Wi\u0119cej informacji na ich stronie.
+dialog.deletebydate.onlyonedate=Te punkty zosta\u0142y zarejestrowane w tym samym czasie.
+dialog.deletebydate.intro=Dla ka\u017cdej daty w \u015bcie\u017cce, mo\u017cesz wybra\u0107 czy usun\u0105\u0107 czy zostawi\u0107 punkty
+dialog.deletebydate.nodate=Brak znacznika czasu.
+dialog.deletebydate.column.keep=Zostaw
+dialog.deletebydate.column.delete=Usu\u0144
+dialog.setaltitudetolerance.text.metres=Limit (w metrach) poni\u017cej kt\u00f3rego, ma\u0142e spadki wzniosy b\u0119d\u0105 ignorowane
+dialog.setaltitudetolerance.text.feet=Limit (w stopach) poni\u017cej kt\u00f3rego, ma\u0142e spadki wzniosy b\u0119d\u0105 ignorowane
 
 # 3d window
 dialog.3d.title=GpsPrune widok tr\u00f3jwymiarowy
@@ -564,6 +606,8 @@ confirm.addtimeoffset=Dodano przesuni\u0119cie czasowe
 confirm.addaltitudeoffset=Dodano przesuni\u0119cie wysoko\u015bci
 confirm.rearrangewaypoints=Przestawiono punkty po\u015brednie
 confirm.rearrangephotos=Zmieniono kolejno\u015b\u0107 zdj\u0119\u0107
+confirm.splitsegments=
+confirm.sewsegments=Po\u0142\u0105czono %d fragmenty/\u00f3w
 confirm.cutandmove=Przesuni\u0119to zaznaczenie
 confirm.interpolate=Dodano punkty
 confirm.convertnamestotimes=Zmieniono nazwy punkt\u00f3w po\u015brednich
@@ -582,13 +626,20 @@ confirm.createpoint=stworzono punkt
 confirm.rotatephoto=obr\u00f3cono zdj\u0119cie
 confirm.running=Przetwarzam dane ...
 confirm.lookupsrtm=Znaleziono %d warto\u015bci wysoko\u015bci
+confirm.downloadsrtm=Pobrano %d plik\u00f3w do kesza
+confirm.downloadsrtm.1=Pobrano %d plik do kesza
+confirm.downloadsrtm.none=Nie pobrano \u017cadnych plik\u00f3w, wszystkie by\u0142y ju\u017c w keszu
 confirm.deletefieldvalues=Warto\u015bci p\u00f3l usuni\u0119to
 confirm.audioload=dodano pliki audio
 confirm.correlateaudios.single=audio zosta\u0142o po\u0142\u0105czone
 confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone
 
-# Tips
+# Tips, shown just once when appropriate
 tip.title=Porada
+tip.useamapcache=Konfiguruj\u0105c kesz dyskowy (Ustawienia -> Zapisz mapy na dysk)\nprzyspieszasz wy\u015bwietlanie i ograniczasz ruch sieciowy
+tip.learntimeparams=Resultat b\u0119dzie dok\u0142adniejszy je\u015bli u\u017cyjesz
+tip.downloadsrtm=Mo\u017cesz przyspieszy\u0107 operacj\u0119 wywo\u0142uj\u0105c polecenie
+tip.usesrtmfor3d=\u015acie\u017cka nie zawiera danych o wysoko\u015bciach\nMo\u017cesz u\u017cy\u0107 funkcji SRTM by pobrac przybli\u017cone dane\no wysko\u015bciach w trybie widoku 3D.
 tip.manuallycorrelateone=Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie.
 
 # Buttons
@@ -691,6 +742,7 @@ fieldname.longitude=D\u0142ugo\u015b\u0107
 fieldname.altitude=Wysoko\u015b\u0107
 fieldname.timestamp=Czas
 fieldname.time=Czas
+fieldname.date=Data
 fieldname.waypointname=Nazwa
 fieldname.waypointtype=Typ
 fieldname.newsegment=Odcinek
@@ -764,7 +816,9 @@ undo.croptrack=przytnij \u015bcie\u017ck\u0119
 undo.deletemarked=usu\u0144 punkty
 undo.insert=wstaw punkty
 undo.reverse=odwr\u00f3\u0107 zakres
-undo.mergetracksegments=po\u0142\u0105cz fragmenty \u015bcie\u017cki
+undo.mergetracksegments=scal fragmenty \u015bcie\u017cki
+undo.splitsegments=podziel \u015bcie\u017ck\u0119 na fragmenty
+undo.sewsegments=po\u0142\u0105cz fragmenty \u015bcie\u017cki
 undo.addtimeoffset=dodaj przesuni\u0119cie czasowe
 undo.addaltitudeoffset=dodaj przeuni\u0119cie wysoko\u015bci
 undo.rearrangewaypoints=przestaw punkty po\u015brednie
@@ -823,5 +877,7 @@ error.playaudiofailed=Nie powiod\u0142o si\u0119 odtwarzanie pliku audio
 error.cache.notthere=Nie znaleziono katalogu kesza
 error.cache.empty=Katalog kesza jest pusty
 error.cache.cannotdelete=\u017badne p\u0142ytki nie mog\u0142y zosta\u0107 usuni\u0119te
-error.interpolate.invalidparameter=Ilo\u015b\u0107 punkt\u00f3w musi zawiera\u0107 si\u0119 w zakresie od 1 do 1000
 error.learnestimationparams.failed=Oszacowanie wsp\u00f3\u0142czynnik\u00f3w dla danej scie\u017cki nie powiod\u0142o si\u0119.\nSpr\u00f3buj za\u0142adowa\u0107 wi\u0119cej \u015bcie\u017cek.
+error.tracksplit.nosplit=Nie mo\u017cna podzieli\u0107 \u015bcie\u017cki
+error.downloadsrtm.nocache=Nie mo\u017cna zapisa\u0107 plik\u00f3w\nSprawd\u017a ustawienia kesza
+error.sewsegments.nothingdone=Nie mo\u017cna po\u0142\u0105czy\u0107 fragment\u00f3w\nW \u015bcie\u017cce jest teraz %d fragment\u00f3w.
index 85870ad0faf16167cd24df9298d57b083c37187d..6319688bcabc95c9560ab35623de97d43084715c 100644 (file)
@@ -13,10 +13,7 @@ menu.track.undo=Desfazer
 menu.track.clearundo=Limpar lista de desfazer
 menu.track.markrectangle=Marcar pontos no ret\u00e2ngulo
 menu.track.deletemarked=Remover pontos marcados
-menu.track.rearrange=Rearrumar pontos
-menu.track.rearrange.start=Tudo para o in\u00edcio do arquivo
-menu.track.rearrange.end=Tudo para o fim do arquivo
-menu.track.rearrange.nearest=Cada um para o ponto da rota mais pr\u00f3ximo
+function.rearrangewaypoints=Rearrumar pontos
 menu.range=Intervalo
 menu.range.all=Selecionar tudo
 menu.range.none=Desmarcar todas as sele\u00e7\u00f5es
@@ -92,6 +89,7 @@ function.compress=Comprimir rota
 function.deleterange=Remover intervalo
 function.croptrack=Cortar rota
 function.interpolate=Interpolar pontos
+function.deletebydate=Remover pontos de acordo com a data
 function.addtimeoffset=Adicionar diferen\u00e7a de tempo
 function.addaltitudeoffset=Adicionar diferen\u00e7a de altitude
 function.convertnamestotimes=Converter nomes dos pontos para tempos
@@ -106,6 +104,7 @@ function.estimatetime=Tempo estimado
 function.learnestimationparams=Aprender os par\u00e2metros para estimativa de tempo
 function.setmapbg=Definir como fundo do mapa
 function.setpaths=Definir caminhos do programa
+function.selectsegment=Selecionar segmento atual
 function.splitsegments=Dividir rota em segmentos
 function.sewsegments=Reunir segmentos em rota
 function.getgpsies=Obter rotas Gpsies
@@ -413,11 +412,13 @@ dialog.correlate.audioselect.intro=Selecione um destes \u00e1udios correlacionad
 dialog.correlate.select.audioname=Nome do \u00e1udio
 dialog.correlate.select.audiolater=\u00c1udio posterior
 dialog.rearrangephotos.desc=Selecione o destino e a ordena\u00e7\u00e3o dos pontos das fotos
-dialog.rearrangephotos.tostart=Mover para o in\u00edcio
-dialog.rearrangephotos.toend=Mover para o fim
-dialog.rearrangephotos.nosort=N\u00e3o ordenar
-dialog.rearrangephotos.sortbyfilename=Ordenar pelo nome do arquivo
-dialog.rearrangephotos.sortbytime=Ordenar pela hora
+dialog.rearrange.tostart=Mover para o in\u00edcio
+dialog.rearrange.toend=Mover para o fim
+dialog.rearrange.tonearest=Cada um para o ponto da rota mais pr\u00f3ximo
+dialog.rearrange.nosort=N\u00e3o ordenar
+dialog.rearrange.sortbyfilename=Ordenar pelo nome do arquivo
+dialog.rearrange.sortbyname=Ordenar pelo nome
+dialog.rearrange.sortbytime=Ordenar pela hora
 dialog.compress.closepoints.title=Remo\u00e7\u00e3o de ponto pr\u00f3ximo
 dialog.compress.closepoints.paramdesc=Fator de deslocamento
 dialog.compress.wackypoints.title=Remo\u00e7\u00e3o de ponto exc\u00eantrica
@@ -852,7 +853,6 @@ error.playaudiofailed=Falha ao reproduzir arquivo de \u00e1udio
 error.cache.notthere=A paste de cache de fundos n\u00e3o foi encontrada
 error.cache.empty=A pasta de cache de fundos est\u00e1 vazia
 error.cache.cannotdelete=Nenhum fundo pode ser removido
-error.interpolate.invalidparameter=O n\u00famero de pontos deve estar entre 1 e 1000
 error.learnestimationparams.failed=N\u00e3o foi poss\u00edvel aprender par\u00e2metros desta rota.\nTente baixar mais rotas.
 error.tracksplit.nosplit=A rota n\u00e3o pode ser dividida.
 error.downloadsrtm.nocache=Os arquivos n\u00e3o puderam ser salvos.\nPor favor, verifique a cache do disco.
index b94b88c63b1eef426ef123c46803fd3824655b98..e734fb73036ceb50438ec78dd60f1af99b82c2f4 100644 (file)
@@ -6,24 +6,21 @@ menu.file=Fi\u015fier
 menu.file.addphotos=Adaug\u0103 foto
 menu.file.recentfiles=Fi\u015fiere recente
 menu.file.save=Salvare
-menu.file.exit=Iesire
+menu.file.exit=Ie\u015fire
+menu.online=Internet
 menu.track=Traseu
 menu.track.undo=Anulare
 menu.track.clearundo=\u015etergere lista de anulari
 menu.track.deletemarked=\u015etergere puncte marcate
-menu.track.rearrange=Rearanjare waypoint
-menu.track.rearrange.start=Toate la inceputul fi\u015fierului
-menu.track.rearrange.end=Toate la sfarsitul fi\u015fierului
-menu.track.rearrange.nearest=Fiecare la punctul cel mai apropiat al traseului
 menu.range=Interval
 menu.range.all=Selectare toate
 menu.range.none=Nu selecta niciun punct
 menu.range.start=Seteaza inceputul selectiei
 menu.range.end=Seteaza sfarsitul selectiei
-menu.range.average=Mediere selectie
-menu.range.reverse=Inversare selectie
+menu.range.average=Mediere selec\u0163ie
+menu.range.reverse=Inversare selec\u0163ie
 menu.range.mergetracksegments=Unire segmente traseu
-menu.range.cutandmove=Taiere si mutare selectie
+menu.range.cutandmove=Taiere si mutare selec\u0163ie
 menu.point=Punct
 menu.point.editpoint=Editare punct
 menu.point.deletepoint=\u015etergere punct
@@ -38,6 +35,7 @@ menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Harti Yahoo
 menu.view.browser.bing=Harti Bing
 menu.settings=Set\u0103ri
+menu.settings.onlinemode=\u00cencarc\u0103 h\u0103r\u021bi
 menu.help=Ajutor
 # Popup menu for map
 menu.map.zoomin=Apropie
@@ -53,6 +51,7 @@ menu.map.editmode=Mod de editare
 
 # Alt keys for menus
 altkey.menu.file=F
+altkey.menu.online=N
 altkey.menu.track=T
 altkey.menu.range=I
 altkey.menu.point=P
@@ -87,24 +86,37 @@ function.deleterange=\u015etergere gama
 function.interpolate=Interpolare
 function.addtimeoffset=Adaug\u0103 decalaj timp
 function.addaltitudeoffset=Adaug\u0103 decalaj altitudine
+function.rearrangewaypoints=Rearanjare waypoint
 function.findwaypoint=Gasire waypoint
 function.charts=Grafice
 function.show3d=Vizualizare arborescenta
 function.distances=Distan\u0163e
 function.fullrangedetails=Informa\u0163ie complet
-function.loadaudio=Adaug\u0103 audio
-function.setmapbg=Fundal
+function.getgpsies=\u00cencarc\u0103 trassee Gpsies
+function.uploadgpsies=Trimite date spre Gpsies
+function.downloadsrtm=\u00cencarc\u0103 date SRTM
+function.estimatetime=Estimare durat\u0103
+function.setmapbg=Seteaza harta
+function.selectsegment=Selectare segment curent
 function.setcolours=Selectare culorile
 function.setlanguage=Selectare limba
 function.connecttopoint=Conecteaza la punct
 function.disconnectfrompoint=Deconecteaza de la punct
 function.removephoto=Elimina foto
 function.correlatephotos=Corelare fotografii
+function.rearrangephotos=Rearanjare fotografii
+function.rotatephotoleft=Roti foto la st\u00e2nga
+function.rotatephotoright=Roti foto la dreapta
+function.photopopup=Arat\u0103 foto
+function.loadaudio=Adaug\u0103 audio
+function.removeaudio=Elimina audio
+function.playaudio=Redare audio
 function.help=Ajutor
 function.showkeys=Arat\u0103 tastele scurt\u0103turi
 function.about=Despre GpsPrune
 function.checkversion=Verific\u0103 pentru o versiune noua
 function.saveconfig=Salvare set\u0103ri
+function.diskcache=Salvare harti
 function.getweatherforecast=Prognoz\u0103 meteo
 
 # Dialogs
@@ -114,8 +126,9 @@ dialog.openappend.title=Adauga la datele existente
 dialog.openappend.text=Adauga la datele deja incarcate?
 dialog.deletepoint.title=\u015eterge Punct
 dialog.deletepoint.deletephoto=\u015eterg fotografiile atasate acestui punct?
-dialog.deletephoto.title=\u015eterge Foto
+dialog.deletephoto.title=\u015eterge foto
 dialog.deletephoto.deletepoint=\u015eterg punct atasat acestei fotografii?
+dialog.deleteaudio.deletepoint=\u015eterg punct atasat acestei audio?
 dialog.openoptions.title=Optiuni deschidere
 dialog.load.table.field=Cimp
 dialog.load.table.datatype=Tip data
@@ -129,30 +142,55 @@ dialog.delimiter.other=Alte
 dialog.openoptions.deliminfo.records=inregistrari, cu
 dialog.openoptions.deliminfo.fields=cimpuri
 dialog.openoptions.deliminfo.norecords=Nu sunt inregistrari
+dialog.openoptions.altitudeunits=Unit\u0103\u0163i de altitudini
+dialog.openoptions.speedunits=Unit\u0103\u0163i de viteza
+dialog.openoptions.vertspeedunits=Unit\u0103\u0163i de viteza vertical\u0103
 dialog.selecttracks.noname=F\u0103r\u0103 nume
 dialog.jpegload.subdirectories=Include subdirectori
 dialog.jpegload.loadjpegswithoutcoords=Include fotografii fara coordonate
 dialog.jpegload.loadjpegsoutsidearea=Include fotografii din afara zonei curente
-dialog.jpegload.progress.title=Incarcare fotografii
+dialog.jpegload.progress.title=\u00cenc\u0103rcare fotografii
 dialog.jpegload.progress=Va rog sa asteptati, caut fotografiile
 dialog.gpsload.nogpsbabel=Nu gasesc programul gpsbabel. Continui ?
 dialog.gpsload.device=Nume dispozitiv
 dialog.gpsload.format=Format
-dialog.gpsload.getwaypoints=Incarcare waypoints
+dialog.gpsload.getwaypoints=\u00cencarc\u0103 waypoints
+dialog.gpsload.gettracks=\u00cencarc\u0103 trasee
+dialog.gpsload.save=Salvare fi\u015fier
+dialog.gpssend.sendwaypoints=Trimite waypoints
+dialog.gpssend.sendtracks=Trimite trasee
 dialog.gpssend.trackname=Nume traseu
 dialog.gpsbabel.filters=Filtre
-dialog.gpsbabel.filter.simplify=Simplifica
+dialog.addfilter.title=Adaug\u0103 filtru
+dialog.gpsbabel.filter.discard=Arunc\u0103
+dialog.gpsbabel.filter.simplify=Simplific\u0103
 dialog.gpsbabel.filter.distance=Distan\u0163\u0103
+dialog.gpsbabel.filter.interpolate=Interpolare
+dialog.gpsbabel.filter.discard.intro=Arunc\u0103 puncte dac\u0103
+dialog.gpsbabel.filter.discard.hdop=Hdop >
+dialog.gpsbabel.filter.discard.vdop=Vdop >
 dialog.gpsbabel.filter.discard.numsats=Num\u0103r de sateli\u0163i <
+dialog.gpsbabel.filter.simplify.maxpoints=Num\u0103r de puncte <
+dialog.gpsbabel.filter.simplify.length=diferen\u0163\u0103 de lungime
+dialog.gpsbabel.filter.distance.distance=Dac\u0103 distan\u0163\u0103 <
+dialog.gpsbabel.filter.distance.time=\u0219i diferen\u0163\u0103 de timp <
+dialog.gpsbabel.filter.interpolate.distance=Dac\u0103 distan\u0163\u0103 >
+dialog.gpsbabel.filter.interpolate.time=sau diferen\u0163\u0103 de timp >
 dialog.saveoptions.title=Salvare fi\u015fier
 dialog.save.table.field=Cimp
+dialog.save.table.hasdata=Date
 dialog.save.table.save=Salvare
+dialog.save.coordinateunits=Format coordonate
+dialog.save.altitudeunits=Unit\u0103\u0163i de altitudini
+dialog.save.timestampformat=Format de timp
 dialog.save.overwrite.title=Fi\u015fierul exist\u0103
 dialog.save.overwrite.text=Fi\u015fierul exist\u0103. \u00cel suprascriu?
 dialog.exportkml.text=Titlu
+dialog.exportkml.imagesize=Dimensiune imaginii
 dialog.exportkml.trackcolour=Culoarea liniei
 dialog.exportgpx.name=Nume
 dialog.exportgpx.desc=Descriere
+dialog.exportgpx.encoding=Codare
 dialog.exportgpx.encoding.system=Sistem
 dialog.exportgpx.encoding.utf8=UTF-8
 dialog.exportpov.font=Fontul
@@ -164,19 +202,30 @@ dialog.3d.useterrain=Arat\u0103 teren
 dialog.3d.terraingridsize=Dimensiune a grilei
 dialog.exportpov.baseimage=Imagine cartografice
 dialog.baseimage.title=Imagine cartografice
+dialog.baseimage.zoom=Nivel de zoom
+dialog.baseimage.incomplete=Imagine incomplet\u0103
 dialog.baseimage.tiles=Tigla
-dialog.exportsvg.phi=Azimut \u03D5
-dialog.exportsvg.theta=\u00cenclina\u0163ie \u03B8
+dialog.exportsvg.phi=Azimut \u03d5
+dialog.exportsvg.theta=\u00cenclina\u0163ie \u03b8
+dialog.pointtype.track=Puncte de traseu
+dialog.pointtype.waypoint=Waypoints
+dialog.pointtype.photo=Puncte foto
+dialog.pointtype.audio=Puncte audio
+dialog.pointtype.selection=Doar interval
 dialog.undo.title=Anulare
+dialog.pointedit.title=Editare punct
 dialog.pointedit.intro=V\u0103 rog selecta\u0163i r\u00e2ndul care va fi editat
 dialog.pointedit.table.field=Cimp
 dialog.pointedit.table.value=Valoare
+dialog.pointnameedit.name=Nume de waypoint
 dialog.pointnameedit.uppercase=Litere MARI
 dialog.pointnameedit.lowercase=Litere mici
 dialog.addtimeoffset.days=Zile
 dialog.addtimeoffset.hours=Ore
 dialog.addtimeoffset.minutes=Minute
 dialog.findwaypoint.search=C\u0103utare
+dialog.saveexif.table.photoname=Nume
+dialog.saveexif.title=Salvare Exif
 dialog.saveexif.table.status=Stare
 dialog.saveexif.table.save=Salveaza
 dialog.saveexif.photostatus.connected=Conectat
@@ -185,21 +234,64 @@ dialog.saveexif.photostatus.modified=Modificat
 dialog.saveexif.overwrite=Suprascrie fi\u015fiere
 dialog.charts.xaxis=Axa X
 dialog.charts.yaxis=Axa Y
+dialog.charts.output=Rezultat
+dialog.charts.svgwidth=L\u0103\u021bime SVG
+dialog.charts.svgheight=\u00cen\u0103l\u021bime SVG
+dialog.distances.column.from=De punct
+dialog.distances.column.to=Spre punct
 dialog.distances.currentpoint=Punct curent
+dialog.estimatetime.details=Detalii
+dialog.estimatetime.parameters=Parametrii
+dialog.estimatetime.parameters.timefor=Durata pentru
+dialog.estimatetime.results=Rezultate
+dialog.estimatetime.results.estimatedtime=Durata estimat\u0103
+dialog.estimatetime.results.actualtime=Durata (measured)
+dialog.learnestimationparams.averageerror=Eroare estimat
+dialog.learnestimationparams.combinedresults=Rezultate combinat
+dialog.learnestimationparams.weight.current=curente
+dialog.learnestimationparams.weight.calculated=calculate
+dialog.addmapsource.sourcename=Nume
 dialog.addmapsource.noname=F\u0103r\u0103 nume
 dialog.gpsies.column.name=Nume
 dialog.gpsies.column.length=Lungime
 dialog.gpsies.description=Descriere
 dialog.gpsies.nodescription=F\u0103r\u0103 descriere
+dialog.gpsies.nonefound=Nu a fost g\u0103sit
+dialog.gpsies.username=Gpsies username
+dialog.gpsies.password=Gpsies parol\u0103
+dialog.gpsies.keepprivate=Traseu privat
+dialog.gpsies.activities=Activit\u0103\u0163i
 dialog.wikipedia.column.name=Nume
 dialog.wikipedia.column.distance=Distan\u0163\u0103
+dialog.wikipedia.nonefound=Nu a fost g\u0103sit
+dialog.correlate.select.photoname=Nume
+dialog.correlate.select.timediff=Diferenta de timp
 dialog.correlate.options.offset.hours=ore,
 dialog.correlate.options.offset.minutes=minute,
 dialog.correlate.options.offset.seconds=secunde
+dialog.correlate.options.correlate=Corelare
+dialog.correlate.timestamp.beginning=\u00cenceptutul
+dialog.correlate.timestamp.middle=Mijlocul
+dialog.correlate.timestamp.end=Sf\u00e2r\u015fitul
+dialog.correlate.select.audioname=Nume
+dialog.rearrange.tostart=Toate la inceputul fi\u015fierului
+dialog.rearrange.toend=Toate la sfarsitul fi\u015fierului
+dialog.rearrange.tonearest=Fiecare la punctul cel mai apropiat al traseului
+dialog.rearrange.nosort=Nu sunt sortate
+dialog.rearrange.sortbyfilename=Sorta dup\u0103 nume de fi\u015fier
+dialog.rearrange.sortbyname=Sorta dup\u0103 nume
+dialog.rearrange.sortbytime=Sorta dup\u0103 timp
 dialog.pastecoordinates.coords=Coordonate
 dialog.about.version=Versiunea
+dialog.about.build=Construi
+dialog.about.languages=Limbi
 dialog.about.systeminfo=Informa\u0163ii a sistemului
 dialog.about.systeminfo.os=Sistem de operare
+dialog.about.systeminfo.java3d=Java3d instalat
+dialog.about.systeminfo.povray=Povray instalat
+dialog.about.systeminfo.exiftool=Exiftool instalat
+dialog.about.systeminfo.gpsbabel=Gpsbabel instalat
+dialog.about.systeminfo.gnuplot=Gnuplot instalat
 dialog.about.systeminfo.exiflib=Bibliotec\u0103 Exif
 dialog.about.systeminfo.exiflib.internal=Intern
 dialog.about.systeminfo.exiflib.internal.failed=Intern (absent)
@@ -210,15 +302,31 @@ dialog.about.no=Nu
 dialog.about.readme=Cite\u015fte-m\u0103
 dialog.checkversion.releasedate1=Aceasta versiune noua a fost lansapa pe
 dialog.checkversion.releasedate2=.
+dialog.saveconfig.prune.languagecode=Limb\u0103 (RO)
+dialog.saveconfig.prune.languagefile=Fi\u015fier de limba
+dialog.saveconfig.prune.gpsdevice=Dispozitiv GPS
+dialog.saveconfig.prune.gpsformat=Format GPS
 dialog.setcolours.background=Fund
 dialog.setcolours.lines=Linii
 dialog.setcolours.primary=Primar
 dialog.setcolours.secondary=Secundar
 dialog.setcolours.point=Puncte
+dialog.setcolours.selection=Selec\u0163ie
 dialog.setcolours.text=Text
+dialog.colourchooser.title=Selectare culoare
 dialog.colourchooser.red=Ro\u0219u
 dialog.colourchooser.green=Verde
 dialog.colourchooser.blue=Albastru
+dialog.colourer.type.none=Nimic
+dialog.setlanguage.language=Limb\u0103
+dialog.setlanguage.languagefile=Fi\u015fier de limba
+dialog.diskcache.table.tiles=Tigla
+dialog.searchwikipedianames.search=C\u0103utare :
+dialog.weather.location=Loca\u0163ie
+dialog.weather.sunrise=R\u0103s\u0103rit
+dialog.weather.sunset=Apus de soare
+dialog.weather.currentforecast=Vremea curent\u0103
+dialog.weather.day.now=Vremea curent\u0103
 dialog.weather.day.today=Ast\u0103zi
 dialog.weather.day.tomorrow=M\u00e2ine
 dialog.weather.day.monday=Luni
@@ -233,6 +341,13 @@ dialog.weather.day.sunday=Duminic\u0103
 confirm.loadfile=Date incarcate din fi\u015fier
 confirm.save.ok1=Salvat cu succes
 confirm.save.ok2=puncte \u00een
+confirm.media.connect=foto/audio conectat
+confirm.photo.disconnect=foto deconectat
+confirm.audio.disconnect=audio deconectat
+confirm.media.removed=\u0219ters
+confirm.running=Executare ...
+confirm.downloadsrtm=S-au desc\u0103rcat %d fi\u015fiere
+confirm.downloadsrtm.1=S-au desc\u0103rcat %d fi\u015fier
 
 # Tips
 tip.title=Indiciu
@@ -258,7 +373,7 @@ button.select=Selectare
 button.selectall=Selecteaza tot
 button.selectnone=Deselecteaza tot
 button.load=Descarca
-button.upload=Inc\u0103rca
+button.upload=Trimite
 button.guessfields=Ghici cimpuri
 button.check=Verifica
 button.delete=\u015etergere
@@ -281,27 +396,43 @@ filetype.audio=Fi\u015fiere MP3, OGG, WAV
 details.trackdetails=Detalii traseul
 details.track.points=Puncte
 details.pointdetails=Detalii punctul
+details.index.selected=Punct
+details.index.of=de
+details.photofile=Fi\u015fier
 details.rangedetails=Detalii intervalul
 details.range.selected=Selectat
 details.range.to=la
 details.altitude.to=la
+details.range.climb=Urcare
+details.range.descent=Cobor\u00e2re
 details.coordformat=Format coordonate
-details.distanceunits=Unitati de distan\u0163e
+details.distanceunits=Unit\u0103\u0163i de distan\u0163e
 display.range.time.secs=s
 display.range.time.mins=m
 display.range.time.hours=o
 display.range.time.days=z
 details.range.avespeed=Viteza medie
 details.range.maxspeed=Viteza maxim\u0103
+details.range.numsegments=Num\u0103r de segmente
+details.range.pace=Ritm
+details.range.gradient=Gradient
+details.lists.waypoints=Waypoints
 details.lists.photos=Foto-uri
 details.lists.audio=Audio
+details.photodetails=Detalii foto
+details.photo.loading=\u00cenc\u0103rcare
+details.photo.bearing=Direc\u0163ie
+details.media.connected=Conectat
 details.audiodetails=Detalii audio
+details.audio.file=Fi\u015fier
 
 # Field names
 fieldname.latitude=Latitudine
 fieldname.longitude=Longitudine
 fieldname.altitude=Altitudine
+fieldname.timestamp=Timp
 fieldname.time=Timp
+fieldname.date=Data
 fieldname.waypointname=Nume
 fieldname.waypointtype=Tip
 fieldname.newsegment=Segment
@@ -322,22 +453,52 @@ units.kilometresperhour=km pe or\u0103
 units.kilometresperhour.short=km/o
 units.miles=Mil\u0103
 units.miles.short=mi
+units.milesperhour=mil\u0103 pe or\u0103
+units.milesperhour.short=mpo
 units.nauticalmiles=Mil\u0103 marin\u0103
 units.nauticalmiles.short=mm
 units.nauticalmilesperhour.short=kn
+units.metrespersec=metri pe secund
+units.metrespersec.short=m/s
 units.hours=ore
 units.minutes=minute
 units.seconds=secunde
+units.degminsec=Grad-min-sec
+units.degmin=Grad-min
+units.deg=Grad
+units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
 
 # How to combine conditions, such as filters
-logic.and=\ufeff\u0219i
+logic.and=\u0219i
 logic.or=sau
 
+# External urls
+wikipedia.lang=ro
+openweathermap.lang=ro
+
 # Cardinals for 3d plots
 cardinal.n=N
 cardinal.s=S
 cardinal.e=E
 cardinal.w=V
 
-wikipedia.lang=ro
-openweathermap.lang=ro
+# Undo operations
+undo.load=\u00cencarc\u0103 date
+undo.loadphotos=\u00cencarc\u0103 fotografii
+undo.loadaudios=\u00cencarc\u0103 audio
+undo.editpoint=Editare punct
+undo.deletepoint=\u015eterge punct
+undo.removephoto=Elimina foto
+undo.removeaudio=Elimina audio
+undo.deleterange=\u015eterge interval
+undo.deletemarked=\u015eterge puncte
+undo.connect=conecteaza
+undo.disconnect=deconecteaza
+undo.rotatephoto=roti foto
+
+# Error messages
+error.function.notavailable.title=Func\u021bie indisponibil\u0103
\ No newline at end of file
index 3481dba779666bd1f6f35496285ff2e3a40e6e0d..ec306381c093d9748411080d8419bf80fb9d395c 100644 (file)
@@ -13,10 +13,7 @@ menu.track.undo=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c
 menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439
 menu.track.markrectangle=\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 \u0432 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0435
 menu.track.deletemarked=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438
-menu.track.rearrange=\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u044b
-menu.track.rearrange.start=\u0412\u0441\u0435 \u0432 \u043d\u0430\u0447\u0430\u043b\u043e \u0444\u0430\u0439\u043b\u0430
-menu.track.rearrange.end=\u0412\u0441\u0435 \u0432 \u043a\u043e\u043d\u0435\u0446 \u0444\u0430\u0439\u043b\u0430
-menu.track.rearrange.nearest=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u043a \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0439
+function.rearrangewaypoints=\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u044b
 menu.range=\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b
 menu.range.all=\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435
 menu.range.none=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u044b\u0431\u043e\u0440\u043a\u0443
@@ -385,11 +382,12 @@ dialog.correlate.audioselect.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0
 dialog.correlate.select.audioname=\u0418\u043c\u044f \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438
 dialog.correlate.select.audiolater=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u0437\u0434\u043d\u0435\u0435
 dialog.rearrangephotos.desc=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438 \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u0444\u043e\u0442\u043e
-dialog.rearrangephotos.tostart=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043d\u0430\u0447\u0430\u043b\u043e
-dialog.rearrangephotos.toend=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0435\u0446
-dialog.rearrangephotos.nosort=\u041d\u0435 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c
-dialog.rearrangephotos.sortbyfilename=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0444\u0430\u0439\u043b\u0430
-dialog.rearrangephotos.sortbytime=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438
+dialog.rearrange.tostart=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043d\u0430\u0447\u0430\u043b\u043e
+dialog.rearrange.toend=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0435\u0446
+dialog.rearrange.tonearest=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u043a \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0439
+dialog.rearrange.nosort=\u041d\u0435 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c
+dialog.rearrange.sortbyfilename=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0444\u0430\u0439\u043b\u0430
+dialog.rearrange.sortbytime=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438
 dialog.compress.closepoints.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0431\u043b\u0438\u0436\u0435\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a
 dialog.compress.closepoints.paramdesc=\u0420\u0430\u0437\u043c\u0430\u0445
 dialog.compress.wackypoints.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 "\u0448\u0430\u043b\u044c\u043d\u044b\u0445"(\u043d\u0435\u043e\u0431\u044b\u0447\u043d\u044b\u0445) \u0442\u043e\u0447\u0435\u043a
@@ -794,7 +792,7 @@ error.load.noxmlinzip=\u0412 zip-\u0430\u0440\u0445\u0438\u0432\u0435 \u043d\u04
 error.load.othererror=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438 \u0444\u0430\u0439\u043b\u0430:
 error.jpegload.dialogtitle=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0444\u043e\u0442\u043e
 error.jpegload.nofilesfound=\u0424\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
-error.jpegload.nojpegsfound=JEPG-\u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
+error.jpegload.nojpegsfound=JPEG-\u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
 error.jpegload.nogpsfound=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 GPS-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f
 error.jpegload.exifreadfailed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c Exif-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u041d\u0435\u0442 Exif-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u200b\u200b\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c\n\u0431\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u043a\u0438\u0445-\u043b\u0438\u0431\u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0445 \u0438\u043b\u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a.
 error.audioload.nofilesfound=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
@@ -820,5 +818,4 @@ error.playaudiofailed=\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u043e\u0441\u0
 error.cache.notthere=\u041f\u0430\u043f\u043a\u0430 \u043a\u044d\u0448\u0430 \u0441 \u0442\u0430\u0439\u043b\u0430\u043c\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430
 error.cache.empty=\u041f\u0430\u043f\u043a\u0430 \u043a\u044d\u0448\u0430 \u0441 \u0442\u0430\u0439\u043b\u0430\u043c\u0438 \u043f\u0443\u0441\u0442\u0430
 error.cache.cannotdelete=\u041d\u0435\u0442 \u0442\u0430\u0439\u043b\u043e\u0432, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f
-error.interpolate.invalidparameter=\u041d\u043e\u043c\u0435\u0440 \u0442\u043e\u0447\u043a\u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0442 1 \u0434\u043e 1000
 error.tracksplit.nosplit=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u0440\u0435\u043a
index 7f421c8d4070689bf0f93de6eebd01dcd1e751e3..ecde4b72fe5147e657cabc13187a2b7294ba7425 100644 (file)
@@ -12,10 +12,10 @@ menu.track.undo=\u00c5ngra
 menu.track.clearundo=Rensa \u00e5ngra-historik
 menu.track.markrectangle=Markera punkter i rektangel
 menu.track.deletemarked=Radera markerade punkter
-menu.track.rearrange=Ordna waypoints
-menu.track.rearrange.start=Alla till b\u00f6rjan av fil
-menu.track.rearrange.end=Alla till slut av fil
-menu.track.rearrange.nearest=Varje till n\u00e4rmaste sp\u00e5rpunkt
+function.rearrangewaypoints=Ordna waypoints
+dialog.rearrange.tostart=Alla till b\u00f6rjan av fil
+dialog.rearrange.toend=Alla till slut av fil
+dialog.rearrange.tonearest=Varje till n\u00e4rmaste sp\u00e5rpunkt
 menu.range=Intervall
 menu.range.all=V\u00e4lj alla
 menu.range.none=V\u00e4lj ingen
index 4baf0dec79adc56ea46040281094a9422585f01a..d236ff39e3b0db5b7ed7c3edf9d377e50a16d52b 100644 (file)
@@ -17,10 +17,10 @@ function.interpolate=\u0130nterpolasyon
 menu.range.average=Se\u00e7me ortala
 menu.range.reverse=S\u0131ra tersine \u00e7evir
 menu.range.mergetracksegments=\u0130z par\u00e7alar\u0131 birle\u015ftir
-menu.track.rearrange=Yol noktalar\u0131 yeniden diz
-menu.track.rearrange.start=Hepsini dosyan\u0131n ba\u015f\u0131na
-menu.track.rearrange.end=Hepsini dosyan\u0131n sonuna
-menu.track.rearrange.nearest=En yak\u0131n iz noktaya
+function.rearrangewaypoints=Yol noktalar\u0131 yeniden diz
+dialog.rearrange.tostart=Hepsini dosyan\u0131n ba\u015f\u0131na
+dialog.rearrange.toend=Hepsini dosyan\u0131n sonuna
+dialog.rearrange.tonearest=En yak\u0131n iz noktaya
 menu.range.cutandmove=Se\u00e7me kes ve ta\u015f\u0131
 menu.range=S\u0131ra
 menu.point=Nokta
index 6f74d55d017cfd26f789839bba0fa1d94049ccea..3b47eeaa310eebf75288f17d90b9f8baab17e0bf 100644 (file)
@@ -12,10 +12,10 @@ menu.track.undo=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438
 menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043c\u0456\u043d
 menu.track.markrectangle=\u041f\u043e\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u0443 \u043f\u0440\u044f\u043c\u043e\u043a\u0443\u0442\u043d\u0438\u043a\u0443
 menu.track.deletemarked=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u043f\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u0456 \u0442\u043e\u0447\u043a\u0438
-menu.track.rearrange=\u041f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0438
-menu.track.rearrange.start=\u0423\u0441\u0435 \u043d\u0430 \u043f\u043e\u0447\u0430\u0442\u043e\u043a \u0444\u0430\u0439\u043b\u0443
-menu.track.rearrange.end=\u0423\u0441\u0435 \u043d\u0430 \u043a\u0456\u043d\u0435\u0446\u044c \u0444\u0430\u0439\u043b\u0443
-menu.track.rearrange.nearest=\u041f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u0434\u043e \u043d\u0430\u0439\u0431\u043b\u0438\u0436\u0447\u043e\u0457
+function.rearrangewaypoints=\u041f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0438
+dialog.rearrange.tostart=\u0423\u0441\u0435 \u043d\u0430 \u043f\u043e\u0447\u0430\u0442\u043e\u043a \u0444\u0430\u0439\u043b\u0443
+dialog.rearrange.toend=\u0423\u0441\u0435 \u043d\u0430 \u043a\u0456\u043d\u0435\u0446\u044c \u0444\u0430\u0439\u043b\u0443
+dialog.rearrange.tonearest=\u041f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u0434\u043e \u043d\u0430\u0439\u0431\u043b\u0438\u0436\u0447\u043e\u0457
 menu.range=\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b
 menu.range.all=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u0443\u0441\u0456
 menu.range.none=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438 \u0432\u0438\u0431\u0456\u0440\u043a\u0443
index 1fcca74ebd5a4ff88cb9c1871227b712846a3907..40319fa8ea93367e8c24a3e42cf3aeff11422bd7 100644 (file)
@@ -13,10 +13,6 @@ menu.track.undo=\u64a4\u9500
 menu.track.clearundo=\u6e05\u9664\u64a4\u9500\u6e05\u5355
 menu.track.markrectangle=\u6807\u8bb0\u9009\u53d6\u533a\u57df\u5185\u7684\u70b9
 menu.track.deletemarked=\u5220\u9664\u5df2\u6807\u8bb0\u8f68\u8ff9\u70b9
-menu.track.rearrange=\u91cd\u65b0\u6392\u5217\u822a\u70b9
-menu.track.rearrange.start=\u81f3\u8d77\u59cb\u4f4d\u7f6e
-menu.track.rearrange.end=\u81f3\u672b\u4f4d\u7f6e
-menu.track.rearrange.nearest=\u81f3\u6700\u8fd1\u8f68\u8ff9\u70b9
 menu.range=\u822a\u6bb5
 menu.range.all=\u5168\u9009
 menu.range.none=\u64a4\u9500\u9009\u62e9
@@ -92,8 +88,10 @@ function.compress=\u538b\u7f29\u8f68\u8ff9(\u6807\u8bb0\u8981\u5220\u9664\u822a\
 function.deleterange=\u5220\u9664\u8f68\u8ff9\u70b9\u6bb5
 function.croptrack=\u4fee\u526a\u8f68\u8ff9
 function.interpolate=\u91cd\u53e0\u8f68\u8ff9\u70b9
+function.deletebydate=\u6839\u636e\u65e5\u671f\u5220\u9664\u8f68\u8ff9\u70b9
 function.addtimeoffset=\u52a0\u5165\u65f6\u95f4\u5dee
 function.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb
+function.rearrangewaypoints=\u91cd\u65b0\u6392\u5217\u822a\u70b9
 function.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4
 function.deletefieldvalues=\u5220\u9664\u5b57\u6bb5\u503c
 function.findwaypoint=\u67e5\u627e\u822a\u70b9
@@ -106,6 +104,7 @@ function.estimatetime=\u4f30\u8ba1\u65f6\u95f4
 function.learnestimationparams=\u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4
 function.setmapbg=\u80cc\u666f\u5730\u56fe
 function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84
+function.selectsegment=\u9009\u4e2d\u5f53\u524d\u8f68\u8ff9\u6bb5
 function.splitsegments=\u5206\u5272\u8f68\u8ff9
 function.sewsegments=\u63a5\u5408\u8f68\u8ff9\u7247\u6bb5
 function.getgpsies=\u83b7\u53d6Gpsies\u8f68\u8ff9
@@ -141,6 +140,7 @@ function.saveconfig=\u4fdd\u5b58\u8bbe\u7f6e
 function.diskcache=\u4fdd\u5b58\u5730\u56fe
 function.managetilecache=\u7ba1\u7406\u5730\u56fe\u533a\u57df\u6570\u636e\u7f13\u5b58
 function.getweatherforecast=\u83b7\u53d6\u5929\u6c14\u9884\u62a5
+function.setaltitudetolerance=\u8bbe\u7f6e\u9ad8\u5ea6\u516c\u5dee
 
 # Dialogs
 dialog.exit.confirm.title=\u9000\u51fa
@@ -379,6 +379,7 @@ dialog.gpsies.activity.sailing=\u5e06\u8239
 dialog.gpsies.activity.skating=\u6ed1\u51b0
 dialog.wikipedia.column.name=\u6587\u7ae0\u9898\u76ee
 dialog.wikipedia.column.distance=\u8ddd\u79bb
+dialog.wikipedia.nonefound=\u672a\u627e\u5230\u7ef4\u57fa\u767e\u79d1\u6761\u76ee
 dialog.correlate.notimestamps=\u6570\u636e\u70b9\u4e2d\u65e0\u65f6\u95f4\u4fe1\u606f\uff0c\u7167\u7247\u65e0\u6cd5\u94fe\u63a5
 dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u7167\u7247\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f
 dialog.correlate.nouncorrelatedaudios=\u6240\u6709\u97f3\u9891\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f
@@ -412,12 +413,15 @@ dialog.correlate.timestamp.end=\u7ed3\u675f
 dialog.correlate.audioselect.intro=\u9009\u62e9\u4ee5\u4e0b\u58f0\u97f3\u6587\u4ef6\u4f5c\u4e3a\u65f6\u95f4\u504f\u5dee
 dialog.correlate.select.audioname=\u58f0\u97f3\u6587\u4ef6\u540d\u5b57
 dialog.correlate.select.audiolater=\u58f0\u97f3\u5ef6\u8fdf
+dialog.rearrangewaypoints.desc=\u9009\u62e9\u76ee\u7684\u5730\u5e76\u6392\u5217\u8def\u70b9
 dialog.rearrangephotos.desc=\u9009\u62e9\u76ee\u7684\u5730\u53ca\u7167\u7247\u70b9\u6392\u5217\u987a\u5e8f
-dialog.rearrangephotos.tostart=\u79fb\u5230\u5f00\u59cb
-dialog.rearrangephotos.toend=\u79fb\u5230\u672b\u5c3e
-dialog.rearrangephotos.nosort=\u4e0d\u6392\u5e8f
-dialog.rearrangephotos.sortbyfilename=\u6309\u540d\u79f0\u6392\u5e8f
-dialog.rearrangephotos.sortbytime=\u6309\u65f6\u95f4\u6392\u5e8f
+dialog.rearrange.tostart=\u79fb\u5230\u5f00\u59cb
+dialog.rearrange.toend=\u79fb\u5230\u672b\u5c3e
+dialog.rearrange.tonearest=\u81f3\u6700\u8fd1\u8f68\u8ff9\u70b9
+dialog.rearrange.nosort=\u4e0d\u6392\u5e8f
+dialog.rearrange.sortbyfilename=\u6309\u6587\u4ef6\u540d\u6392\u5e8f
+dialog.rearrange.sortbyname=\u6309\u8def\u70b9\u540d\u6392\u5e8f
+dialog.rearrange.sortbytime=\u6309\u65f6\u95f4\u6392\u5e8f
 dialog.compress.closepoints.title=\u90bb\u8fd1\u70b9\u5220\u9664
 dialog.compress.closepoints.paramdesc=\u8303\u56f4\u7cfb\u6570
 dialog.compress.wackypoints.title=\u5f02\u5e38\u70b9\u5220\u9664
@@ -515,6 +519,19 @@ dialog.colourchooser.title=\u8bf7\u9009\u62e9\u989c\u8272
 dialog.colourchooser.red=\u7ea2
 dialog.colourchooser.green=\u7eff
 dialog.colourchooser.blue=\u84dd
+dialog.colourer.intro=\u53ef\u4ee5\u8d4b\u4e88\u8f68\u8ff9\u70b9\u4e0d\u540c\u7684\u989c\u8272
+dialog.colourer.type=\u7740\u8272\u6a21\u5f0f
+dialog.colourer.type.none=\u65e0
+dialog.colourer.type.byfile=\u6309\u6587\u4ef6
+dialog.colourer.type.bysegment=\u6309\u8f68\u8ff9\u6bb5
+dialog.colourer.type.byaltitude=\u6309\u9ad8\u5ea6
+dialog.colourer.type.byspeed=\u6309\u901f\u5ea6
+dialog.colourer.type.byvertspeed=\u6309\u5782\u76f4\u901f\u5ea6
+dialog.colourer.type.bygradient=\u6309\u5761\u5ea6
+dialog.colourer.type.bydate=\u6309\u65e5\u671f
+dialog.colourer.start=\u8d77\u59cb\u989c\u8272
+dialog.colourer.end=\u7ed3\u675f\u989c\u8272
+dialog.colourer.maxcolours=\u6700\u5927\u989c\u8272\u6570\u91cf
 dialog.setlanguage.firstintro=\u4f60\u53ef\u4ee5\u9009\u62e9\u5df2\u6709\u8bed\u8a00,<p>\u6216\u9009\u62e9\u5916\u6302\u8bed\u8a00\u5305
 dialog.setlanguage.secondintro=\u8bf7\u4fdd\u5b58\u8bbe\u7f6e<p>\u5e76\u91cd\u542fGpsPrune\u4f7f\u8bbe\u7f6e\u751f\u6548
 dialog.setlanguage.language=\u8bed\u8a00
@@ -560,7 +577,17 @@ dialog.weather.day.thursday=\u5468\u56db
 dialog.weather.day.friday=\u5468\u4e94
 dialog.weather.day.saturday=\u5468\u516d
 dialog.weather.day.sunday=\u5468\u65e5
+dialog.weather.wind=\u98ce\u529b
+dialog.weather.temp=\u6e29\u5ea6
+dialog.weather.humidity=\u6e7f\u5ea6
 dialog.weather.creditnotice=\u5929\u6c14\u4fe1\u606f\u83b7\u53d6\u81eaopenweathermap.org\uff0c\u83b7\u53d6\u66f4\u591a\u5929\u6c14\u8be6\u60c5\u8bf7\u8bbf\u95ee\u7f51\u7ad9\u3002
+dialog.deletebydate.onlyonedate=\u6240\u6709\u8f68\u8ff9\u70b9\u90fd\u662f\u540c\u4e00\u5929\u7684
+dialog.deletebydate.intro=\u4f60\u53ef\u4ee5\u9009\u62e9\u4fdd\u7559\u6216\u5220\u9664\u67d0\u4e00\u5929\u7684\u8f68\u8ff9\u70b9
+dialog.deletebydate.nodate=\u6ca1\u6709\u65f6\u95f4\u6233
+dialog.deletebydate.column.keep=\u4fdd\u7559
+dialog.deletebydate.column.delete=\u5220\u9664
+dialog.setaltitudetolerance.text.metres=\u4e0d\u8d85\u8fc7\u6b64\u6570\u503c(\u7c73)\u7684\u9ad8\u5ea6\u53d8\u5316\u5c06\u88ab\u5ffd\u7565
+dialog.setaltitudetolerance.text.feet=
 
 # 3d window
 dialog.3d.title=GpsPrune 3D \u663e\u793a
@@ -715,6 +742,7 @@ fieldname.longitude=\u7ecf\u5ea6
 fieldname.altitude=\u9ad8\u5ea6
 fieldname.timestamp=\u65f6\u95f4
 fieldname.time=\u65f6\u95f4
+fieldname.date=\u65e5\u671f
 fieldname.waypointname=\u540d\u79f0
 fieldname.waypointtype=\u7c7b\u578b
 fieldname.newsegment=\u6bb5
@@ -849,7 +877,6 @@ error.playaudiofailed=\u65e0\u6cd5\u64ad\u653e\u58f0\u97f3\u6587\u4ef6
 error.cache.notthere=\u672a\u627e\u5230\u533a\u57df\u6570\u636e\u7f13\u5b58\u6587\u4ef6\u5939
 error.cache.empty=\u533a\u57df\u6570\u636e\u6587\u4ef6\u5939\u7a7a
 error.cache.cannotdelete=\u65e0\u53ef\u5220\u9664\u533a\u57df\u6570\u636e
-error.interpolate.invalidparameter=\u8f93\u5165\u70b9\u6570\u91cf\u5fc5\u987b\u57281\u52301000\u4e4b\u95f4
 error.learnestimationparams.failed=\u65e0\u6cd5\u4ece\u6b64\u8f68\u8ff9\u5f97\u5230\u53c2\u6570\n \u5c1d\u8bd5\u8f7d\u5165\u66f4\u591a\u8f68\u8ff9
 error.tracksplit.nosplit=\u6b64\u8f68\u8ff9\u65e0\u6cd5\u5206\u5272
 error.downloadsrtm.nocache=\u6587\u4ef6\u65e0\u6cd5\u4fdd\u5b58\n\u8bf7\u68c0\u67e5\u78c1\u76d8\u7f13\u5b58
index a177eaad9f77455efa2f4ba51ac66d3eb0ce3660..672cd02d3cfe74fb32e537eb173d5377f5d239cf 100644 (file)
@@ -258,7 +258,7 @@ public class JpegLoader implements Runnable, Cancellable
                // Apply timestamp to photo and its point (if any)
                photo.setTimestamp(timestamp);
                if (photo.getDataPoint() != null) {
-                       photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.FORMAT_ISO_8601), false);
+                       photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.Format.ISO8601), false);
                }
                return photo;
        }
index b9dd6c0ffbc995d063328c457c3e7abac65e3738..7b1c9c68eebf2dba7df83b97aa9bc16568b67066 100644 (file)
@@ -1,9 +1,9 @@
-GpsPrune version 16.3
-=====================
+GpsPrune version 17
+===================
 
 GpsPrune is an application for viewing, editing and managing coordinate data from GPS systems,
 including format conversion, charting and photo correlation.
-Full details can be found at http://activityworkshop.net/software/gpsprune/
+Full details can be found at http://gpsprune.activityworkshop.net/
 
 GpsPrune is copyright 2006-2014 activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
 You may freely use the software, and may help others to freely use it too.  For further information
@@ -17,7 +17,7 @@ Running
 =======
 
 To run GpsPrune from the jar file, simply call it from a command prompt or shell:
-   java -jar gpsprune_16.3.jar
+   java -jar gpsprune_17.jar
 
 If the jar file is saved in a different directory, you will need to include the path.
 Depending on your system settings, you may be able to click or double-click on the jar file
@@ -25,34 +25,19 @@ in a file manager window to execute it.  A shortcut, menu item, alias, desktop i
 or other link can of course be made should you wish.
 
 To specify a language other than the default, use an additional parameter, eg:
-   java -jar gpsprune_16.3.jar --lang=DE
-
-
-New with version 16.3
-=====================
-The following fixes were added since version 16.2:
-  - Fix for gpx caching of points which failed to load
-  - Additional newlines / tabs in gpx export
-  - API key for openweathermap.org
-  - Improvements to 3d terrain reflections
-  - Additional translations
-
-New with version 16.2
-=====================
-The following fixes were added since version 16.1:
-  - Fix for Gpx-slicing UTF8 files
-  - Conversion of sunrise/sunset times to local timezone
-  - Removal of Cloudmade maps
-  - Additional translations
+   java -jar gpsprune_17.jar --lang=DE
 
-New with version 16.1
-=====================
-The following fixes were added since version 16:
-  - Caching of terrain information for three-dimensional views
-  - Additional translations
-  - Improved void filling by interpolation
-  - Remembering file type of imported files
 
+New with version 17
+===================
+The following features were added since version 16:
+  - Colouring the track points according to various criteria (such as altitude,
+    speed, segment, file) in both the regular map view and the image export
+  - Marking points for deletion according to their date
+  - Select the current segment
+  - Adding an altitude tolerance to the climb and descent calculations
+  - Sorting waypoints by name or by timestamp
+  
 New with version 16
 ===================
 The following features were added since version 15:
@@ -64,11 +49,11 @@ The following features were added since version 15:
   - Function to download and save SRTM tiles
 
 New with version 15
-=====================
+===================
 The following features were added since version 14:
   - Extend povray output using map image on base plane
   - Export an image of the map and track at a selected zoom level
-  - Estimation of hiking times and learining of parameter values
+  - Estimation of hiking times and learning of parameter values
   - Allow altitude / speed profile to show any arbitrary field
   - Accept files dragged and dropped onto the GpsPrune window
   - Take account of timezone if present in track timestamps
@@ -78,7 +63,7 @@ The following features were added since version 14:
   - Allow loading of speeds and vertical speeds from text files
 
 New with version 14
-=====================
+===================
 The following features were added since version 13:
   - Dragging of existing points
   - Creation of new points by dragging the halfway point between two points
@@ -120,7 +105,6 @@ The following features were added since version 11:
 
 New with version 11
 ===================
-
 The following features were added since version 10:
   - Option to select which of the named tracks to load out of a gpx file or gps
   - Function to delete all values of a single field (eg all altitudes, all timestamps)
@@ -132,7 +116,6 @@ The following features were added since version 10:
 
 New with version 10
 ===================
-
 The following features were added since version 9:
   - Function to lookup altitudes using SRTM data from the Space Shuttle
   - Choice between altitude profile and speed profile in main view
@@ -144,7 +127,6 @@ The following features were added since version 9:
 
 New with version 9
 ==================
-
 The following features were added since version 8:
   - Ability to paste coordinates (eg from wikipedia or geocaching sites) to create new points
   - Configurable colour settings
@@ -158,7 +140,6 @@ The following features were added since version 8:
 
 New with version 8
 ==================
-
 The following features were added since version 7:
   - Loading of NMEA files (with suffix .nmea)
   - Loading of nearby tracks from gpsies.com
@@ -172,7 +153,6 @@ The following features were added since version 7:
 
 New with version 7
 ==================
-
 The following features were added since version 6:
   - Loading of KMZ files and zipped GPX
   - Improved compression functions with four configurable algorithms
@@ -184,7 +164,6 @@ The following features were added since version 6:
 
 New with version 6
 ==================
-
 The following features were added since version 5:
   - Map view using OpenStreetMap images is now integrated in the main window, with control for map transparency
   - Pov export has new option to use sphere sweeps for better appearance
@@ -197,8 +176,7 @@ The following features were added since version 5:
 
 New with version 5
 ==================
-
-The following features were added since version 4.1:
+The following features were added since version 4:
   - New map window in the View menu, showing points overlaid on OpenStreetMap images
   - New function to launch a browser showing the area in either Google Maps or OpenStreetMap
   - Handling of track segments, including loading, saving and exporting, and preservation during edits and undos
@@ -209,7 +187,6 @@ The following features were added since version 4.1:
 
 New with version 4
 ==================
-
 The following features were added since version 3:
   - Automatic correlation of photos with points based on timestamps
   - Manual disconnection of photos from points
@@ -222,7 +199,6 @@ The following features were added since version 3:
 
 New with version 3
 ==================
-
 The following features were added since version 2:
   - Loading of GPX and KML files
   - Loading of jpeg photos with or without coordinate data
@@ -234,7 +210,6 @@ The following features were added since version 2:
 
 New with version 2
 ==================
-
 The following features were added since version 1:
   - Display of data in 3d view using Java3D library
   - Export of 3d model to POV format for rendering by povray
@@ -244,7 +219,6 @@ The following features were added since version 1:
 
 Features of version 1
 =====================
-
 The following features were included in version 1:
   - Loading of text files, display in overhead and profile views
   - Display of track details such as distances, speeds
index b4a73d43d86f301a464d8ab5dde1e68be189d332..ecf61d393907e4085926f0bfbefbfd76076ccc6c 100644 (file)
@@ -74,7 +74,7 @@ public class FileSaver
        private static final int[] FORMAT_COORDS = {Coordinate.FORMAT_NONE, Coordinate.FORMAT_DEG_MIN_SEC,
                Coordinate.FORMAT_DEG_MIN, Coordinate.FORMAT_DEG};
        private static final Unit[] UNIT_ALTS = {null, UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_FEET};
-       private static final int[] FORMAT_TIMES = {Timestamp.FORMAT_ORIGINAL, Timestamp.FORMAT_LOCALE, Timestamp.FORMAT_ISO_8601};
+       private static final Timestamp.Format[] FORMAT_TIMES = {Timestamp.Format.ORIGINAL, Timestamp.Format.LOCALE, Timestamp.Format.ISO8601};
 
 
        /**
@@ -437,7 +437,7 @@ public class FileSaver
                        }
                }
                // Get timestamp format
-               int timestampFormat = Timestamp.FORMAT_ORIGINAL;
+               Timestamp.Format timestampFormat = Timestamp.Format.ORIGINAL;
                for (int i=0; i<_timestampUnitsRadios.length; i++)
                {
                        if (_timestampUnitsRadios[i].isSelected()) {
@@ -572,7 +572,7 @@ public class FileSaver
         * @param inTimestampFormat timestamp format
         */
        private void saveField(StringBuffer inBuffer, DataPoint inPoint, Field inField,
-               int inCoordFormat, Unit inAltitudeUnit, int inTimestampFormat)
+               int inCoordFormat, Unit inAltitudeUnit, Timestamp.Format inTimestampFormat)
        {
                // Output field according to type
                if (inField == Field.LATITUDE)
@@ -595,14 +595,8 @@ public class FileSaver
                {
                        if (inPoint.hasTimestamp())
                        {
-                               if (inTimestampFormat == Timestamp.FORMAT_ORIGINAL) {
-                                       // output original string
-                                       inBuffer.append(inPoint.getTimestamp().getText(Timestamp.FORMAT_ORIGINAL));
-                               }
-                               else {
-                                       // format value accordingly
-                                       inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat));
-                               }
+                               // format value accordingly
+                               inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat));
                        }
                }
                else
index 1b2263bc8260d6dc49115f969de5e9c40be1ffcb..0f038b77ce60f473a50a64b068c68b698f5b2050 100644 (file)
@@ -535,7 +535,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                source = replaceGpxTags(source, "lat=\"", "\"", inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
                source = replaceGpxTags(source, "lon=\"", "\"", inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
                source = replaceGpxTags(source, "<ele>", "</ele>", inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES));
-               source = replaceGpxTags(source, "<time>", "</time>", inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
+               source = replaceGpxTags(source, "<time>", "</time>", inPoint.getTimestamp().getText(Timestamp.Format.ISO8601));
                if (inPoint.isWaypoint())
                {
                        source = replaceGpxTags(source, "<name>", "</name>", inPoint.getWaypointName());
@@ -685,7 +685,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                if (inPoint.hasTimestamp() && inTimestamps)
                {
                        inWriter.write("\t\t<time>");
-                       inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
+                       inWriter.write(inPoint.getTimestamp().getText(Timestamp.Format.ISO8601));
                        inWriter.write("</time>\n");
                }
                // write waypoint name after elevation and time
@@ -757,7 +757,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                if (inPoint.hasTimestamp() && inTimestamps)
                {
                        inWriter.write("\t\t\t\t<time>");
-                       inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
+                       inWriter.write(inPoint.getTimestamp().getText(Timestamp.Format.ISO8601));
                        inWriter.write("</time>\n");
                }
                // photo, audio
index 41bc0e6d0e7f186bc884ad98b080de4b0474bef6..f3383fe33c2b936c001420df545baf482a6e02dd 100644 (file)
@@ -36,6 +36,7 @@ import tim.prune.data.Track;
 import tim.prune.gui.BaseImageDefinitionPanel;
 import tim.prune.gui.GuiGridLayout;
 import tim.prune.gui.WholeNumberField;
+import tim.prune.gui.colour.PointColourer;
 import tim.prune.gui.map.MapSource;
 import tim.prune.gui.map.MapSourceLibrary;
 import tim.prune.gui.map.MapUtils;
@@ -289,9 +290,11 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer
                final int zoomFactor = 1 << _baseImagePanel.getImageDefinition().getZoom();
                Graphics g = inImage.getImage().getGraphics();
                // TODO: Set line width, style etc
-               g.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT));
+               final PointColourer pointColourer = _app.getPointColourer();
+               final Color defaultPointColour = Config.getColourScheme().getColour(ColourScheme.IDX_POINT);
+               g.setColor(defaultPointColour);
 
-               // Loop over points
+               // Loop to draw all track points
                final Track track = _app.getTrackInfo().getTrack();
                final int numPoints = track.getNumPoints();
                int prevX = 0, prevY = 0;
@@ -300,6 +303,12 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer
                        DataPoint point = track.getPoint(i);
                        if (!point.isWaypoint())
                        {
+                               // Determine what colour to use to draw the track point
+                               if (pointColourer != null)
+                               {
+                                       Color c = pointColourer.getColour(i);
+                                       g.setColor(c == null ? defaultPointColour : c);
+                               }
                                double x = track.getX(i) - xRange.getMinimum();
                                double y = track.getY(i) - yRange.getMinimum();
                                // use zoom level to calculate pixel coords on image
@@ -318,10 +327,11 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer
                                prevX = px; prevY = py;
                        }
                }
-               // Draw waypoints
+
+               // Now the waypoints
                final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT);
                g.setColor(textColour);
-               // Loop over points
+               // Loop again to draw waypoints
                for (int i=0; i<numPoints; i++)
                {
                        DataPoint point = track.getPoint(i);
index 802183427fd0212300ac91314cc4c8afb1e60f20..50386ccebeea79a057e56fa18d925fcac546d00d 100644 (file)
@@ -51,11 +51,11 @@ import tim.prune.data.Timestamp;
 import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;
 import tim.prune.data.UnitSetLibrary;
-import tim.prune.gui.ColourChooser;
-import tim.prune.gui.ColourPatch;
 import tim.prune.gui.DialogCloser;
 import tim.prune.gui.ImageUtils;
 import tim.prune.gui.WholeNumberField;
+import tim.prune.gui.colour.ColourChooser;
+import tim.prune.gui.colour.ColourPatch;
 import tim.prune.load.GenericFileFilter;
 import tim.prune.save.xml.XmlUtils;
 
@@ -684,7 +684,7 @@ public class KmlExporter extends GenericFunction implements Runnable
                                        // Add timestamp (if any) to the list
                                        whenList.append("<when>");
                                        if (point.hasTimestamp()) {
-                                               whenList.append(point.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
+                                               whenList.append(point.getTimestamp().getText(Timestamp.Format.ISO8601));
                                        }
                                        whenList.append("</when>\n");
                                        // Add coordinates to the list
index 19e4485c66bf0e09ec576793804bdf88c4187591..c00274787a0f458ebddadbe092070045e1a6ce7f 100644 (file)
@@ -321,8 +321,11 @@ public class PovExporter extends Export3dFunction
                                        {
                                                // file saved - store directory in config for later
                                                Config.setConfigString(Config.KEY_TRACK_DIR, povFile.getParentFile().getAbsolutePath());
-                                               // also store exaggeration
+                                               // also store exaggeration and grid size
                                                Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_altFactor * 100));
+                                               if (_terrainPanel.getUseTerrain() && _terrainPanel.getGridSize() > 20) {
+                                                       Config.setConfigInt(Config.KEY_TERRAIN_GRID_SIZE, _terrainPanel.getGridSize());
+                                               }
                                        }
                                        else
                                        {
index d99a9f68056637ae838788c709d40e9d061c68cc..73d9c4f3c50e984662822401b095fcefc94f3438 100644 (file)
@@ -8,13 +8,24 @@ import tim.prune.data.TrackInfo;
  */
 public abstract class UndoDeleteOperation implements UndoOperation
 {
+       /** Flag to remember whether the deleted point was at the beginning or end of the selected range */
+       private boolean _isAtBoundaryOfSelectedRange = false;
+
+       /**
+        * @param inAtBoundary true if deleted point was at the beginning or end of the selected range
+        */
+       public void setAtBoundaryOfSelectedRange(boolean inAtBoundary)
+       {
+               _isAtBoundaryOfSelectedRange = inAtBoundary;
+       }
+
        /**
         * Modify the current point/range selection after the delete operation is undone
         * @param inTrackInfo track info object
         * @param inStartIndex start index of reinserted range
         * @param inEndIndex end index of reinserted range
         */
-       protected static void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex)
+       protected void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex)
        {
                final int numPointsInserted = inEndIndex - inStartIndex + 1;
                // See if there is a currently selected point, if so does it need to be modified
@@ -26,12 +37,19 @@ public abstract class UndoDeleteOperation implements UndoOperation
                // Same for currently selected range
                int rangeStart = inTrackInfo.getSelection().getStart();
                int rangeEnd   = inTrackInfo.getSelection().getEnd();
-               if (rangeEnd >= inStartIndex && rangeEnd > rangeStart)
+               // Was the deleted point at the start or end of the selected range?
+               final boolean wasAtStart = numPointsInserted == 1 && inStartIndex == rangeStart && _isAtBoundaryOfSelectedRange;
+               final boolean wasAtEnd   = numPointsInserted == 1 && inStartIndex == (rangeEnd+1) && _isAtBoundaryOfSelectedRange;
+               if (rangeEnd >= inStartIndex && rangeEnd > rangeStart || wasAtStart || wasAtEnd)
                {
                        rangeEnd += numPointsInserted;
                        if (rangeStart >= inStartIndex) {
                                rangeStart += numPointsInserted;
                        }
+                       // Extend selection if the deleted point was at the start or end
+                       if (wasAtStart) {
+                               rangeStart--;
+                       }
                        inTrackInfo.getSelection().selectRange(rangeStart, rangeEnd);
                }
        }