]> gitweb.fperrin.net Git - GpsPrune.git/commitdiff
Version 14, October 2012
authoractivityworkshop <mail@activityworkshop.net>
Sun, 15 Feb 2015 10:15:00 +0000 (11:15 +0100)
committeractivityworkshop <mail@activityworkshop.net>
Sun, 15 Feb 2015 10:15:00 +0000 (11:15 +0100)
125 files changed:
tim/prune/App.java
tim/prune/FunctionLibrary.java
tim/prune/GpsPrune.java
tim/prune/config/Config.java
tim/prune/copyright.txt
tim/prune/correlate/Correlator.java
tim/prune/correlate/MediaPreviewTableModel.java
tim/prune/correlate/MediaPreviewTableRow.java
tim/prune/correlate/MediaSelectionTableModel.java
tim/prune/data/Altitude.java
tim/prune/data/AltitudeRange.java
tim/prune/data/DataPoint.java
tim/prune/data/Distance.java
tim/prune/data/Field.java
tim/prune/data/MediaList.java
tim/prune/data/MediaObject.java
tim/prune/data/MidpointData.java [new file with mode: 0644]
tim/prune/data/PointScaler.java
tim/prune/data/RecentFile.java
tim/prune/data/Selection.java
tim/prune/data/SpeedCalculator.java [new file with mode: 0644]
tim/prune/data/SpeedValue.java [new file with mode: 0644]
tim/prune/data/Timestamp.java
tim/prune/data/Track.java
tim/prune/data/TrackInfo.java
tim/prune/data/Unit.java [new file with mode: 0644]
tim/prune/data/UnitSet.java [new file with mode: 0644]
tim/prune/data/UnitSetLibrary.java [new file with mode: 0644]
tim/prune/function/AddAltitudeOffset.java
tim/prune/function/CropToSelection.java [new file with mode: 0644]
tim/prune/function/DeleteBitOfTrackFunction.java [new file with mode: 0644]
tim/prune/function/DeleteSelectedRangeFunction.java [new file with mode: 0644]
tim/prune/function/DiskCacheConfig.java
tim/prune/function/FullRangeDetails.java
tim/prune/function/GetWikipediaFunction.java
tim/prune/function/InterpolateFunction.java [new file with mode: 0644]
tim/prune/function/PasteCoordinates.java
tim/prune/function/PhotoComparer.java [new file with mode: 0644]
tim/prune/function/PhotoPopupFunction.java
tim/prune/function/PlayAudioFunction.java
tim/prune/function/RearrangePhotosFunction.java
tim/prune/function/SearchWikipediaNames.java
tim/prune/function/cache/ManageCacheFunction.java
tim/prune/function/cache/RowInfo.java
tim/prune/function/charts/Charter.java
tim/prune/function/compress/CompressTrackFunction.java
tim/prune/function/compress/CompressionAlgorithm.java
tim/prune/function/compress/MarkPointsInRectangleFunction.java [new file with mode: 0644]
tim/prune/function/distance/DistanceFunction.java
tim/prune/function/distance/DistanceTableModel.java
tim/prune/function/edit/PointNameEditor.java
tim/prune/function/gpsies/GenericDownloaderFunction.java
tim/prune/function/gpsies/GetGpsiesFunction.java
tim/prune/function/gpsies/TrackListModel.java
tim/prune/function/gpsies/UploadGpsiesFunction.java
tim/prune/gui/DetailsDisplay.java
tim/prune/gui/IconManager.java
tim/prune/gui/ImageUtils.java
tim/prune/gui/MenuManager.java
tim/prune/gui/SelectorDisplay.java
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/autopan.gif
tim/prune/gui/images/connect_photo_icon.png [deleted file]
tim/prune/gui/images/cut_and_move.gif
tim/prune/gui/images/drag_points_icon.gif [new file with mode: 0644]
tim/prune/gui/images/drag_points_icon_on.gif [new file with mode: 0644]
tim/prune/gui/images/scalebar.gif
tim/prune/gui/map/CloudmadeMapSource.java
tim/prune/gui/map/DiskTileCacher.java
tim/prune/gui/map/MapCanvas.java
tim/prune/gui/map/MapPosition.java
tim/prune/gui/map/MapSource.java
tim/prune/gui/map/MapSourceLibrary.java
tim/prune/gui/map/MapTileManager.java
tim/prune/gui/map/MapUtils.java
tim/prune/gui/map/MemTileCacher.java
tim/prune/gui/map/OsmMapSource.java
tim/prune/gui/map/ScaleBar.java
tim/prune/gui/map/TileDownloader.java [new file with mode: 0644]
tim/prune/gui/profile/AltitudeData.java
tim/prune/gui/profile/ProfileChart.java
tim/prune/gui/profile/ProfileData.java
tim/prune/gui/profile/SpeedData.java
tim/prune/gui/profile/VerticalSpeedData.java [new file with mode: 0644]
tim/prune/lang/prune-texts_af.properties
tim/prune/lang/prune-texts_cz.properties
tim/prune/lang/prune-texts_da.properties [new file with mode: 0644]
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_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_in.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_tr.properties
tim/prune/lang/prune-texts_zh.properties
tim/prune/load/MediaHelper.java
tim/prune/load/NmeaFileLoader.java
tim/prune/load/TextFileLoader.java
tim/prune/readme.txt
tim/prune/save/FileSaver.java
tim/prune/save/GpsSaver.java
tim/prune/save/GpxExporter.java
tim/prune/save/PovExporter.java
tim/prune/threedee/Java3DWindow.java
tim/prune/threedee/ThreeDModel.java
tim/prune/undo/UndoAddAltitudeOffset.java
tim/prune/undo/UndoDeleteMarked.java [moved from tim/prune/undo/UndoCompress.java with 81% similarity]
tim/prune/undo/UndoDeletePoint.java
tim/prune/undo/UndoDeleteRange.java
tim/prune/undo/UndoEditPoint.java
tim/prune/undo/UndoInsert.java
tim/prune/undo/UndoInterpolate.java [new file with mode: 0644]
tim/prune/undo/UndoLoadPhotos.java
tim/prune/undo/UndoLookupSrtm.java

index 770fd2089aaac23518ecd687bec0f0d8878ee738..205d88ae11c74165c23bd6aa7ef1fab275af4485 100644 (file)
@@ -62,8 +62,12 @@ public class App
        private boolean _mangleTimestampsConfirmed = false;
        private Viewport _viewport = null;
        private ArrayList<File> _dataFiles = null;
-       private boolean _firstDataFile = true;
+       private boolean _autoAppendNextFile = false;
        private boolean _busyLoading = false;
+       private AppMode _appMode = AppMode.NORMAL;
+
+       /** Enum for the app mode - currently only two options but may expand later */
+       public enum AppMode {NORMAL, DRAWRECT};
 
 
        /**
@@ -123,14 +127,15 @@ public class App
                if (inDataFiles == null || inDataFiles.size() == 0) {
                        _dataFiles = null;
                }
-               else {
+               else
+               {
                        _dataFiles = inDataFiles;
                        File f = _dataFiles.get(0);
                        _dataFiles.remove(0);
                        // Start load of specified file
                        if (_fileLoader == null)
                                _fileLoader = new FileLoader(this, _frame);
-                       _firstDataFile = true;
+                       _autoAppendNextFile = false; // prompt for append
                        _fileLoader.openFile(f);
                }
        }
@@ -144,6 +149,7 @@ public class App
        {
                _undoStack.add(inUndo);
                UpdateMessageBroker.informSubscribers(inConfirmText);
+               setCurrentMode(AppMode.NORMAL);
        }
 
        /**
@@ -272,6 +278,7 @@ public class App
                DataPoint currentPoint = _trackInfo.getCurrentPoint();
                if (currentPoint != null)
                {
+                       // Check for photo
                        boolean deletePhoto = false;
                        Photo currentPhoto = currentPoint.getPhoto();
                        if (currentPhoto != null)
@@ -291,10 +298,11 @@ public class App
                        // store necessary information to undo it later
                        int pointIndex = _trackInfo.getSelection().getCurrentPointIndex();
                        int photoIndex = _trackInfo.getPhotoList().getPhotoIndex(currentPhoto);
+                       int audioIndex = _trackInfo.getAudioList().getAudioIndex(currentPoint.getAudio());
                        DataPoint nextTrackPoint = _trackInfo.getTrack().getNextTrackPoint(pointIndex + 1);
                        // Construct Undo object
                        UndoOperation undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex,
-                               nextTrackPoint != null && nextTrackPoint.getSegmentStart());
+                               audioIndex, nextTrackPoint != null && nextTrackPoint.getSegmentStart());
                        // call track to delete point
                        if (_trackInfo.deletePoint())
                        {
@@ -312,94 +320,15 @@ public class App
                                                // decouple photo from point
                                                currentPhoto.setDataPoint(null);
                                        }
-                                       UpdateMessageBroker.informSubscribers();
+                                       UpdateMessageBroker.informSubscribers(DataSubscriber.PHOTOS_MODIFIED);
                                }
-                               // Confirm
-                               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.deletepoint.single"));
-                       }
-               }
-       }
-
-
-       /**
-        * Delete the currently selected range
-        */
-       public void deleteSelectedRange()
-       {
-               if (_track == null) return;
-               // Find out if photos should be deleted or not
-               int selStart = _trackInfo.getSelection().getStart();
-               int selEnd = _trackInfo.getSelection().getEnd();
-               if (selStart >= 0 && selEnd >= selStart)
-               {
-                       int numToDelete = selEnd - selStart + 1;
-                       boolean[] deletePhotos = new boolean[numToDelete];
-                       Photo[] photosToDelete = new Photo[numToDelete];
-                       boolean deleteAll = false;
-                       boolean deleteNone = false;
-                       String[] questionOptions = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
-                               I18nManager.getText("button.yestoall"), I18nManager.getText("button.notoall"),
-                               I18nManager.getText("button.cancel")};
-                       DataPoint point = null;
-                       for (int i=0; i<numToDelete; i++)
-                       {
-                               point = _trackInfo.getTrack().getPoint(i + selStart);
-                               if (point != null && point.getPhoto() != null)
-                               {
-                                       if (deleteAll)
-                                       {
-                                               deletePhotos[i] = true;
-                                               photosToDelete[i] = point.getPhoto();
-                                       }
-                                       else if (deleteNone) {deletePhotos[i] = false;}
-                                       else
-                                       {
-                                               int response = JOptionPane.showOptionDialog(_frame,
-                                                       I18nManager.getText("dialog.deletepoint.deletephoto") + " " + point.getPhoto().getName(),
-                                                       I18nManager.getText("dialog.deletepoint.title"),
-                                                       JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
-                                                       questionOptions, questionOptions[1]);
-                                               // check for cancel or close
-                                               if (response == 4 || response == -1) {return;}
-                                               // check for yes or yes to all
-                                               if (response == 0 || response == 2)
-                                               {
-                                                       deletePhotos[i] = true;
-                                                       photosToDelete[i] = point.getPhoto();
-                                                       if (response == 2) {deleteAll = true;}
-                                               }
-                                               // check for no to all
-                                               if (response == 3) {deleteNone = true;}
-                                       }
+                               // Delete audio object (without bothering to ask)
+                               if (audioIndex > -1) {
+                                       _trackInfo.getAudioList().deleteAudio(audioIndex);
                                }
-                       }
-                       // add information to undo stack
-                       UndoDeleteRange undo = new UndoDeleteRange(_trackInfo);
-                       // delete requested photos
-                       for (int i=0; i<numToDelete; i++)
-                       {
-                               point = _trackInfo.getTrack().getPoint(i + selStart);
-                               if (point != null && point.getPhoto() != null)
-                               {
-                                       if (deletePhotos[i])
-                                       {
-                                               // delete photo from list
-                                               _trackInfo.getPhotoList().deletePhoto(_trackInfo.getPhotoList().getPhotoIndex(point.getPhoto()));
-                                       }
-                                       else
-                                       {
-                                               // decouple from point
-                                               point.getPhoto().setDataPoint(null);
-                                       }
-                               }
-                       }
-                       // call track to delete range
-                       if (_trackInfo.deleteRange())
-                       {
-                               _undoStack.push(undo);
                                // Confirm
-                               UpdateMessageBroker.informSubscribers("" + numToDelete + " "
-                                       + I18nManager.getText("confirm.deletepoint.multi"));
+                               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.deletepoint.single"));
+                               UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_ADDED_OR_REMOVED);
                        }
                }
        }
@@ -410,7 +339,7 @@ public class App
         */
        public void finishCompressTrack()
        {
-               UndoCompress undo = new UndoCompress(_track);
+               UndoDeleteMarked undo = new UndoDeleteMarked(_track);
                // call track to do compress
                int numPointsDeleted = _trackInfo.deleteMarkedPoints();
                // add to undo stack if successful
@@ -422,7 +351,7 @@ public class App
                                 + (numPointsDeleted==1?I18nManager.getText("confirm.deletepoint.single"):I18nManager.getText("confirm.deletepoint.multi")));
                }
                else {
-                       showErrorMessage("function.compress", "dialog.compress.nonefound");
+                       showErrorMessage("function.compress", "dialog.deletemarked.nonefound");
                }
        }
 
@@ -528,29 +457,6 @@ public class App
        }
 
 
-       /**
-        * Interpolate the two selected points
-        */
-       public void interpolateSelection()
-       {
-               // Get number of points to add
-               Object numPointsStr = JOptionPane.showInputDialog(_frame,
-                       I18nManager.getText("dialog.interpolate.parameter.text"),
-                       I18nManager.getText("dialog.interpolate.title"),
-                       JOptionPane.QUESTION_MESSAGE, null, null, "");
-               int numPoints = parseNumber(numPointsStr);
-               if (numPoints <= 0) return;
-
-               UndoInsert undo = new UndoInsert(_trackInfo.getSelection().getStart() + 1,
-                       numPoints);
-               // call track to interpolate
-               if (_trackInfo.interpolate(numPoints))
-               {
-                       _undoStack.add(undo);
-               }
-       }
-
-
        /**
         * Average the selected points
         */
@@ -570,7 +476,7 @@ public class App
 
 
        /**
-        * Create a new point at the given position
+        * Create a new point at the end of the track
         * @param inPoint point to add
         */
        public void createPoint(DataPoint inPoint)
@@ -589,6 +495,26 @@ public class App
        }
 
 
+       /**
+        * Create a new point before the given position
+        * @param inPoint point to add
+        * @param inIndex index of following point
+        */
+       public void createPoint(DataPoint inPoint, int inIndex)
+       {
+               // create undo object
+               UndoInsert undo = new UndoInsert(inIndex, 1);
+               _undoStack.add(undo);
+               // add point to track
+               _track.insertPoint(inPoint, inIndex);
+               // ensure track's field list contains point's fields
+               _track.extendFieldList(inPoint.getFieldList());
+               _trackInfo.selectPoint(inIndex);
+               // update listeners
+               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.createpoint"));
+       }
+
+
        /**
         * Cut the current selection and move it to before the currently selected point
         */
@@ -638,19 +564,6 @@ public class App
                _track.clearDeletionMarkers();
        }
 
-       /**
-        * Receive loaded data and start load
-        * @param inFieldArray array of fields
-        * @param inDataArray array of data
-        * @param inAltFormat altitude format
-        * @param inSourceInfo information about the source of the data
-        */
-       public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray,
-               Altitude.Format inAltFormat, SourceInfo inSourceInfo)
-       {
-               informDataLoaded(inFieldArray, inDataArray, inAltFormat, inSourceInfo, null, null);
-       }
-
        /**
         * Receive loaded data and determine whether to filter on tracks or not
         * @param inFieldArray array of fields
@@ -715,6 +628,7 @@ public class App
                        // go directly to load
                        informDataLoaded(loadedTrack, inSourceInfo);
                }
+               setCurrentMode(AppMode.NORMAL);
        }
 
 
@@ -730,16 +644,19 @@ public class App
                {
                        // ask whether to replace or append
                        int answer = 0;
-                       if (_dataFiles == null || _firstDataFile) {
+                       if (_autoAppendNextFile) {
+                               // Automatically append the next file
+                               answer = JOptionPane.YES_OPTION;
+                       }
+                       else {
+                               // Ask whether to append or not
                                answer = JOptionPane.showConfirmDialog(_frame,
                                        I18nManager.getText("dialog.openappend.text"),
                                        I18nManager.getText("dialog.openappend.title"),
                                        JOptionPane.YES_NO_CANCEL_OPTION);
                        }
-                       else {
-                               // Automatically append if there's a file load queue
-                               answer = JOptionPane.YES_OPTION;
-                       }
+                       _autoAppendNextFile = false; // reset flag to cancel autoappend
+
                        if (answer == JOptionPane.YES_OPTION)
                        {
                                // append data to current Track
@@ -807,12 +724,20 @@ public class App
                loadNextFile();
        }
 
+       /**
+        * External trigger to automatically append the next loaded file
+        * instead of prompting to replace or append
+        */
+       public void autoAppendNextFile()
+       {
+               _autoAppendNextFile = true;
+       }
+
        /**
         * Load the next file in the waiting list, if any
         */
        private void loadNextFile()
        {
-               _firstDataFile = false;
                if (_dataFiles == null || _dataFiles.size() == 0) {
                        _dataFiles = null;
                }
@@ -821,6 +746,7 @@ public class App
                                public void run() {
                                        File f = _dataFiles.get(0);
                                        _dataFiles.remove(0);
+                                       _autoAppendNextFile = true;
                                        _fileLoader.openFile(f);
                                }
                        }).start();
@@ -941,32 +867,12 @@ public class App
                        showErrorMessageNoLookup("error.undofailed.title",
                                I18nManager.getText("error.undofailed.text") + " : " + ue.getMessage());
                        _undoStack.clear();
-                       UpdateMessageBroker.informSubscribers();
                }
                catch (EmptyStackException empty) {}
+               UpdateMessageBroker.informSubscribers();
        }
 
 
-       /**
-        * 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;
-       }
-
        /**
         * Show a map url in an external browser
         * @param inSourceIndex index of map source to use
@@ -1035,4 +941,14 @@ public class App
        public boolean isBusyLoading() {
                return _busyLoading;
        }
+
+       /** @return current app mode */
+       public AppMode getCurrentMode() {
+               return _appMode;
+       }
+
+       /** @param inMode the current app mode */
+       public void setCurrentMode(AppMode inMode) {
+               _appMode = inMode;
+       }
 }
index 0830049b57bb69c74760bc6b608dd74cde760386..f06dbe457983ad723c8f00ee9f3c8b797517b656 100644 (file)
@@ -36,6 +36,9 @@ public abstract class FunctionLibrary
        public static RearrangeWaypointsFunction FUNCTION_REARRANGE_WAYPOINTS = null;
        public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null;
        public static GenericFunction FUNCTION_COMPRESS = null;
+       public static GenericFunction FUNCTION_DELETE_RANGE = null;
+       public static GenericFunction FUNCTION_CROP_TRACK = null;
+       public static GenericFunction FUNCTION_INTERPOLATE = null;
        public static GenericFunction FUNCTION_LOOKUP_SRTM = null;
        public static GenericFunction FUNCTION_LOOKUP_WIKIPEDIA = null;
        public static GenericFunction FUNCTION_SEARCH_WIKIPEDIA = null;
@@ -98,6 +101,9 @@ public abstract class FunctionLibrary
                FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp);
                FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp);
                FUNCTION_COMPRESS = new CompressTrackFunction(inApp);
+               FUNCTION_DELETE_RANGE = new DeleteSelectedRangeFunction(inApp);
+               FUNCTION_CROP_TRACK = new CropToSelection(inApp);
+               FUNCTION_INTERPOLATE = new InterpolateFunction(inApp);
                FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp);
                FUNCTION_LOOKUP_WIKIPEDIA = new GetWikipediaFunction(inApp);
                FUNCTION_SEARCH_WIKIPEDIA = new SearchWikipediaNames(inApp);
index a444a02c9960b5fc41e35ebc245c9c914b0aa413..e1dc55e0574b29549d5d5596d1a8c9c6c620c753 100644 (file)
@@ -28,16 +28,16 @@ import tim.prune.gui.profile.ProfileChart;
 /**
  * GpsPrune is a tool to visualize, edit, convert and prune GPS data
  * Please see the included readme.txt or http://activityworkshop.net
- * This software is copyright activityworkshop.net 2006-2011 and made available through the Gnu GPL version 2.
+ * This software is copyright activityworkshop.net 2006-2012 and made available through the Gnu GPL version 2.
  * For license details please see the included license.txt.
  * GpsPrune is the main entry point to the application, including initialisation and launch
  */
 public class GpsPrune
 {
        /** Version number of application, used in about screen and for version check */
-       public static final String VERSION_NUMBER = "13.4";
+       public static final String VERSION_NUMBER = "14";
        /** Build number, just used for about screen */
-       public static final String BUILD_NUMBER = "244c";
+       public static final String BUILD_NUMBER = "265a";
        /** Static reference to App object */
        private static App APP = null;
 
@@ -87,7 +87,7 @@ public class GpsPrune
                        {
                                // Check if a data file has been given
                                File f = new File(arg);
-                               if (f.exists() && f.canRead()) {
+                               if (f.exists() && f.isFile() && f.canRead()) {
                                        dataFiles.add(f);
                                }
                                else
@@ -258,8 +258,11 @@ public class GpsPrune
        private static String makeUnknownParameterString(String inParam)
        {
                File file = new File(inParam);
-               if (file.exists()) {
-                       return (file.canRead() ? "Something wrong with file" : "Cannot read file") + " '" + inParam + "'";
+               if (file.exists())
+               {
+                       if (file.isDirectory()) return "'" + inParam + "' is a directory";
+                       if (!file.canRead())    return "Cannot read file '" + inParam + "'";
+                       return "Something wrong with file '" + inParam + "'";
                }
                do
                {
index a286d7fcc116315d03d4666b3af2d0cd4883b8af..5a95bebdebc62d52c6da4f52ec7391026781c206 100644 (file)
@@ -5,6 +5,8 @@ import java.io.FileInputStream;
 import java.util.Properties;
 
 import tim.prune.data.RecentFileList;
+import tim.prune.data.UnitSet;
+import tim.prune.data.UnitSetLibrary;
 
 
 /**
@@ -21,6 +23,8 @@ public abstract class Config
        private static ColourScheme _colourScheme = new ColourScheme();
        /** Recently-used file list */
        private static RecentFileList _recentFiles = new RecentFileList();
+       /** Current unit set */
+       private static UnitSet _unitSet = UnitSetLibrary.getUnitSet(null);
 
        /** Default config file */
        public static final File DEFAULT_CONFIG_FILE = new File(".pruneconfig");
@@ -40,8 +44,8 @@ public abstract class Config
        public static final String KEY_GPS_FORMAT = "prune.gpsformat";
        /** Key for Povray font */
        public static final String KEY_POVRAY_FONT = "prune.povrayfont";
-       /** Key for metric/imperial */
-       public static final String KEY_METRIC_UNITS = "prune.metricunits";
+       /** Key for the selected unit set */
+       public static final String KEY_UNITSET_KEY  = "prune.unitsetkey";
        /** Key for index of map source */
        public static final String KEY_MAPSOURCE_INDEX = "prune.mapsource";
        /** Key for number of fixed map sources */
@@ -135,6 +139,8 @@ public abstract class Config
                _configValues.putAll(props);
                _colourScheme.loadFromHex(_configValues.getProperty(KEY_COLOUR_SCHEME));
                _recentFiles = new RecentFileList(_configValues.getProperty(KEY_RECENT_FILES));
+               _unitSet = UnitSetLibrary.getUnitSet(_configValues.getProperty(KEY_UNITSET_KEY));
+
                if (loadFailed) {
                        throw new ConfigException();
                }
@@ -159,6 +165,7 @@ public abstract class Config
                props.put(KEY_KMZ_IMAGE_WIDTH, "240");
                props.put(KEY_KMZ_IMAGE_HEIGHT, "240");
                props.put(KEY_AUTOSAVE_SETTINGS, "0"); // autosave false by default
+               props.put(KEY_UNITSET_KEY, "unitset.kilometres"); // metric by default
                return props;
        }
 
@@ -287,9 +294,9 @@ public abstract class Config
         */
        public static boolean isKeyBoolean(String inKey)
        {
-               // Only two boolean keys so far
+               // Only one boolean key so far (after metric flag was removed)
                return inKey != null && (
-                       inKey.equals(KEY_METRIC_UNITS) || inKey.equals(KEY_SHOW_MAP));
+                       inKey.equals(KEY_SHOW_MAP));
        }
 
        /**
@@ -299,4 +306,18 @@ public abstract class Config
        {
                setConfigString(KEY_COLOUR_SCHEME, _colourScheme.toString());
        }
+
+       /**
+        * @return the current unit set
+        */
+       public static UnitSet getUnitSet() {
+               return _unitSet;
+       }
+
+       public static void selectUnitSet(int inIndex)
+       {
+               _unitSet = UnitSetLibrary.getUnitSet(inIndex);
+               // Set name of set in config
+               setConfigString(KEY_UNITSET_KEY, _unitSet.getNameKey());
+       }
 }
index d65d1dda57ca108c6eb889548d2bfa055b6e4410..b690fdc51102c8e6ce0faa6bb227c9307a47456b 100644 (file)
@@ -1,4 +1,4 @@
-The source code of GpsPrune is copyright 2006-2011 activityworkshop.net
+The source code of GpsPrune is copyright 2006-2012 activityworkshop.net
 and is distributed under the terms of the Gnu GPL version 2.
 
 Portions of the package jpeg.drew (if included in this package) were taken
index 2a4b49f882937100705aa67195c48552676f70f1..85b96e92304b0a5f1da3ea470272d8d165582972 100644 (file)
@@ -35,6 +35,8 @@ import tim.prune.data.MediaList;
 import tim.prune.data.TimeDifference;
 import tim.prune.data.Timestamp;
 import tim.prune.data.Track;
+import tim.prune.data.Unit;
+import tim.prune.data.UnitSetLibrary;
 
 /**
  * Abstract superclass of the two correlator functions
@@ -312,8 +314,10 @@ public abstract class Correlator extends GenericFunction
        {
                JPanel card = new JPanel();
                card.setLayout(new BorderLayout(10, 10));
-               card.add(new JLabel(I18nManager.getText(
-                       "dialog.correlate." + getMediaTypeKey() + "select.intro")), BorderLayout.NORTH);
+               JLabel introLabel = new JLabel(I18nManager.getText(
+                       "dialog.correlate." + getMediaTypeKey() + "select.intro"));
+               introLabel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
+               card.add(introLabel, BorderLayout.NORTH);
                // table doesn't have model yet - that will be attached later
                _selectionTable = new JTable();
                JScrollPane photoScrollPane = new JScrollPane(_selectionTable);
@@ -368,7 +372,7 @@ public abstract class Correlator extends GenericFunction
                offsetPanelBot.setLayout(new FlowLayout());
                offsetPanelBot.setBorder(null);
                _mediaLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options." + getMediaTypeKey() + "later"));
-               _pointLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options.pointlaterphoto"));
+               _pointLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options.pointlater" + getMediaTypeKey()));
                _mediaLaterOption.addItemListener(optionsChangedListener);
                _pointLaterOption.addItemListener(optionsChangedListener);
                ButtonGroup laterGroup = new ButtonGroup();
@@ -380,6 +384,12 @@ public abstract class Correlator extends GenericFunction
                offsetPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
                card2Top.add(offsetPanel);
 
+               // listener for radio buttons
+               ActionListener radioListener = new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               enableEditBoxes();
+                       }
+               };
                // time limits section
                JPanel limitsPanel = new JPanel();
                limitsPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.limitspanel")));
@@ -388,9 +398,11 @@ public abstract class Correlator extends GenericFunction
                timeLimitPanel.setLayout(new FlowLayout());
                JRadioButton noTimeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.notimelimit"));
                noTimeLimitRadio.addItemListener(optionsChangedListener);
+               noTimeLimitRadio.addActionListener(radioListener);
                timeLimitPanel.add(noTimeLimitRadio);
                _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + " : ");
                _timeLimitRadio.addItemListener(optionsChangedListener);
+               _timeLimitRadio.addActionListener(radioListener);
                timeLimitPanel.add(_timeLimitRadio);
                groupRadioButtons(noTimeLimitRadio, _timeLimitRadio);
                _limitMinBox = new JTextField(3);
@@ -407,9 +419,11 @@ public abstract class Correlator extends GenericFunction
                distLimitPanel.setLayout(new FlowLayout());
                JRadioButton noDistLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.nodistancelimit"));
                noDistLimitRadio.addItemListener(optionsChangedListener);
+               noDistLimitRadio.addActionListener(radioListener);
                distLimitPanel.add(noDistLimitRadio);
                _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit"));
                _distLimitRadio.addItemListener(optionsChangedListener);
+               _distLimitRadio.addActionListener(radioListener);
                distLimitPanel.add(_distLimitRadio);
                groupRadioButtons(noDistLimitRadio, _distLimitRadio);
                _limitDistBox = new JTextField(4);
@@ -516,6 +530,18 @@ public abstract class Correlator extends GenericFunction
                _okButton.setEnabled(inCardNum == 2 && ((MediaPreviewTableModel) _previewTable.getModel()).hasAnySelected());
        }
 
+       /**
+        * Enable or disable the edit boxes according to the radio button selections
+        */
+       private void enableEditBoxes()
+       {
+               // enable/disable text field for distance input
+               _limitDistBox.setEnabled(_distLimitRadio.isSelected());
+               // and for time limits
+               _limitMinBox.setEnabled(_timeLimitRadio.isSelected());
+               _limitSecBox.setEnabled(_timeLimitRadio.isSelected());
+       }
+
        /**
         * Parse the time limit values entered and validate them
         * @return TimeDifference object describing limit
@@ -557,9 +583,9 @@ public abstract class Correlator extends GenericFunction
        /**
         * @return the selected distance units from the dropdown
         */
-       protected Distance.Units getSelectedDistanceUnits()
+       protected Unit getSelectedDistanceUnits()
        {
-               final Distance.Units[] distUnits = {Distance.Units.KILOMETRES, Distance.Units.METRES, Distance.Units.MILES};
+               final Unit[] distUnits = {UnitSetLibrary.UNITS_KILOMETRES, UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_MILES};
                return distUnits[_distUnitsDropdown.getSelectedIndex()];
        }
 
@@ -604,6 +630,7 @@ public abstract class Correlator extends GenericFunction
                _mediaLaterOption.setSelected(timeDiff.getIsPositive());
                _pointLaterOption.setSelected(!timeDiff.getIsPositive());
                _previewEnabled = true;
+               enableEditBoxes();
                createPreview(timeDiff, true);
        }
 
@@ -635,19 +662,22 @@ public abstract class Correlator extends GenericFunction
        protected PointMediaPair getPointPairForMedia(Track inTrack, MediaObject inMedia, TimeDifference inOffset)
        {
                PointMediaPair pair = new PointMediaPair(inMedia);
-               // Add/subtract offset to media timestamp
-               Timestamp mediaStamp = getMediaTimestamp(inMedia).createMinusOffset(inOffset);
-               int numPoints = inTrack.getNumPoints();
-               for (int i=0; i<numPoints; i++)
+               if (inMedia.hasTimestamp())
                {
-                       DataPoint point = inTrack.getPoint(i);
-                       if (point.getPhoto() == null && point.getAudio() == null)
+                       // Add/subtract offset to media timestamp
+                       Timestamp mediaStamp = getMediaTimestamp(inMedia).createMinusOffset(inOffset);
+                       int numPoints = inTrack.getNumPoints();
+                       for (int i=0; i<numPoints; i++)
                        {
-                               Timestamp pointStamp = point.getTimestamp();
-                               if (pointStamp != null && pointStamp.isValid())
+                               DataPoint point = inTrack.getPoint(i);
+                               if (point.getPhoto() == null && point.getAudio() == null)
                                {
-                                       long numSeconds = pointStamp.getSecondsSince(mediaStamp);
-                                       pair.addPoint(point, numSeconds);
+                                       Timestamp pointStamp = point.getTimestamp();
+                                       if (pointStamp != null && pointStamp.isValid())
+                                       {
+                                               long numSeconds = pointStamp.getSecondsSince(mediaStamp);
+                                               pair.addPoint(point, numSeconds);
+                                       }
                                }
                        }
                }
index 78120ec784050a9b697c9bd2e172200e427e0989..3d6efa8196c1ca57cf47aced06d83b211577c5f8 100644 (file)
@@ -4,7 +4,8 @@ import java.text.NumberFormat;
 import java.util.ArrayList;
 import javax.swing.table.AbstractTableModel;
 import tim.prune.I18nManager;
-import tim.prune.data.Distance;
+import tim.prune.data.Unit;
+import tim.prune.data.UnitSetLibrary;
 
 /**
  * Class to act as the table model for the correlation preview table
@@ -16,7 +17,7 @@ public class MediaPreviewTableModel extends AbstractTableModel
        /** ArrayList containing TableRow objects */
        private ArrayList<MediaPreviewTableRow> _list = new ArrayList<MediaPreviewTableRow>();
        /** Distance units */
-       private Distance.Units _distanceUnits = Distance.Units.KILOMETRES;
+       private Unit _distanceUnits = UnitSetLibrary.UNITS_KILOMETRES;
        /** Number formatter */
        private static final NumberFormat FORMAT_ONE_DP = NumberFormat.getNumberInstance();
 
@@ -89,7 +90,10 @@ public class MediaPreviewTableModel extends AbstractTableModel
                MediaPreviewTableRow row = _list.get(inRowIndex);
                if (inColumnIndex == 0) return row.getMedia().getName();
                else if (inColumnIndex == 1) {
-                       return row.getMedia().getTimestamp().getText();
+                       if (row.getMedia().hasTimestamp()) {
+                               return row.getMedia().getTimestamp().getText();
+                       }
+                       return ""; // media doesn't have a timestamp
                }
                else if (inColumnIndex == 2) {
                        if (row.getPointPair().isValid()) {
@@ -110,7 +114,7 @@ public class MediaPreviewTableModel extends AbstractTableModel
        /**
         * @param inUnits the distance units to use
         */
-       public void setDistanceUnits(Distance.Units inUnits)
+       public void setDistanceUnits(Unit inUnits)
        {
                _distanceUnits = inUnits;
        }
index cadede31b39613e5512fc6a98be0d284e8cb814d..700336f7d1b928ecf5d5370100226e3435782ec9 100644 (file)
@@ -1,6 +1,7 @@
 package tim.prune.correlate;
 
 import tim.prune.data.Distance;
+import tim.prune.data.Unit;
 
 /**
  * Class to hold the contents of a single row in the correlation preview table
@@ -9,7 +10,6 @@ public class MediaPreviewTableRow extends MediaSelectionTableRow
 {
        private PointMediaPair _pointPair = null;
        private double _distance = 0.0;
-       private int _status = 0;
        private boolean _correlate = false;
 
 
@@ -22,7 +22,6 @@ public class MediaPreviewTableRow extends MediaSelectionTableRow
                super(inPointPair.getMedia(), inPointPair.getMinSeconds());
                _pointPair = inPointPair;
                _distance = inPointPair.getMinRadians();
-               _status = 0;
                _correlate = (inPointPair.getMedia().getDataPoint() == null);
        }
 
@@ -30,19 +29,11 @@ public class MediaPreviewTableRow extends MediaSelectionTableRow
         * @param inUnits units to use
         * @return distance in selected format
         */
-       public double getDistance(Distance.Units inUnits)
+       public double getDistance(Unit inUnits)
        {
                return Distance.convertRadiansToDistance(_distance, inUnits);
        }
 
-       /**
-        * @return point status
-        */
-       public int getStatus()
-       {
-               return _status;
-       }
-
        /**
         * @return point pair object
         */
index 69d059fffe53e2116f93bc94f81f416900a21ebd..93f8759bfa4c4352d653b489b85bbd649d52eb79 100644 (file)
@@ -83,7 +83,9 @@ public class MediaSelectionTableModel extends AbstractTableModel
                // MAYBE: only show time of photos (not date) if dates all identical
                MediaSelectionTableRow row = _list.get(inRowIndex);
                if (inColumnIndex == 0) return row.getMedia().getName();
-               else if (inColumnIndex == 1) return row.getMedia().getTimestamp().getText();
+               else if (inColumnIndex == 1) {
+                       return (row.getMedia().hasTimestamp() ? row.getMedia().getTimestamp().getText() : "");
+               }
                else if (inColumnIndex == 2) return row.getTimeDiff().getDescription();
                return (row.getTimeDiff().getIsPositive() ? I18nManager.getText("dialog.about.yes") :
                        I18nManager.getText("dialog.about.no"));
index 06a32911197fc3e0560e81c45ac042bb5a080ed3..ac71d759cb4a45a8a7af971b8d6adc830010bf83 100644 (file)
@@ -21,8 +21,8 @@ public class Altitude
        }
 
        /** Constants for conversion */
-       private static final double CONVERT_FEET_TO_METRES = 0.3048;
-       private static final double CONVERT_METRES_TO_FEET = 3.28084;
+       private static final double CONVERT_METRES_TO_FEET = UnitSetLibrary.UNITS_FEET.getMultFactorFromStd();
+       private static final double CONVERT_FEET_TO_METRES = 1 / CONVERT_METRES_TO_FEET;
 
        /** Constant for no altitude value */
        public static final Altitude NONE = new Altitude(null, Format.NO_FORMAT);
@@ -75,6 +75,7 @@ public class Altitude
         */
        public void reset(Altitude inClone)
        {
+               _stringValue = inClone._stringValue;
                _value = inClone._value;
                _format = inClone._format;
                _valid = inClone._valid;
@@ -97,6 +98,14 @@ public class Altitude
                return _value;
        }
 
+       /**
+        * @param inAltUnit altitude units to use
+        * @return rounded value in specified units
+        */
+       public int getValue(Unit inAltUnit)
+       {
+               return (int) (getMetricValue() * inAltUnit.getMultFactorFromStd());
+       }
 
        /**
         * @return format of number
@@ -124,6 +133,17 @@ public class Altitude
                return _value;
        }
 
+       /**
+        * @return value of altitude in metres, used for calculations and charts
+        */
+       public double getMetricValue()
+       {
+               if (_format == Format.FEET) {
+                       return _value / UnitSetLibrary.UNITS_FEET.getMultFactorFromStd();
+               }
+               return _value;
+       }
+
        /**
         * Get a string version of the value
         * @param inFormat specified format
@@ -196,6 +216,8 @@ public class Altitude
                        else
                                offset = inOffset * CONVERT_METRES_TO_FEET;
                }
+               // FIXME: The following will fail if _stringValue is null - not sure how it can get in that state!
+               if (_stringValue == null) System.err.println("*** Altitude.addOffset - how did the string value get to be null?");
                // Add the offset
                double newValue = Double.parseDouble(_stringValue.trim()) + offset;
                _value = (int) newValue;
index 98fa20edd5295f0e8097ec3407c98856a8fa0f53..b7ec647ab6456f4ad9bdef0f625d513fa28360d4 100644 (file)
@@ -6,17 +6,35 @@ package tim.prune.data;
  */
 public class AltitudeRange
 {
+       /** Range of altitudes in metres */
        private IntegerRange _range = new IntegerRange();
-       private Altitude.Format _format = Altitude.Format.NO_FORMAT;
+       /** Empty flag */
+       private boolean _empty;
+       /** Previous metric value */
+       private int _prevValue;
+       /** Total climb in metres */
+       private double _climb;
+       /** Total descent in metres */
+       private double _descent;
 
 
+       /**
+        * Constructor
+        */
+       public AltitudeRange() {
+               clear();
+       }
+
        /**
         * Clear the altitude range
         */
        public void clear()
        {
                _range.clear();
-               _format = Altitude.Format.NO_FORMAT;
+               _climb = 0.0;
+               _descent = 0.0;
+               _empty = true;
+               _prevValue = 0;
        }
 
 
@@ -26,17 +44,33 @@ public class AltitudeRange
         */
        public void addValue(Altitude inAltitude)
        {
-               if (inAltitude != null)
+               if (inAltitude != null && inAltitude.isValid())
                {
-                       int altValue = inAltitude.getValue(_format);
+                       int altValue = (int) inAltitude.getMetricValue();
                        _range.addValue(altValue);
-                       if (_format == Altitude.Format.NO_FORMAT)
+                       // Compare with previous value if any
+                       if (!_empty)
                        {
-                               _format = inAltitude.getFormat();
+                               if (altValue > _prevValue)
+                                       _climb += (altValue - _prevValue);
+                               else
+                                       _descent += (_prevValue - altValue);
                        }
+                       _prevValue = altValue;
+                       _empty = false;
                }
        }
 
+       /**
+        * Reset the climb/descent calculations starting from the given value
+        * @param inAltitude altitude value
+        */
+       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);
+       }
 
        /**
         * @return true if altitude range found
@@ -48,28 +82,40 @@ public class AltitudeRange
 
 
        /**
+        * @param inUnit altitude units to use
         * @return minimum value, or -1 if none found
         */
-       public int getMinimum()
+       public int getMinimum(Unit inUnit)
        {
-               return _range.getMinimum();
+               if (_range.getMinimum() <= 0) return _range.getMinimum();
+               return (int) (_range.getMinimum() * inUnit.getMultFactorFromStd());
        }
 
-
        /**
+        * @param inUnit altitude units to use
         * @return maximum value, or -1 if none found
         */
-       public int getMaximum()
+       public int getMaximum(Unit inUnit)
        {
-               return _range.getMaximum();
+               if (_range.getMaximum() <= 0) return _range.getMaximum();
+               return (int) (_range.getMaximum() * inUnit.getMultFactorFromStd());
        }
 
+       /**
+        * @param inUnit altitude units to use
+        * @return total climb
+        */
+       public int getClimb(Unit inUnit)
+       {
+               return (int) (_climb * inUnit.getMultFactorFromStd());
+       }
 
        /**
-        * @return the altitude format used
+        * @param inUnit altitude units to use
+        * @return total descent
         */
-       public Altitude.Format getFormat()
+       public int getDescent(Unit inUnit)
        {
-               return _format;
+               return (int) (_descent * inUnit.getMultFactorFromStd());
        }
 }
index 3d17cbc5edec4797a3dd63fee6e58579bcdd6025..34d7bd72055275f81339e1fe23c434d2008603c6 100644 (file)
@@ -38,6 +38,8 @@ public class DataPoint
                _fieldValues = inValueArray;
                // save list of fields
                _fieldList = inFieldList;
+               // Remove double quotes around values
+               removeQuotes(_fieldValues);
                // parse fields into objects
                parseFields(null, inAltFormat);
        }
@@ -164,7 +166,7 @@ public class DataPoint
                }
                else {
                        // use default altitude format from config
-                       parseFields(inField, Config.getConfigBoolean(Config.KEY_METRIC_UNITS)?Altitude.Format.METRES:Altitude.Format.FEET);
+                       parseFields(inField, Config.getUnitSet().getDefaultAltitudeFormat());
                }
        }
 
@@ -363,6 +365,25 @@ public class DataPoint
                return _photo != null || _audio != null;
        }
 
+       /**
+        * @return name of attached photo and/or audio
+        */
+       public String getMediaName()
+       {
+               String mediaName = null;
+               if (_photo != null) mediaName = _photo.getName();
+               if (_audio != null)
+               {
+                       if (mediaName == null) {
+                               mediaName = _audio.getName();
+                       }
+                       else {
+                               mediaName = mediaName + ", " + _audio.getName();
+                       }
+               }
+               return mediaName;
+       }
+
        /**
         * Interpolate a set of points between this one and the given one
         * @param inEndPoint end point of interpolation
@@ -457,6 +478,41 @@ public class DataPoint
        }
 
 
+       /**
+        * Remove all single and double quotes surrounding each value
+        * @param inValues array of values
+        */
+       private static void removeQuotes(String[] inValues)
+       {
+               if (inValues == null) {return;}
+               for (int i=0; i<inValues.length; i++)
+               {
+                       inValues[i] = removeQuotes(inValues[i]);
+               }
+       }
+
+       /**
+        * Remove any single or double quotes surrounding a value
+        * @param inValue value to modify
+        * @return modified String
+        */
+       private static String removeQuotes(String inValue)
+       {
+               if (inValue == null) {return inValue;}
+               final int len = inValue.length();
+               if (len <= 1) {return inValue;}
+               // get the first and last characters
+               final char firstChar = inValue.charAt(0);
+               final char lastChar  = inValue.charAt(len-1);
+               if (firstChar == lastChar)
+               {
+                       if (firstChar == '\"' || firstChar == '\'') {
+                               return inValue.substring(1, len-1);
+                       }
+               }
+               return inValue;
+       }
+
        /**
         * Get string for debug
         * @see java.lang.Object#toString()
index d12d98817d5c7fce86d687f12f566940a03dbbd7..83864a59e890216548957711513ae188c5ba8571 100644 (file)
@@ -1,70 +1,58 @@
 package tim.prune.data;
 
+import tim.prune.config.Config;
+
 /**
  * Class to provide distance constants and functions
  */
 public abstract class Distance
 {
-       /** distance units */
-       public enum Units
-       {
-               /** Kilometres */
-               KILOMETRES,
-               /** Miles */
-               MILES,
-               /** Metres */
-               METRES,
-               /** Feet */
-               FEET
-       }
-
        // Geographical constants
-       private static final double EARTH_RADIUS_KM = 6372.795;
-       // Conversion constants
-       private static final double CONVERT_KM_TO_MILES = 0.621371192;
+       /** Earth radius in metres */
+       private static final double EARTH_RADIUS_M = 6372795.0;
 
 
        /**
         * Convert the given angle in radians into a distance
         * @param inAngDist angular distance in radians
-        * @param inUnits desired units, eg miles or km
-        * @return distance in specified format
+        * @return distance in currently configured distance units
         */
-       public static double convertRadiansToDistance(double inAngDist, Units inUnits)
+       public static double convertRadiansToDistance(double inAngDist)
+       {
+               return convertRadiansToDistance(inAngDist, Config.getUnitSet().getDistanceUnit());
+       }
+
+       /**
+        * Convert the given angle in radians into a distance
+        * @param inAngDist angular distance in radians
+        * @param inUnit distance units
+        * @return distance in specified distance units
+        */
+       public static double convertRadiansToDistance(double inAngDist, Unit inUnit)
        {
                // Multiply by appropriate factor
-               if (inUnits == Units.MILES)
-                       return inAngDist * EARTH_RADIUS_KM * CONVERT_KM_TO_MILES;
-               else if (inUnits == Units.METRES)
-                       return inAngDist * EARTH_RADIUS_KM * 1000;
-               // default kilometres
-               return inAngDist * EARTH_RADIUS_KM;
+               return inAngDist * EARTH_RADIUS_M * inUnit.getMultFactorFromStd();
        }
 
        /**
         * Convert the given distance into an angle in radians
-        * @param inDist distance to convert
-        * @param inUnits units, eg miles or km
+        * @param inDist distance to convert in the current distance units
         * @return angular distance in radians
         */
-       public static double convertDistanceToRadians(double inDist, Units inUnits)
+       public static double convertDistanceToRadians(double inDist)
        {
-               // Divide by appropriate factor
-               if (inUnits == Units.MILES)
-                       return inDist / EARTH_RADIUS_KM / CONVERT_KM_TO_MILES;
-               else if (inUnits == Units.METRES)
-                       return inDist / EARTH_RADIUS_KM / 1000;
-               // default kilometres
-               return inDist / EARTH_RADIUS_KM;
+               return convertDistanceToRadians(inDist, Config.getUnitSet().getDistanceUnit());
        }
 
        /**
-        * Convert the given distance from metres to miles
-        * @param inMetres number of metres
-        * @return number of miles
+        * Convert the given distance into an angle in radians
+        * @param inDist distance to convert in the current distance units
+        * @param inUnit distance unit
+        * @return angular distance in radians
         */
-       public static double convertMetresToMiles(double inMetres)
+       public static double convertDistanceToRadians(double inDist, Unit inUnit)
        {
-               return inMetres / 1000.0 * CONVERT_KM_TO_MILES;
+               // Divide by appropriate factor
+               return inDist / EARTH_RADIUS_M / inUnit.getMultFactorFromStd();
        }
 }
index 6d877049e295515a7a6e895207b114f4c35b458c..d020a7ebdd5789e080e1323dd2459a8796c851cc 100644 (file)
@@ -20,10 +20,15 @@ public class Field
        public static final Field DESCRIPTION = new Field("fieldname.description", true);
        public static final Field NEW_SEGMENT = new Field("fieldname.newsegment", true);
 
+       public static final Field SPEED          = new Field("fieldname.speed", true);
+       public static final Field VERTICAL_SPEED = new Field("fieldname.verticalspeed", true);
+
        // TODO: Field for photo filename, ability to load (from text) and save (to text)
 
+       /** List of all the available fields */
        private static final Field[] ALL_AVAILABLE_FIELDS = {
                LATITUDE, LONGITUDE, ALTITUDE, TIMESTAMP, WAYPT_NAME, WAYPT_TYPE, DESCRIPTION, NEW_SEGMENT,
+               SPEED, VERTICAL_SPEED,
                new Field(I18nManager.getText("fieldname.custom"))
        };
 
index d7b4780e7cd6e32447dcaa77e10976cb73a9923f..c0f4408f3637a23e932679bd441449928ea5777e 100644 (file)
@@ -172,7 +172,7 @@ public abstract class MediaList
        public boolean hasUncorrelatedMedia()
        {
                for (MediaObject m : _media) {
-                       if (m.getDataPoint() == null) {
+                       if (m.getDataPoint() == null && m.hasTimestamp()) {
                                return true;
                        }
                }
index e028f4ab4ddc7f1050f0c9e2e3792da90c3e6c41..478618e4a5ac330795df9684600c4a54f3452e9c 100644 (file)
@@ -105,6 +105,15 @@ public abstract class MediaObject
                return _url;
        }
 
+       /**
+        * @return the full path to the media, either filename or url
+        */
+       public String getFullPath()
+       {
+               if (_file != null) return _file.getAbsolutePath();
+               return getUrl();
+       }
+
        /**
         * @return true if details are valid (might not have timestamp)
         */
diff --git a/tim/prune/data/MidpointData.java b/tim/prune/data/MidpointData.java
new file mode 100644 (file)
index 0000000..3fdebb2
--- /dev/null
@@ -0,0 +1,98 @@
+package tim.prune.data;
+
+/**
+ * Class to hold information about the mid-points between
+ * adjacent track points.  Used by the MapCanvas for creating
+ * points by dragging.
+ */
+public class MidpointData
+{
+       // track object
+       private Track _track = null;
+       // Scaled x, y values
+       private double[] _xValues = null;
+       private double[] _yValues = null;
+       // Validity flags
+       private boolean[] _valids = null;
+       // Flag to set data stale
+       private boolean _needRefresh = true;
+
+
+       /**
+        * Flag the data as needing to be updated
+        * @param inTrack track object from which to get the data
+        */
+       public void updateData(Track inTrack)
+       {
+               _track = inTrack;
+               _needRefresh = true;
+       }
+
+       /**
+        * Update the arrays of data from the track
+        */
+       private synchronized void updateData()
+       {
+               _needRefresh = false;
+               if (_track == null) return;
+               // Make arrays the right size
+               final int numPoints = _track.getNumPoints();
+               if (_xValues == null || _xValues.length != numPoints)
+               {
+                       _xValues = new double[numPoints];
+                       _yValues = new double[numPoints];
+                       _valids  = new boolean[numPoints];
+               }
+               if (numPoints <= 0) return;
+               _valids[0] = false;
+
+               // Loop over the points in the track
+               for (int i=1; i<numPoints; i++)
+               {
+                       boolean pointValid = false;
+                       DataPoint point = _track.getPoint(i);
+                       if (point != null && !point.getSegmentStart() && !point.isWaypoint())
+                       {
+                               _xValues[i] = (_track.getX(i) + _track.getX(i-1)) / 2.0;
+                               _yValues[i] = (_track.getY(i) + _track.getY(i-1)) / 2.0;
+                               pointValid = true;
+                       }
+                       _valids[i] = pointValid;
+               }
+       }
+
+       /**
+        * Find the nearest point to the specified x and y coordinates
+        * or -1 if no point is within the specified max distance
+        * @param inX x coordinate
+        * @param inY y coordinate
+        * @param inMaxDist maximum distance from selected coordinates
+        * @return index of nearest point or -1 if not found
+        */
+       public int getNearestPointIndex(double inX, double inY, double inMaxDist)
+       {
+               if (_track == null) return -1;
+               if (_needRefresh) updateData();
+               final int numPoints = _track.getNumPoints();
+               int nearestPoint = 0;
+               double nearestDist = -1.0;
+               double currDist;
+               for (int i=1; i < numPoints; i++)
+               {
+                       if (_valids[i])
+                       {
+                               currDist = Math.abs(_xValues[i] - inX) + Math.abs(_yValues[i] - inY);
+                               if (currDist < nearestDist || nearestDist < 0.0)
+                               {
+                                       nearestPoint = i;
+                                       nearestDist = currDist;
+                               }
+                       }
+               }
+               // Check whether it's within required distance
+               if (nearestDist > inMaxDist && inMaxDist > 0.0) {
+                       return -1;
+               }
+               return nearestPoint;
+       }
+}
index b3b94f3d2b6ce8751c78830fc5dad4ebe85c1b64..180f3a5aef74e131d0c9bab9649310b4e840ef5e 100644 (file)
@@ -84,7 +84,7 @@ public class PointScaler
                        DataPoint p2 = new DataPoint(new Latitude(latRange.getMaximum(), Coordinate.FORMAT_DEG),
                                new Longitude(_lonMedian, Coordinate.FORMAT_DEG), null);
                        double horizDist = Distance.convertRadiansToDistance(
-                               DataPoint.calculateRadiansBetween(p1, p2), Distance.Units.METRES);
+                               DataPoint.calculateRadiansBetween(p1, p2), UnitSetLibrary.UNITS_METRES); // both in m
                        _altFactor = 1.0 / horizDist;
 
                        // create new arrays for scaled values
index d13388d28e4c0da1911c6923eb6831da08dbce04..e7d95ba944bf19cff22f8d77a93f0e7bf0a63d73 100644 (file)
@@ -72,6 +72,7 @@ public class RecentFile
        public boolean isSameFile(RecentFile inOther)
        {
                return inOther != null && isValid() && inOther.isValid()
-                       && _file.equals(inOther._file);
+                       && (_file.equals(inOther._file) || _file.getAbsolutePath().equals(inOther._file.getAbsolutePath()));
+               // Note that the file.equals should be sufficient but sometimes it returns false even if the absolute paths are identical
        }
 }
index 81ab64ce7c6cc42dc7df15d788e06a90df9bb31f..f5c41ffd91f6015d8d4762e546fe8385c5dadc8e 100644 (file)
@@ -16,9 +16,7 @@ public class Selection
        private int _startIndex = -1, _endIndex = -1;
        private int _currentPhotoIndex = -1;
        private int _currentAudioIndex = -1;
-       private IntegerRange _altitudeRange = null;
-       private int _climb = -1, _descent = -1;
-       private Altitude.Format _altitudeFormat = Altitude.Format.NO_FORMAT;
+       private AltitudeRange _altitudeRange = null;
        private long _totalSeconds = 0L, _movingSeconds = 0L;
        private double _angDistance = -1.0, _angMovingDistance = -1.0;
        private int _numSegments = 0;
@@ -66,7 +64,6 @@ public class Selection
         */
        private void recalculate()
        {
-               _altitudeFormat = Altitude.Format.NO_FORMAT;
                _numSegments = 0;
                final int numPoints = _track.getNumPoints();
                // Recheck if the number of points has changed
@@ -76,18 +73,14 @@ public class Selection
                }
                if (numPoints > 0 && hasRangeSelected())
                {
-                       _altitudeRange = new IntegerRange();
-                       _climb = 0;
-                       _descent = 0;
+                       _altitudeRange = new AltitudeRange();
                        Altitude altitude = null;
                        Timestamp time = null, startTime = null, endTime = null;
                        Timestamp previousTime = null;
                        DataPoint lastPoint = null, currPoint = null;
                        _angDistance = 0.0; _angMovingDistance = 0.0;
                        _totalSeconds = 0L; _movingSeconds = 0L;
-                       int altValue = 0;
-                       int lastAltValue = 0;
-                       boolean foundAlt = false;
+                       // Loop over points in selection
                        for (int i=_startIndex; i<=_endIndex; i++)
                        {
                                currPoint = _track.getPoint(i);
@@ -95,19 +88,7 @@ public class Selection
                                // Ignore waypoints in altitude calculations
                                if (!currPoint.isWaypoint() && altitude.isValid())
                                {
-                                       altValue = altitude.getValue(_altitudeFormat);
-                                       if (_altitudeFormat == Altitude.Format.NO_FORMAT)
-                                               _altitudeFormat = altitude.getFormat();
-                                       _altitudeRange.addValue(altValue);
-                                       if (foundAlt)
-                                       {
-                                               if (altValue > lastAltValue)
-                                                       _climb += (altValue - lastAltValue);
-                                               else
-                                                       _descent += (lastAltValue - altValue);
-                                       }
-                                       lastAltValue = altValue;
-                                       foundAlt = true;
+                                       _altitudeRange.addValue(altitude);
                                }
                                // Store the first and last timestamp in the range
                                time = currPoint.getTimestamp();
@@ -167,44 +148,16 @@ public class Selection
                return _endIndex;
        }
 
-
-       /**
-        * @return the altitude format, ie feet or metres
-        */
-       public Altitude.Format getAltitudeFormat()
-       {
-               return _altitudeFormat;
-       }
-
        /**
         * @return altitude range
         */
-       public IntegerRange getAltitudeRange()
+       public AltitudeRange getAltitudeRange()
        {
                if (!_valid) recalculate();
                return _altitudeRange;
        }
 
 
-       /**
-        * @return climb
-        */
-       public int getClimb()
-       {
-               if (!_valid) recalculate();
-               return _climb;
-       }
-
-       /**
-        * @return descent
-        */
-       public int getDescent()
-       {
-               if (!_valid) recalculate();
-               return _descent;
-       }
-
-
        /**
         * @return number of seconds spanned by selection
         */
@@ -224,21 +177,19 @@ public class Selection
        }
 
        /**
-        * @param inUnits distance units to use, from class Distance
         * @return distance of Selection in specified units
         */
-       public double getDistance(Distance.Units inUnits)
+       public double getDistance()
        {
-               return Distance.convertRadiansToDistance(_angDistance, inUnits);
+               return Distance.convertRadiansToDistance(_angDistance);
        }
 
        /**
-        * @param inUnits distance units to use, from class Distance
-        * @return moving distance of Selection in specified units
+        * @return moving distance of Selection in current units
         */
-       public double getMovingDistance(Distance.Units inUnits)
+       public double getMovingDistance()
        {
-               return Distance.convertRadiansToDistance(_angMovingDistance, inUnits);
+               return Distance.convertRadiansToDistance(_angMovingDistance);
        }
 
        /**
diff --git a/tim/prune/data/SpeedCalculator.java b/tim/prune/data/SpeedCalculator.java
new file mode 100644 (file)
index 0000000..d83a7bd
--- /dev/null
@@ -0,0 +1,204 @@
+package tim.prune.data;
+
+import tim.prune.config.Config;
+
+/**
+ * Abstract class to hold static calculation functions
+ * for speed (and vertical speed)
+ */
+public abstract class SpeedCalculator
+{
+       /**
+        * Calculate the speed value of the track at the specified index
+        * @param inTrack track object
+        * @param inIndex index of point to calculate speed for
+        * @param inValue object in which to place result of calculation
+        */
+       public static void calculateSpeed(Track inTrack, int inIndex, SpeedValue inValue)
+       {
+               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;}
+               boolean pointHasSpeed = false;
+               double  speedValue = 0.0;
+
+               // First, see if point has a speed value already
+               // FIXME: How do we know what units this speed is in?  m/s or mph or km/h or what?
+               String speedStr = point.getFieldValue(Field.SPEED);
+               try {
+                       speedValue = Double.parseDouble(speedStr);
+                       pointHasSpeed = true;
+               }
+               catch (Exception e) {} // ignore, leave pointHasSpeed false
+
+               // otherwise, see if we can calculate it from the timestamps
+               if (!pointHasSpeed && point.hasTimestamp() && !point.isWaypoint())
+               {
+                       double totalRadians = 0.0;
+                       int index = inIndex-1;
+                       DataPoint p = null;
+                       DataPoint q = point;
+                       Timestamp earlyStamp = point.getTimestamp();
+                       boolean stop = false;
+
+                       // Count backwards until timestamp earlier than now; total distances back to this point
+                       if (!point.getSegmentStart())
+                       {
+                               do
+                               {
+                                       p = inTrack.getPoint(index);
+                                       boolean timeOk = p != null && p.hasTimestamp() && !p.getTimestamp().isAfter(point.getTimestamp());
+                                       boolean pValid = timeOk && !p.isWaypoint();
+                                       if (pValid) {
+                                               totalRadians += DataPoint.calculateRadiansBetween(p, q);
+                                               earlyStamp = p.getTimestamp();
+                                       }
+                                       stop = (p == null) || (p.hasTimestamp() && !p.getTimestamp().isEqual(point.getTimestamp())
+                                               || p.getSegmentStart());
+                                       index--;
+                                       if (p != null && !p.isWaypoint()) {
+                                               q = p;
+                                       }
+                               }
+                               while (!stop);
+                       }
+                       // Count forwards until timestamp later than now; total distances forward to this point
+                       Timestamp lateStamp = point.getTimestamp();
+                       q = point;
+                       index = inIndex+1;
+                       do
+                       {
+                               p = inTrack.getPoint(index);
+                               boolean timeOk = p != null && p.hasTimestamp() && !p.getTimestamp().isBefore(point.getTimestamp());
+                               boolean pValid = timeOk && !p.isWaypoint() && !p.getSegmentStart();
+                               if (pValid) {
+                                       totalRadians += DataPoint.calculateRadiansBetween(p, q);
+                                       lateStamp = p.getTimestamp();
+                               }
+                               stop = (p == null) || (p.hasTimestamp() && !p.getTimestamp().isEqual(point.getTimestamp())
+                                       || p.getSegmentStart());
+                               index++;
+                               if (p != null && !p.isWaypoint()) {
+                                       q = p;
+                               }
+                       }
+                       while (!stop);
+
+                       // See if we've managed to get a time range of at least a second
+                       long milliseconds = lateStamp.getMillisecondsSince(earlyStamp);
+                       if (milliseconds >= 1000L)
+                       {
+                               double dist = Distance.convertRadiansToDistance(totalRadians);
+                               // Store the value and maintain max and min values
+                               speedValue = dist / milliseconds * 1000.0 * 60.0 * 60.0; // convert from per millisec to per hour
+                               pointHasSpeed = true;
+                       }
+               }
+               // Did we get a value?
+               if (pointHasSpeed)
+               {
+                       inValue.setValue(speedValue);
+               }
+               // otherwise, just leave value as invalid
+       }
+
+
+       /**
+        * Calculate the vertical speed value of the track at the specified index
+        * @param inTrack track object
+        * @param inIndex index of point to calculate speed for
+        * @param inValue object in which to place the result of calculation
+        */
+       public static void calculateVerticalSpeed(Track inTrack, int inIndex, SpeedValue inValue)
+       {
+               if (inTrack == null || inIndex < 0 || inValue == null) {
+                       System.err.println("Cannot calculate vert speed for index " + inIndex);
+                       return;
+               }
+               inValue.setInvalid();
+
+               DataPoint point = inTrack.getPoint(inIndex);
+               boolean pointHasSpeed = false;
+               double  speedValue = 0.0;
+
+               // First, see if point has a speed value already
+               if (point != null)
+               {
+                       // FIXME: Can we assume m/s or ft/s?
+                       String speedStr = point.getFieldValue(Field.VERTICAL_SPEED);
+                       try {
+                               speedValue = Double.parseDouble(speedStr);
+                               pointHasSpeed = true;
+                       }
+                       catch (Exception e) {} // ignore, leave pointHasSpeed false
+               }
+               // otherwise, see if we can calculate it from the heights and timestamps
+               if (!pointHasSpeed
+                       && point != null && point.hasTimestamp() && point.hasAltitude() && !point.isWaypoint())
+               {
+                       int index = inIndex-1;
+                       DataPoint p = null;
+                       Timestamp earlyStamp = point.getTimestamp();
+                       Altitude firstAlt = point.getAltitude();
+                       boolean stop = false;
+
+                       // Count backwards until timestamp earlier than now
+                       if (!point.getSegmentStart())
+                       {
+                               do
+                               {
+                                       p = inTrack.getPoint(index);
+                                       boolean timeOk = p != null && p.hasTimestamp() && !p.getTimestamp().isAfter(point.getTimestamp());
+                                       boolean pValid = timeOk && !p.isWaypoint();
+                                       if (pValid) {
+                                               earlyStamp = p.getTimestamp();
+                                               if (p.hasAltitude()) firstAlt = p.getAltitude();
+                                       }
+                                       stop = (p == null) || (p.hasTimestamp() && !p.getTimestamp().isEqual(point.getTimestamp())
+                                               || p.getSegmentStart());
+                                       index--;
+                               }
+                               while (!stop);
+                       }
+
+                       // Count forwards until timestamp later than now
+                       Timestamp lateStamp = point.getTimestamp();
+                       Altitude lastAlt = point.getAltitude();
+                       index = inIndex+1;
+                       do
+                       {
+                               p = inTrack.getPoint(index);
+                               boolean timeOk = p != null && p.hasTimestamp() && !p.getTimestamp().isBefore(point.getTimestamp());
+                               boolean pValid = timeOk && !p.isWaypoint() && !p.getSegmentStart();
+                               if (pValid) {
+                                       lateStamp = p.getTimestamp();
+                                       if (p.hasAltitude()) lastAlt = p.getAltitude();
+                               }
+                               stop = (p == null) || (p.hasTimestamp() && !p.getTimestamp().isEqual(point.getTimestamp())
+                                       || p.getSegmentStart());
+                               index++;
+                       }
+                       while (!stop);
+
+                       // See if we've managed to get a non-zero time range
+                       long milliseconds = lateStamp.getMillisecondsSince(earlyStamp);
+                       if (milliseconds >= 1000L)
+                       {
+                               double altDiff = (lastAlt.getMetricValue() - firstAlt.getMetricValue())
+                                * Config.getUnitSet().getVerticalSpeedUnit().getMultFactorFromStd();
+                               speedValue = altDiff / milliseconds * 1000.0; // units are feet/sec or metres/sec
+                               pointHasSpeed = true;
+                       }
+               }
+               // Check whether we got a value from either method
+               if (pointHasSpeed)
+               {
+                       inValue.setValue(speedValue);
+               }
+       }
+}
diff --git a/tim/prune/data/SpeedValue.java b/tim/prune/data/SpeedValue.java
new file mode 100644 (file)
index 0000000..45ed28e
--- /dev/null
@@ -0,0 +1,45 @@
+package tim.prune.data;
+
+/**
+ * Holder for a speed value, including a boolean valid flag
+ */
+public class SpeedValue
+{
+       /** Valid flag */
+       private boolean _valid = false;
+       /** Value as a double, using current units */
+       private double  _value = 0.0;
+
+
+       /**
+        * Set the flag to invalid
+        */
+       public void setInvalid()
+       {
+               _valid = false;
+               _value = 0.0;
+       }
+
+       /**
+        * @param inValue speed value to set
+        */
+       public void setValue(double inValue)
+       {
+               _valid = true;
+               _value = inValue;
+       }
+
+       /**
+        * @return true if value is valid
+        */
+       public boolean isValid() {
+               return _valid;
+       }
+
+       /**
+        * @return numeric value
+        */
+       public double getValue() {
+               return _value;
+       }
+}
index e7a0ab90e3af6e5625bbdc833810e2c60fb5e92b..477a7227125a0a82a7a4fcd4a7c899d2352bc03a 100644 (file)
@@ -15,7 +15,7 @@ import java.util.regex.Pattern;
 public class Timestamp
 {
        private boolean _valid = false;
-       private long _seconds = 0L;
+       private long _milliseconds = 0L;
        private String _text = null;
        private String _timeText = null;
 
@@ -25,6 +25,8 @@ public class Timestamp
        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;
+       private static final Pattern ISO8601_FRACTIONAL_PATTERN
+               = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{1,3})Z?");
        private static final Pattern GENERAL_TIMESTAMP_PATTERN
                = Pattern.compile("(\\d{4})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})");
        private static long SECS_SINCE_1970 = 0L;
@@ -41,6 +43,27 @@ public class Timestamp
        /** Specifies ISO 8601 timestamp format */
        public static final int FORMAT_ISO_8601 = 2;
 
+       /** Identifier for the parsing strategy to use */
+       private enum ParseType
+       {
+               NONE,
+               ISO8601_FRACTIONAL,
+               LONG,
+               FIXED_FORMAT0,
+               FIXED_FORMAT1,
+               FIXED_FORMAT2,
+               FIXED_FORMAT3,
+               FIXED_FORMAT4,
+               FIXED_FORMAT5,
+               FIXED_FORMAT6,
+               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};
+
        // Static block to initialise offsets
        static
        {
@@ -69,51 +92,116 @@ public class Timestamp
         */
        public Timestamp(String inString)
        {
-               // TODO: Does it really help to store timestamps in seconds rather than ms?
+               _valid = false;
                if (inString != null && !inString.equals(""))
                {
-                       // Try to parse into a long
-                       try
+                       // Try each of the parse types in turn
+                       for (ParseType type : ALL_PARSE_TYPES)
                        {
-                               long rawValue = Long.parseLong(inString.trim());
-                               _seconds = getSeconds(rawValue);
-                               _valid = true;
+                               if (parseString(inString, type))
+                               {
+                                       ALL_PARSE_TYPES[0] = type;
+                                       _valid = true;
+                                       return;
+                               }
                        }
-                       catch (NumberFormatException nfe)
-                       {
-                               // String is not a long, so try a date/time string instead
-                               // try each of the date formatters in turn
-                               Date date = null;
-                               for (int i=0; i<ALL_DATE_FORMATS.length && !_valid; i++)
+               }
+       }
+
+       /**
+        * Try to parse the given string in the specified way
+        * @param inString String to parse
+        * @param inType parse type to use
+        * @return true if successful
+        */
+       private boolean parseString(String inString, ParseType inType)
+       {
+               if (inString == null || inString.equals("")) {
+                       return false;
+               }
+               switch (inType)
+               {
+                       case NONE: return false;
+                       case LONG:
+                               // Try to parse into a long
+                               try
                                {
-                                       try
-                                       {
-                                               date = ALL_DATE_FORMATS[i].parse(inString);
-                                               CALENDAR.setTime(date);
-                                               _seconds = CALENDAR.getTimeInMillis() / 1000L;
-                                               _valid = true;
+                                       long rawValue = Long.parseLong(inString.trim());
+                                       _milliseconds = getMilliseconds(rawValue);
+                                       return true;
+                               }
+                               catch (NumberFormatException nfe)
+                               {}
+                               break;
+
+                       case ISO8601_FRACTIONAL:
+                               final Matcher fmatcher = ISO8601_FRACTIONAL_PATTERN.matcher(inString);
+                               if (fmatcher.matches())
+                               {
+                                       try {
+                                               _milliseconds = getMilliseconds(Integer.parseInt(fmatcher.group(1)), // year
+                                                       Integer.parseInt(fmatcher.group(2)), // month
+                                                       Integer.parseInt(fmatcher.group(3)), // day
+                                                       Integer.parseInt(fmatcher.group(4)), // hour
+                                                       Integer.parseInt(fmatcher.group(5)), // minute
+                                                       Integer.parseInt(fmatcher.group(6)), // second
+                                                       fmatcher.group(7));                  // fractional seconds
+                                               return true;
                                        }
-                                       catch (ParseException e) {}
+                                       catch (NumberFormatException nfe) {}
                                }
-                               if (!_valid && inString.length() == 19)
+                               break;
+
+                       case FIXED_FORMAT0: return parseString(inString, ALL_DATE_FORMATS[0]);
+                       case FIXED_FORMAT1: return parseString(inString, ALL_DATE_FORMATS[1]);
+                       case FIXED_FORMAT2: return parseString(inString, ALL_DATE_FORMATS[2]);
+                       case FIXED_FORMAT3: return parseString(inString, ALL_DATE_FORMATS[3]);
+                       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 GENERAL_STRING:
+                               if (inString.length() == 19)
                                {
                                        final Matcher matcher = GENERAL_TIMESTAMP_PATTERN.matcher(inString);
                                        if (matcher.matches())
                                        {
                                                try {
-                                                       _seconds = getSeconds(Integer.parseInt(matcher.group(1)),
+                                                       _milliseconds = getMilliseconds(Integer.parseInt(matcher.group(1)),
                                                                Integer.parseInt(matcher.group(2)),
                                                                Integer.parseInt(matcher.group(3)),
                                                                Integer.parseInt(matcher.group(4)),
                                                                Integer.parseInt(matcher.group(5)),
-                                                               Integer.parseInt(matcher.group(6)));
-                                                       _valid = true;
+                                                               Integer.parseInt(matcher.group(6)),
+                                                               null); // no fractions of a second
+                                                       return true;
                                                }
                                                catch (NumberFormatException nfe2) {} // parse shouldn't fail if matcher matched
                                        }
                                }
-                       }
+                               return false;
                }
+               return false;
+       }
+
+
+       /**
+        * Try to parse the given string with the given date format
+        * @param inString String to parse
+        * @param inDateFormat Date format to use
+        * @return true if successful
+        */
+       private boolean parseString(String inString, DateFormat inDateFormat)
+       {
+               try
+               {
+                       Date date = inDateFormat.parse(inString);
+                       CALENDAR.setTime(date);
+                       _milliseconds = CALENDAR.getTimeInMillis();
+                       return true;
+               }
+               catch (ParseException e) {}
+               return false;
        }
 
 
@@ -128,7 +216,7 @@ public class Timestamp
         */
        public Timestamp(int inYear, int inMonth, int inDay, int inHour, int inMinute, int inSecond)
        {
-               _seconds = getSeconds(inYear, inMonth, inDay, inHour, inMinute, inSecond);
+               _milliseconds = getMilliseconds(inYear, inMonth, inDay, inHour, inMinute, inSecond, null);
                _valid = true;
        }
 
@@ -139,22 +227,24 @@ public class Timestamp
         */
        public Timestamp(long inMillis)
        {
-               _seconds = inMillis / 1000;
+               _milliseconds = inMillis;
                _valid = true;
        }
 
 
        /**
-        * Convert the given timestamp parameters into a number of seconds
+        * Convert the given timestamp parameters into a number of milliseconds
         * @param inYear year
         * @param inMonth month, beginning with 1
         * @param inDay day of month, beginning with 1
         * @param inHour hour of day, 0-24
         * @param inMinute minute
         * @param inSecond seconds
-        * @return number of seconds
+        * @param inFraction fractions of a second
+        * @return number of milliseconds
         */
-       private static long getSeconds(int inYear, int inMonth, int inDay, int inHour, int inMinute, int inSecond)
+       private static long getMilliseconds(int inYear, int inMonth, int inDay,
+               int inHour, int inMinute, int inSecond, String inFraction)
        {
                Calendar cal = Calendar.getInstance();
                cal.set(Calendar.YEAR, inYear);
@@ -163,16 +253,30 @@ public class Timestamp
                cal.set(Calendar.HOUR_OF_DAY, inHour);
                cal.set(Calendar.MINUTE, inMinute);
                cal.set(Calendar.SECOND, inSecond);
-               cal.set(Calendar.MILLISECOND, 0);
-               return cal.getTimeInMillis() / 1000;
+               int millis = 0;
+               if (inFraction != null)
+               {
+                       try {
+                               int frac = Integer.parseInt(inFraction);
+                               final int fracLen = inFraction.length();
+                               switch (fracLen) {
+                                       case 1: millis = frac * 100; break;
+                                       case 2: millis = frac * 10;  break;
+                                       case 3: millis = frac;       break;
+                               }
+                       }
+                       catch (NumberFormatException nfe) {} // ignore errors, millis stay at 0
+               }
+               cal.set(Calendar.MILLISECOND, millis);
+               return cal.getTimeInMillis();
        }
 
        /**
-        * Convert the given long parameters into a number of seconds
+        * Convert the given long parameters into a number of millisseconds
         * @param inRawValue long value representing seconds / milliseconds
-        * @return number of seconds
+        * @return number of milliseconds
         */
-       private static long getSeconds(long inRawValue)
+       private static long getMilliseconds(long inRawValue)
        {
                // check for each format possibility and pick nearest
                long diff1 = Math.abs(SECS_SINCE_1970 - inRawValue);
@@ -182,28 +286,28 @@ public class Timestamp
 
                // Start off with "seconds since 1970" format
                long smallestDiff = diff1;
-               long seconds = inRawValue;
+               long millis = inRawValue * 1000;
                // Now check millis since 1970
                if (diff2 < smallestDiff)
                {
                        // milliseconds since 1970
-                       seconds = inRawValue / 1000L;
+                       millis = inRawValue;
                        smallestDiff = diff2;
                }
                // Now millis since 1990
                if (diff3 < smallestDiff)
                {
                        // milliseconds since 1990
-                       seconds = inRawValue / 1000L + TWENTY_YEARS_IN_SECS;
+                       millis = inRawValue + TWENTY_YEARS_IN_SECS * 1000L;
                        smallestDiff = diff3;
                }
                // Lastly, check gartrip offset
                if (diff4 < smallestDiff)
                {
                        // seconds since gartrip offset
-                       seconds = inRawValue + GARTRIP_OFFSET;
+                       millis = (inRawValue + GARTRIP_OFFSET) * 1000L;
                }
-               return seconds;
+               return millis;
        }
 
        /**
@@ -216,11 +320,11 @@ public class Timestamp
 
        /**
         * @param inOther other Timestamp
-        * @return true if this one is after the other
+        * @return true if this one is at least a second after the other
         */
        public boolean isAfter(Timestamp inOther)
        {
-               return _seconds > inOther._seconds;
+               return getSecondsSince(inOther) > 0L;
        }
 
        /**
@@ -230,7 +334,35 @@ public class Timestamp
         */
        public long getSecondsSince(Timestamp inOther)
        {
-               return _seconds - inOther._seconds;
+               return (_milliseconds - inOther._milliseconds) / 1000L;
+       }
+
+       /**
+        * Calculate the difference between two Timestamps in milliseconds
+        * @param inOther other, earlier Timestamp
+        * @return number of millisseconds since other timestamp
+        */
+       public long getMillisecondsSince(Timestamp inOther)
+       {
+               return _milliseconds - inOther._milliseconds;
+       }
+
+       /**
+        * @param inOther other timestamp to compare
+        * @return true if they're equal to the nearest second
+        */
+       public boolean isEqual(Timestamp inOther)
+       {
+               return getSecondsSince(inOther) == 0L;
+       }
+
+       /**
+        * @param inOther other Timestamp
+        * @return true if this one is before the other
+        */
+       public boolean isBefore(Timestamp inOther)
+       {
+               return getSecondsSince(inOther) < 0L;
        }
 
        /**
@@ -239,7 +371,7 @@ public class Timestamp
         */
        public void addOffset(long inOffset)
        {
-               _seconds += inOffset;
+               _milliseconds += (inOffset * 1000L);
                _text = null;
        }
 
@@ -260,7 +392,7 @@ public class Timestamp
         */
        public Timestamp createPlusOffset(long inSeconds)
        {
-               return new Timestamp((_seconds + inSeconds) * 1000L);
+               return new Timestamp(_milliseconds + (inSeconds * 1000L));
        }
 
 
@@ -271,7 +403,7 @@ public class Timestamp
         */
        public Timestamp createMinusOffset(TimeDifference inOffset)
        {
-               return new Timestamp((_seconds - inOffset.getTotalSeconds()) * 1000L);
+               return new Timestamp(_milliseconds - (inOffset.getTotalSeconds() * 1000L));
        }
 
 
@@ -321,7 +453,7 @@ public class Timestamp
         */
        private String format(DateFormat inFormat)
        {
-               CALENDAR.setTimeInMillis(_seconds * 1000L);
+               CALENDAR.setTimeInMillis(_milliseconds);
                return inFormat.format(CALENDAR.getTime());
        }
 
@@ -331,7 +463,7 @@ public class Timestamp
        public Calendar getCalendar()
        {
                Calendar cal = Calendar.getInstance();
-               cal.setTimeInMillis(_seconds * 1000L);
+               cal.setTimeInMillis(_milliseconds);
                return cal;
        }
 }
index 63cd01faaf488028085a6712c5648ad9ffa8ea1d..b28fd3642091cdc485e1a9130a72c3f2ecd14dac 100644 (file)
@@ -3,7 +3,6 @@ package tim.prune.data;
 import java.util.List;
 
 import tim.prune.UpdateMessageBroker;
-import tim.prune.config.Config;
 import tim.prune.function.edit.FieldEdit;
 import tim.prune.function.edit.FieldEditList;
 import tim.prune.gui.map.MapUtils;
@@ -27,7 +26,6 @@ public class Track
        // Master field list
        private FieldList _masterFieldList = null;
        // variable ranges
-       private AltitudeRange _altitudeRange = null;
        private DoubleRange _latRange = null, _longRange = null;
        private DoubleRange _xRange = null, _yRange = null;
 
@@ -580,7 +578,7 @@ public class Track
                double latitudeDiff = 0.0, longitudeDiff = 0.0;
                double totalAltitude = 0;
                int numAltitudes = 0;
-               Altitude.Format altFormat = Config.getConfigBoolean(Config.KEY_METRIC_UNITS)?Altitude.Format.METRES:Altitude.Format.FEET;
+               Altitude.Format altFormat = Altitude.Format.NO_FORMAT;
                // loop between start and end points
                for (int i=inStartIndex; i<= inEndIndex; i++)
                {
@@ -589,6 +587,9 @@ public class Track
                        longitudeDiff += (currPoint.getLongitude().getDouble() - firstLongitude);
                        if (currPoint.hasAltitude()) {
                                totalAltitude += currPoint.getAltitude().getValue(altFormat);
+                               // Use altitude format of first valid altitude
+                               if (altFormat == Altitude.Format.NO_FORMAT)
+                                       altFormat = currPoint.getAltitude().getFormat();
                                numAltitudes++;
                        }
                }
@@ -643,16 +644,6 @@ public class Track
                return null;
        }
 
-
-       /**
-        * @return altitude range of points as AltitudeRange object
-        */
-       public AltitudeRange getAltitudeRange()
-       {
-               if (!_scaled) scalePoints();
-               return _altitudeRange;
-       }
-
        /**
         * @return the number of (valid) points in the track
         */
@@ -873,10 +864,9 @@ public class Track
         */
        private void scalePoints()
        {
-               // Loop through all points in track, to see limits of lat, long and altitude
+               // Loop through all points in track, to see limits of lat, long
                _longRange = new DoubleRange();
                _latRange = new DoubleRange();
-               _altitudeRange = new AltitudeRange();
                int p;
                _hasWaypoint = false; _hasTrackpoint = false;
                for (p=0; p < getNumPoints(); p++)
@@ -886,10 +876,6 @@ public class Track
                        {
                                _longRange.addValue(point.getLongitude().getDouble());
                                _latRange.addValue(point.getLatitude().getDouble());
-                               if (point.getAltitude().isValid())
-                               {
-                                       _altitudeRange.addValue(point.getAltitude());
-                               }
                                if (point.isWaypoint())
                                        _hasWaypoint = true;
                                else
@@ -930,16 +916,21 @@ public class Track
        {
                int nearestPoint = 0;
                double nearestDist = -1.0;
-               double currDist;
+               double mDist, yDist;
                for (int i=0; i < getNumPoints(); i++)
                {
                        if (!inJustTrackPoints || !_dataPoints[i].isWaypoint())
                        {
-                               currDist = Math.abs(_xValues[i] - inX) + Math.abs(_yValues[i] - inY);
-                               if (currDist < nearestDist || nearestDist < 0.0)
+                               yDist = Math.abs(_yValues[i] - inY);
+                               if (yDist < nearestDist || nearestDist < 0.0)
                                {
-                                       nearestPoint = i;
-                                       nearestDist = currDist;
+                                       // y dist is within range, so check x too
+                                       mDist = yDist + getMinXDist(_xValues[i] - inX);
+                                       if (mDist < nearestDist || nearestDist < 0.0)
+                                       {
+                                               nearestPoint = i;
+                                               nearestDist = mDist;
+                                       }
                                }
                        }
                }
@@ -951,6 +942,16 @@ public class Track
                return nearestPoint;
        }
 
+       /**
+        * @param inX x value of point
+        * @return minimum wrapped value
+        */
+       private static final double getMinXDist(double inX)
+       {
+               // TODO: Can use some kind of floor here?
+               return Math.min(Math.min(Math.abs(inX), Math.abs(inX-1.0)), Math.abs(inX+1.0));
+       }
+
        /**
         * Get the next track point starting from the given index
         * @param inStartIndex index to start looking from
index 58344142d390faf734df76edbf8fc557d5649c8e..7bcbc641e75fcae730f07178d5ad93637486145c 100644 (file)
@@ -184,21 +184,6 @@ public class TrackInfo
                return numAudiosAdded;
        }
 
-       /**
-        * Delete the currently selected range of points
-        * @return true if successful
-        */
-       public boolean deleteRange()
-       {
-               int startSel = _selection.getStart();
-               int endSel = _selection.getEnd();
-               boolean answer = _track.deleteRange(startSel, endSel);
-               // clear range selection
-               _selection.modifyRangeDeleted();
-               return answer;
-       }
-
-
        /**
         * Delete the currently selected point
         * @return true if point deleted
@@ -335,20 +320,6 @@ public class TrackInfo
                return true;
        }
 
-       /**
-        * Interpolate extra points between two selected ones
-        * @param inNumPoints num points to insert
-        * @return true if successful
-        */
-       public boolean interpolate(int inNumPoints)
-       {
-               boolean success = _track.interpolate(_selection.getStart(), inNumPoints);
-               if (success) {
-                       _selection.selectRangeEnd(_selection.getEnd() + inNumPoints);
-               }
-               return success;
-       }
-
 
        /**
         * Average selected points to create a new one
@@ -453,11 +424,13 @@ public class TrackInfo
                // Has the new point got an audio clip?
                DataPoint selectedPoint = _track.getPoint(pointIndex);
                int audioIndex = _selection.getCurrentAudioIndex();
-               if (selectedPoint != null) {
-                       if (selectedPoint.getAudio() != null) audioIndex = _audioList.getAudioIndex(selectedPoint.getAudio());
+               if (selectedPoint != null && selectedPoint.getAudio() != null) {
+                       // New point has an audio, so select it
+                       audioIndex = _audioList.getAudioIndex(selectedPoint.getAudio());
                }
-               else {
-                       if (selectedPoint != currPoint && currPoint.getAudio() != null) {audioIndex = -1;}
+               else if (currPoint != null && selectedPoint != currPoint && currPoint.getAudio() != null) {
+                       // Old point had an audio, so deselect it
+                       audioIndex = -1;
                }
                // give to selection object
                _selection.selectPointPhotoAudio(pointIndex, inPhotoIndex, audioIndex);
@@ -496,11 +469,13 @@ public class TrackInfo
                // Has the new point got a photo?
                DataPoint selectedPoint = _track.getPoint(pointIndex);
                int photoIndex = _selection.getCurrentPhotoIndex();
-               if (selectedPoint != null) {
-                       if (selectedPoint.getPhoto() != null) photoIndex = _photoList.getPhotoIndex(selectedPoint.getPhoto());
+               if (selectedPoint != null && selectedPoint.getPhoto() != null) {
+                       // New point has a photo, so select it
+                       photoIndex = _photoList.getPhotoIndex(selectedPoint.getPhoto());
                }
-               else {
-                       if (selectedPoint != currPoint && currPoint.getPhoto() != null) {photoIndex = -1;}
+               else if (currPoint != null && selectedPoint != currPoint && currPoint.getPhoto() != null) {
+                       // Old point had a photo, so deselect it
+                       photoIndex = -1;
                }
                // give to selection object
                _selection.selectPointPhotoAudio(pointIndex, photoIndex, inAudioIndex);
diff --git a/tim/prune/data/Unit.java b/tim/prune/data/Unit.java
new file mode 100644 (file)
index 0000000..b4e82e3
--- /dev/null
@@ -0,0 +1,75 @@
+package tim.prune.data;
+
+/**
+ * Class to represent a single distance or speed unit
+ * such as kilometres, mph, feet etc
+ */
+public class Unit
+{
+       private String _nameKey = null;
+       private double _multFactorFromStd = 1.0;
+       private boolean _isStandard = false;
+
+       /**
+        * Unit constructor
+        * @param inNameKey name key
+        * @param inMultFactor multiplication factor from standard units
+        */
+       public Unit(String inNameKey, double inMultFactor)
+       {
+               _nameKey = inNameKey;
+               _multFactorFromStd = inMultFactor;
+               _isStandard = false;
+       }
+
+       /**
+        * Unit constructor for standard unit
+        * @param inNameKey name key
+        */
+       public Unit(String inNameKey)
+       {
+               _nameKey = inNameKey;
+               _multFactorFromStd = 1.0;
+               _isStandard = true;
+       }
+
+       /**
+        * Unit constructor
+        * @param inParent parent unit
+        * @param inSuffix suffix to name key
+        */
+       public Unit(Unit inParent, String inSuffix)
+       {
+               _nameKey = inParent._nameKey + inSuffix;
+               _multFactorFromStd = inParent._multFactorFromStd;
+               _isStandard = inParent._isStandard;
+       }
+
+       /**
+        * @return name key
+        */
+       public String getNameKey() {
+               return "units." + _nameKey;
+       }
+
+       /**
+        * @return shortname key
+        */
+       public String getShortnameKey() {
+               return getNameKey() + ".short";
+       }
+
+       /**
+        * @return multiplication factor from standard units
+        */
+       public double getMultFactorFromStd() {
+               return _multFactorFromStd;
+       }
+
+       /**
+        * @return true if this is the standard unit (mult factor 1.0)
+        */
+       public boolean isStandard() {
+               return _isStandard;
+       }
+}
diff --git a/tim/prune/data/UnitSet.java b/tim/prune/data/UnitSet.java
new file mode 100644 (file)
index 0000000..bd53e1b
--- /dev/null
@@ -0,0 +1,74 @@
+package tim.prune.data;
+
+/**
+ * Class to hold a set of units for distance, altitude and speed
+ */
+public class UnitSet
+{
+       private String _nameKey = null;
+       private Unit _distanceUnit = null;
+       private Unit _speedUnit = null;
+       private Unit _altitudeUnit = null;
+       private Unit _vertSpeedUnit = null;
+       private Altitude.Format _defaultAltitudeFormat = Altitude.Format.METRES;
+
+       /**
+        * Constructor
+        * @param inNameKey name key
+        * @param inDistanceUnit distance unit
+        * @param inAltitudeUnit altitude unit
+        * @param inAltitudeFormat default altitude format
+        */
+       public UnitSet(String inNameKey, Unit inDistanceUnit,
+               Unit inAltitudeUnit, Altitude.Format inAltitudeFormat)
+       {
+               _nameKey = inNameKey;
+               _distanceUnit = inDistanceUnit;
+               _speedUnit = new Unit(_distanceUnit, "perhour");
+               _altitudeUnit = inAltitudeUnit;
+               _defaultAltitudeFormat = inAltitudeFormat;
+               _vertSpeedUnit = new Unit(_altitudeUnit, "persec");
+       }
+
+       /**
+        * @return name key
+        */
+       public String getNameKey() {
+               return _nameKey;
+       }
+
+       /**
+        * @return distance unit
+        */
+       public Unit getDistanceUnit() {
+               return _distanceUnit;
+       }
+
+       /**
+        * @return speed unit
+        */
+       public Unit getSpeedUnit() {
+               return _speedUnit;
+       }
+
+       /**
+        * @return altitude unit
+        */
+       public Unit getAltitudeUnit() {
+               return _altitudeUnit;
+       }
+
+       /**
+        * @return vertical speed unit
+        */
+       public Unit getVerticalSpeedUnit() {
+               return _vertSpeedUnit;
+       }
+
+       /**
+        * @return default altitude format
+        */
+       public Altitude.Format getDefaultAltitudeFormat() {
+               return _defaultAltitudeFormat;
+       }
+}
diff --git a/tim/prune/data/UnitSetLibrary.java b/tim/prune/data/UnitSetLibrary.java
new file mode 100644 (file)
index 0000000..b270124
--- /dev/null
@@ -0,0 +1,66 @@
+package tim.prune.data;
+
+/**
+ * List of all possible unit sets, for example
+ * metric, imperial, nautical
+ */
+public abstract class UnitSetLibrary
+{
+       // Distance units - all conversion factors are from metres
+       /** Units for feet (used for loading and converting values) */
+       public static final Unit UNITS_FEET       = new Unit("feet", 3.2808);
+       /** Units for metres */
+       public static final Unit UNITS_METRES     = new Unit("metres");
+       /** Units for km */
+       public static final Unit UNITS_KILOMETRES = new Unit("kilometres", 1/1000.0);
+       /** Units for miles */
+       public static final Unit UNITS_MILES      = new Unit("miles", 1/1609.3);
+       /** Units for nautical miles */
+       public static final Unit UNITS_NAUTICAL_MILES = new Unit("nauticalmiles", 1/1852.0);
+
+       /** Array of available unit sets */
+       private static UnitSet[] _sets = {
+               new UnitSet("unitset.kilometres", UNITS_KILOMETRES, UNITS_METRES, Altitude.Format.METRES),
+               new UnitSet("unitset.miles", UNITS_MILES, UNITS_FEET, Altitude.Format.FEET),
+               new UnitSet("unitset.nautical", UNITS_NAUTICAL_MILES, UNITS_FEET, Altitude.Format.FEET)
+       };
+
+       /**
+        * @return number of available unit sets
+        */
+       public static int getNumUnitSets() {
+               return _sets.length;
+       }
+
+       /**
+        * Get the specified unit set
+        * @param inIndex index of set starting from 0
+        * @return specified unit set or the default one if index out of range
+        */
+       public static UnitSet getUnitSet(int inIndex)
+       {
+               if (inIndex >= 0 && inIndex < getNumUnitSets()) {
+                       return _sets[inIndex];
+               }
+               return _sets[0];
+       }
+
+       /**
+        * Get the unit set specified by the given key
+        * @param inKey key to look for
+        * @return unit set with given key, or default set if key not found
+        */
+       public static UnitSet getUnitSet(String inKey)
+       {
+               // Loop over all available unit sets
+               for (int i=0; i<getNumUnitSets(); i++)
+               {
+                       UnitSet set = getUnitSet(i);
+                       if (set.getNameKey().equals(inKey)) {
+                               return set;
+                       }
+               }
+               // Not found in list, so just return the first one
+               return getUnitSet(0);
+       }
+}
index 1032fcfca84f2ea8e3105973dbe7c5731f1996c0..85c52dee7f5291bc8293be3aa3b5038a0068a150 100644 (file)
@@ -18,6 +18,7 @@ import javax.swing.JTextField;
 import tim.prune.App;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.Field;
 
@@ -69,7 +70,7 @@ public class AddAltitudeOffset extends GenericFunction
                        _dialog.pack();
                }
                // Set label according to altitude units
-               setLabelText(selStart, selEnd);
+               setLabelText();
                // Select the contents of the edit field
                _editField.selectAll();
                _dialog.setVisible(true);
@@ -148,20 +149,11 @@ public class AddAltitudeOffset extends GenericFunction
 
        /**
         * Set the label text according to the current units
-        * @param inStart start index of selection
-        * @param inEnd end index of selection
         */
-       private void setLabelText(int inStart, int inEnd)
+       private void setLabelText()
        {
-               _altFormat = Altitude.Format.NO_FORMAT;
-               for (int i=inStart; i<=inEnd && _altFormat==Altitude.Format.NO_FORMAT; i++)
-               {
-                       Altitude alt = _app.getTrackInfo().getTrack().getPoint(i).getAltitude();
-                       if (alt != null) {
-                               _altFormat = alt.getFormat();
-                       }
-               }
-               if (_altFormat==Altitude.Format.NO_FORMAT) {
+               _altFormat = Altitude.Format.FEET;
+               if (Config.getUnitSet().getAltitudeUnit().isStandard()) {
                        _altFormat = Altitude.Format.METRES;
                }
                final String unitKey = (_altFormat==Altitude.Format.METRES?"units.metres.short":"units.feet.short");
diff --git a/tim/prune/function/CropToSelection.java b/tim/prune/function/CropToSelection.java
new file mode 100644 (file)
index 0000000..e8575af
--- /dev/null
@@ -0,0 +1,51 @@
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.data.Track;
+
+/**
+ * Class to provide the function to crop the track
+ * to the current selection
+ */
+public class CropToSelection extends DeleteBitOfTrackFunction
+{
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public CropToSelection(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.croptrack";
+       }
+
+       /**
+        * @return name key for undo operation
+        */
+       protected String getUndoNameKey() {
+               return "undo.croptrack";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               // check track
+               Track track = _app.getTrackInfo().getTrack();
+               if (track == null || track.getNumPoints() <= 0) return;
+               // check selection
+               final int selStart = _app.getTrackInfo().getSelection().getStart();
+               final int selEnd = _app.getTrackInfo().getSelection().getEnd();
+               if (selStart < 0 || selEnd < 0 || selEnd <= selStart) return;
+               // check for all selected
+               if (selStart == 0 && selEnd == (track.getNumPoints() - 1)) return;
+
+               // Pass indexes to parent class
+               deleteTwoSections(0, selStart-1, selEnd+1, track.getNumPoints()-1);
+       }
+}
diff --git a/tim/prune/function/DeleteBitOfTrackFunction.java b/tim/prune/function/DeleteBitOfTrackFunction.java
new file mode 100644 (file)
index 0000000..eb145cd
--- /dev/null
@@ -0,0 +1,209 @@
+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.data.DataPoint;
+import tim.prune.data.TrackInfo;
+import tim.prune.undo.UndoDeleteRange;
+
+
+/**
+ * Abstract class to hold general deletion routines to delete
+ * either one or two track sections.  Forms parent class to
+ * the DeleteSelection and CropToSelection functions
+ */
+public abstract class DeleteBitOfTrackFunction extends GenericFunction
+{
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public DeleteBitOfTrackFunction(App inApp)
+       {
+               super(inApp);
+       }
+
+       /**
+        * @return key of undo text
+        */
+       protected abstract String getUndoNameKey();
+
+       /**
+        * Delete a single section
+        * @param inStart1 start index of section
+        * @param inEnd1 end index of section
+        */
+       protected void deleteSection(int inStart1, int inEnd1)
+       {
+               deleteTwoSections(inStart1, inEnd1, -1, -1);
+       }
+
+       /**
+        * Delete the two specified sections
+        * @param inStart1 start index of first section to delete
+        * @param inEnd1 end index of first section
+        * @param inStart2 start index of second section to delete
+        * @param inEnd2 end index of second section
+        */
+       protected void deleteTwoSections(int inStart1, int inEnd1, int inStart2, int inEnd2)
+       {
+               boolean[] deleteAllOrNone = {false, false};
+               // TODO: Check for range overlap?  And test!
+               if (inStart1 < 0 || inEnd1 < 0 || inEnd1 < inStart1) {
+                       inStart1 = inEnd1 = -1;
+               }
+               if (inStart2 < 0 || inEnd2 < 0 || inEnd2 < inStart2) {
+                       inStart2 = inEnd2 = -1;
+               }
+               if ((inStart1 >= 0 && inEnd1 < inStart1)
+                       || (inStart2 >= 0 && (inStart2 < inEnd1 || inEnd2 <= inStart2)))
+               {
+                       System.err.println("Invalid ranges: (" + inStart1 + " - " + inEnd1 + "), (" + inStart2 + " - " + inEnd2 + ")");
+                       return;
+               }
+               // First section (if any)
+               int numPoints = inEnd1 - inStart1 + 1;
+               boolean[] deleteMedia1 = new boolean[numPoints];
+               int numDeleted1 = prepareDeleteMedia(inStart1, inEnd1, deleteAllOrNone, deleteMedia1);
+               if (numDeleted1 < 0) return;
+
+               // Second section (if any)
+               numPoints = inEnd2 - inStart2 + 1;
+               boolean[] deleteMedia2 = new boolean[numPoints];
+               int numDeleted2 = prepareDeleteMedia(inStart2, inEnd2, deleteAllOrNone, deleteMedia2);
+               if (numDeleted2 < 0) return;
+               int numDeleted = numDeleted1 + numDeleted2;
+               if (numDeleted <= 0) return;
+
+               // create undo object
+               UndoDeleteRange undo = new UndoDeleteRange(_app.getTrackInfo(), getUndoNameKey(),
+                       inStart1, deleteMedia1, inStart2, deleteMedia2);
+
+               // Loop through media to remove or disconnect
+               if (numDeleted1 > 0) {
+                       resolveMedia(_app.getTrackInfo(), inStart1, deleteMedia1);
+               }
+               if (numDeleted2 > 0) {
+                       resolveMedia(_app.getTrackInfo(), inStart2, deleteMedia2);
+               }
+
+               // Call track to delete ranges 1 and 2
+               if (numDeleted2 > 0) { // delete range2 first
+                       _app.getTrackInfo().getTrack().deleteRange(inStart2, inEnd2);
+               }
+               if (numDeleted1 > 0) { // delete range1 first
+                       _app.getTrackInfo().getTrack().deleteRange(inStart1, inEnd1);
+               }
+
+               // clear selection and notify
+               _app.getTrackInfo().getSelection().clearAll();
+               UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_ADDED_OR_REMOVED);
+
+               // pass back to _app
+               _app.completeFunction(undo, "" + numDeleted + " "
+                       + I18nManager.getText("confirm.deletepoint.multi"));
+       }
+
+       /**
+        * Prepare to delete the media in the given section, including prompting to delete or not
+        * @param inStart start index of the range to delete
+        * @param inEnd end index
+        * @param inDeleteAllOrNone boolean flags for delete all and delete none, held in an array
+        * @param inDeleteMedia boolean flag for each point, whether to delete media or not
+        * @return number of points to delete
+        */
+       private int prepareDeleteMedia(int inStart, int inEnd, boolean[] inDeleteAllOrNone, boolean[] inDeleteMedia)
+       {
+               // Check sanity of inputs
+               if (inStart < 0 || inEnd < 0 || inEnd < inStart) return 0;
+               final int numPoints = inEnd - inStart + 1;
+               if (inDeleteAllOrNone == null || inDeleteAllOrNone.length != 2
+                       || inDeleteMedia == null || inDeleteMedia.length != numPoints) {
+                       return 0;
+               }
+
+               // define buttons on prompt
+               String[] questionOptions = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
+                       I18nManager.getText("button.yestoall"), I18nManager.getText("button.notoall"),
+                       I18nManager.getText("button.cancel")};
+
+               // Loop over points to check for media
+               for (int i=0; i<numPoints; i++)
+               {
+                       DataPoint point = _app.getTrackInfo().getTrack().getPoint(inStart + i);
+                       if (point.hasMedia())
+                       {
+                               // point has either photo or audio
+                               if (inDeleteAllOrNone[0]) // delete all has already been selected
+                               {
+                                       inDeleteMedia[i] = true;
+                               }
+                               else if (inDeleteAllOrNone[1]) // delete none has already been selected
+                               {
+                                       inDeleteMedia[i] = false;
+                               }
+                               else
+                               {
+                                       int response = JOptionPane.showOptionDialog(_app.getFrame(),
+                                               I18nManager.getText("dialog.deletepoint.deletephoto") + " " + point.getMediaName(),
+                                               I18nManager.getText("dialog.deletepoint.title"),
+                                               JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
+                                               questionOptions, questionOptions[1]);
+                                       // check for cancel or close
+                                       if (response == 4 || response == -1) {return -1;}
+                                       // check for yes or yes to all
+                                       if (response == 0 || response == 2)
+                                       {
+                                               inDeleteMedia[i] = true;
+                                               if (response == 2) {inDeleteAllOrNone[0] = true;}
+                                       }
+                                       // check for no to all
+                                       if (response == 3) {inDeleteAllOrNone[1] = true;}
+                               }
+                       }
+               }
+               return numPoints;
+       }
+
+       /**
+        * Resolve the media from the given points by either detaching or deleting
+        * @param inTrack track object
+        * @param inStart start index of range
+        * @param inDeleteFlags media deletion flags
+        */
+       private static void resolveMedia(TrackInfo inTrackInfo, int inStart, boolean[] inDeleteFlags)
+       {
+               for (int i=0; i<inDeleteFlags.length; i++)
+               {
+                       DataPoint point = inTrackInfo.getTrack().getPoint(i + inStart);
+                       if (point != null && point.hasMedia())
+                       {
+                               if (inDeleteFlags[i])
+                               {
+                                       // delete photo and/or audio from lists
+                                       if (point.getPhoto() != null) {
+                                               inTrackInfo.getPhotoList().deletePhoto(inTrackInfo.getPhotoList().getPhotoIndex(point.getPhoto()));
+                                       }
+                                       if (point.getAudio() != null) {
+                                               inTrackInfo.getAudioList().deleteAudio(inTrackInfo.getAudioList().getAudioIndex(point.getAudio()));
+                                       }
+                               }
+                               else
+                               {
+                                       // decouple photo and/or audio from point
+                                       if (point.getPhoto() != null) {
+                                               point.getPhoto().setDataPoint(null);
+                                       }
+                                       if (point.getAudio() != null) {
+                                               point.getAudio().setDataPoint(null);
+                                       }
+                               }
+                       }
+               }
+       }
+}
diff --git a/tim/prune/function/DeleteSelectedRangeFunction.java b/tim/prune/function/DeleteSelectedRangeFunction.java
new file mode 100644 (file)
index 0000000..18f3061
--- /dev/null
@@ -0,0 +1,41 @@
+package tim.prune.function;
+
+import tim.prune.App;
+
+/**
+ * Class to provide the function to delete the currently selected range
+ */
+public class DeleteSelectedRangeFunction extends DeleteBitOfTrackFunction
+{
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public DeleteSelectedRangeFunction(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.deleterange";
+       }
+
+       /**
+        * @return name key for undo operation
+        */
+       protected String getUndoNameKey() {
+               return "undo.deleterange";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               // Get the currently selected range and pass indexes to parent class
+               final int startIndex = _app.getTrackInfo().getSelection().getStart();
+               final int endIndex   = _app.getTrackInfo().getSelection().getEnd();
+               deleteSection(startIndex, endIndex);
+       }
+}
index 1a4fdb19f1c7310b1613d45377abcf4d92c55f79..915bed1041bd6fdc7342c390ed70ab4575d4868c 100644 (file)
@@ -235,7 +235,12 @@ public class DiskCacheConfig extends GenericFunction
                                        I18nManager.getText(getNameKey()), JOptionPane.WARNING_MESSAGE);
                                return;
                        }
-                       // TODO: Check path is writeable too, and give warning if not
+                       // Check that the cache path is writable too, and give warning if not
+                       if (cacheDir.exists() && cacheDir.isDirectory() && !cacheDir.canWrite())
+                       {
+                               JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.diskcache.cannotwrite"),
+                                       I18nManager.getText(getNameKey()), JOptionPane.WARNING_MESSAGE);
+                       }
                }
                Config.setConfigString(Config.KEY_DISK_CACHE, cachePath);
                // inform subscribers so that tiles are wiped from memory and refetched
index 338bf0bebbe1e1aef4d0130cf9b5d904b9c1a5c5..a0bae6517a09419dd5af77622f49b983de0e601e 100644 (file)
@@ -6,6 +6,8 @@ import java.awt.FlowLayout;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
 import java.text.NumberFormat;
 
 import javax.swing.BorderFactory;
@@ -19,8 +21,10 @@ import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.config.Config;
 import tim.prune.data.Altitude;
-import tim.prune.data.Distance;
+import tim.prune.data.AltitudeRange;
+import tim.prune.data.DataPoint;
 import tim.prune.data.Selection;
+import tim.prune.data.Unit;
 import tim.prune.gui.DisplayUtils;
 import tim.prune.gui.profile.SpeedData;
 
@@ -31,15 +35,34 @@ public class FullRangeDetails extends GenericFunction
 {
        /** Dialog */
        private JDialog _dialog = null;
+       /** Label for number of points */
+       private JLabel _numPointsLabel = null;
        /** Label for number of segments */
        private JLabel _numSegsLabel = null;
-       /** Label for pace */
-       private JLabel _paceLabel = null;
-       /** Label for gradient */
-       private JLabel _gradientLabel = null;
-       /** Moving distance, speed */
-       private JLabel _movingDistanceLabel = null, _aveMovingSpeedLabel = null;
+       /** Label for the maximum speed */
        private JLabel _maxSpeedLabel = null;
+
+       /** Label for heading of "total" column */
+       private JLabel _colTotalLabel = null;
+       /** Label for heading of "segments" column */
+       private JLabel _colSegmentsLabel = null;
+       /** Labels for distances */
+       private JLabel _totalDistanceLabel = null, _movingDistanceLabel = null;
+       /** Labels for durations */
+       private JLabel _totalDurationLabel = null, _movingDurationLabel = null;
+       /** Labels for climbs */
+       private JLabel _totalClimbLabel = null, _movingClimbLabel = null;
+       /** Labels for descents */
+       private JLabel _totalDescentLabel = null, _movingDescentLabel = null;
+       /** Labels for pace */
+       private JLabel _totalPaceLabel = null, _movingPaceLabel = null;
+       /** Labels for gradient */
+       private JLabel _totalGradientLabel = null, _movingGradientLabel = null;
+       /** Labels for speed */
+       private JLabel _totalSpeedLabel, _movingSpeedLabel = null;
+       /** Labels for vertical speed */
+       private JLabel _totalVertSpeedLabel, _movingVertSpeedLabel = null;
+
        /** Number formatter for one decimal place */
        private static final NumberFormat FORMAT_ONE_DP = NumberFormat.getNumberInstance();
        /** Flexible number formatter for different decimal places */
@@ -87,50 +110,116 @@ public class FullRangeDetails extends GenericFunction
                JPanel dialogPanel = new JPanel();
                dialogPanel.setLayout(new BorderLayout(5, 5));
                // Label at top
-               JLabel topLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.intro"));
+               JLabel topLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.intro") + ":");
                topLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
                dialogPanel.add(topLabel, BorderLayout.NORTH);
 
                // Details panel in middle
                JPanel midPanel = new JPanel();
-               midPanel.setLayout(new GridLayout(0, 2, 6, 2));
+               midPanel.setLayout(new GridLayout(0, 3, 6, 2));
                midPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               // Number of points
+               JLabel pointsLabel = new JLabel(I18nManager.getText("details.track.points") + ": ");
+               pointsLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(pointsLabel);
+               _numPointsLabel = new JLabel("100");
+               midPanel.add(_numPointsLabel);
+               midPanel.add(new JLabel(" "));
                // Number of segments
                JLabel segLabel = new JLabel(I18nManager.getText("details.range.numsegments") + ": ");
                segLabel.setHorizontalAlignment(JLabel.RIGHT);
                midPanel.add(segLabel);
                _numSegsLabel = new JLabel("100");
                midPanel.add(_numSegsLabel);
+               midPanel.add(new JLabel(" "));
+               // Maximum speed
+               JLabel maxSpeedLabel = new JLabel(I18nManager.getText("details.range.maxspeed") + ": ");
+               maxSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(maxSpeedLabel);
+               _maxSpeedLabel = new JLabel("10 km/h");
+               midPanel.add(_maxSpeedLabel);
+               midPanel.add(new JLabel(" "));
+
+               // blank row
+               for (int i=0; i<3; i++) midPanel.add(new JLabel(" "));
+
+               // Row for column headings
+               midPanel.add(new JLabel(" "));
+               _colTotalLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.coltotal"));
+               midPanel.add(_colTotalLabel);
+               _colSegmentsLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.colsegments"));
+               midPanel.add(_colSegmentsLabel);
+
+               // Distance
+               JLabel distLabel = new JLabel(I18nManager.getText("fieldname.distance") + ": ");
+               distLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(distLabel);
+               _totalDistanceLabel = new JLabel("5 km");
+               midPanel.add(_totalDistanceLabel);
+               _movingDistanceLabel = new JLabel("5 km");
+               midPanel.add(_movingDistanceLabel);
+
+               // Duration
+               JLabel durationLabel = new JLabel(I18nManager.getText("fieldname.duration") + ": ");
+               durationLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(durationLabel);
+               _totalDurationLabel = new JLabel("15 min");
+               midPanel.add(_totalDurationLabel);
+               _movingDurationLabel = new JLabel("15 min");
+               midPanel.add(_movingDurationLabel);
+
+               // Speed
+               JLabel speedLabel = new JLabel(I18nManager.getText("details.range.avespeed") + ": ");
+               speedLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(speedLabel);
+               _totalSpeedLabel = new JLabel("5.5 km/h");
+               midPanel.add(_totalSpeedLabel);
+               _movingSpeedLabel = new JLabel("5.5 km/h");
+               midPanel.add(_movingSpeedLabel);
+
                // Pace
                JLabel paceLabel = new JLabel(I18nManager.getText("details.range.pace") + ": ");
                paceLabel.setHorizontalAlignment(JLabel.RIGHT);
                midPanel.add(paceLabel);
-               _paceLabel = new JLabel("8 min/km");
-               midPanel.add(_paceLabel);
+               _totalPaceLabel = new JLabel("8 min/km");
+               midPanel.add(_totalPaceLabel);
+               _movingPaceLabel = new JLabel("8 min/km");
+               midPanel.add(_movingPaceLabel);
+
+               // Climb
+               JLabel climbLabel = new JLabel(I18nManager.getText("details.range.climb") + ": ");
+               climbLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(climbLabel);
+               _totalClimbLabel = new JLabel("1000 m");
+               midPanel.add(_totalClimbLabel);
+               _movingClimbLabel = new JLabel("1000 m");
+               midPanel.add(_movingClimbLabel);
+               // Descent
+               JLabel descentLabel = new JLabel(I18nManager.getText("details.range.descent") + ": ");
+               descentLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(descentLabel);
+               _totalDescentLabel = new JLabel("1000 m");
+               midPanel.add(_totalDescentLabel);
+               _movingDescentLabel = new JLabel("1000 m");
+               midPanel.add(_movingDescentLabel);
+
                // Gradient
                JLabel gradientLabel = new JLabel(I18nManager.getText("details.range.gradient") + ": ");
                gradientLabel.setHorizontalAlignment(JLabel.RIGHT);
                midPanel.add(gradientLabel);
-               _gradientLabel = new JLabel("10 %");
-               midPanel.add(_gradientLabel);
-               // Moving distance
-               JLabel movingDistLabel = new JLabel(I18nManager.getText("fieldname.movingdistance") + ": ");
-               movingDistLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(movingDistLabel);
-               _movingDistanceLabel = new JLabel("5 km");
-               midPanel.add(_movingDistanceLabel);
-               // Moving speed
-               JLabel movingSpeedLabel = new JLabel(I18nManager.getText("details.range.avemovingspeed") + ": ");
-               movingSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(movingSpeedLabel);
-               _aveMovingSpeedLabel = new JLabel("5 km/h");
-               midPanel.add(_aveMovingSpeedLabel);
-               // Maximum speed
-               JLabel maxSpeedLabel = new JLabel(I18nManager.getText("details.range.maxspeed") + ": ");
-               maxSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(maxSpeedLabel);
-               _maxSpeedLabel = new JLabel("10 km/h");
-               midPanel.add(_maxSpeedLabel);
+               _totalGradientLabel = new JLabel("10 %");
+               midPanel.add(_totalGradientLabel);
+               _movingGradientLabel = new JLabel("10 %");
+               midPanel.add(_movingGradientLabel);
+
+               // Vertical speed
+               JLabel vSpeedLabel = new JLabel(I18nManager.getText("fieldname.verticalspeed") + ": ");
+               vSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(vSpeedLabel);
+               _totalVertSpeedLabel = new JLabel("1 m/s");
+               midPanel.add(_totalVertSpeedLabel);
+               _movingVertSpeedLabel = new JLabel("1 m/s");
+               midPanel.add(_movingVertSpeedLabel);
 
                dialogPanel.add(midPanel, BorderLayout.CENTER);
                // button panel at bottom
@@ -143,6 +232,12 @@ public class FullRangeDetails extends GenericFunction
                                _dialog.dispose();
                        }
                });
+               closeButton.addKeyListener(new KeyAdapter() {
+                       public void keyPressed(KeyEvent inE) {
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
+                               super.keyPressed(inE);
+                       }
+               });
                buttonPanel.add(closeButton);
                dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
                return dialogPanel;
@@ -155,59 +250,153 @@ public class FullRangeDetails extends GenericFunction
        private void updateDetails()
        {
                Selection selection = _app.getTrackInfo().getSelection();
+               // Number of points
+               _numPointsLabel.setText("" + (selection.getEnd()-selection.getStart()+1));
                // Number of segments
                _numSegsLabel.setText("" + selection.getNumSegments());
-               // Pace value
-               if (selection.getNumSeconds() > 0)
+               final boolean isMultiSegments = (selection.getNumSegments() > 1);
+               // Set visibility of third column accordingly
+               _movingDistanceLabel.setVisible(isMultiSegments);
+               _movingDurationLabel.setVisible(isMultiSegments);
+               _movingClimbLabel.setVisible(isMultiSegments);
+               _movingDescentLabel.setVisible(isMultiSegments);
+               _movingSpeedLabel.setVisible(isMultiSegments);
+               _movingPaceLabel.setVisible(isMultiSegments);
+               _movingGradientLabel.setVisible(isMultiSegments);
+               _movingVertSpeedLabel.setVisible(isMultiSegments);
+
+               // Distance in current units
+               final Unit distUnit = Config.getUnitSet().getDistanceUnit();
+               final String distUnitsStr = I18nManager.getText(distUnit.getShortnameKey());
+               final double selectionDistance = selection.getDistance();
+               _totalDistanceLabel.setText(roundedNumber(selectionDistance) + " " + distUnitsStr);
+
+               // Duration
+               long numSecs = selection.getNumSeconds();
+               _totalDurationLabel.setText(DisplayUtils.buildDurationString(numSecs));
+               // Climb and descent
+               final Unit altUnit = Config.getUnitSet().getAltitudeUnit();
+               final String altUnitsStr = " " + I18nManager.getText(altUnit.getShortnameKey());
+               if (selection.getAltitudeRange().hasRange()) {
+                       _totalClimbLabel.setText(selection.getAltitudeRange().getClimb(altUnit) + altUnitsStr);
+                       _totalDescentLabel.setText(selection.getAltitudeRange().getDescent(altUnit) + altUnitsStr);
+               }
+               else {
+                       _totalClimbLabel.setText("");
+                       _totalDescentLabel.setText("");
+               }
+
+               // Overall pace and speed
+               final String speedUnitsStr = I18nManager.getText(Config.getUnitSet().getSpeedUnit().getShortnameKey());
+               if (numSecs > 0 && selectionDistance > 0)
                {
-                       boolean useMetric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
-                       Distance.Units distUnits = useMetric?Distance.Units.KILOMETRES:Distance.Units.MILES;
-                       String distUnitsStr = I18nManager.getText(useMetric?"units.kilometres.short":"units.miles.short");
-                       _paceLabel.setText(DisplayUtils.buildDurationString(
-                                       (long) (selection.getNumSeconds()/selection.getDistance(distUnits)))
+                       _totalPaceLabel.setText(
+                               DisplayUtils.buildDurationString((long) (numSecs/selectionDistance))
                                + " / " + distUnitsStr);
+                       _totalSpeedLabel.setText(roundedNumber(selectionDistance/numSecs*3600.0)
+                               + " " + speedUnitsStr);
                }
                else {
-                       _paceLabel.setText("");
+                       _totalPaceLabel.setText("");
+                       _totalSpeedLabel.setText("");
                }
-               // Gradient
-               Altitude firstAlt = _app.getTrackInfo().getTrack().getPoint(selection.getStart()).getAltitude();
-               Altitude lastAlt = _app.getTrackInfo().getTrack().getPoint(selection.getEnd()).getAltitude();
-               double metreDist = selection.getDistance(Distance.Units.METRES);
-               if (firstAlt.isValid() && lastAlt.isValid() && metreDist > 0.0)
+
+               // Moving distance
+               double movingDist = selection.getMovingDistance();
+               _movingDistanceLabel.setText(roundedNumber(movingDist) + " " + distUnitsStr);
+               // Moving average speed
+               long numMovingSecs = selection.getMovingSeconds();
+               if (numMovingSecs > 0)
+               {
+                       _movingDurationLabel.setText(DisplayUtils.buildDurationString(numMovingSecs));
+                       _movingSpeedLabel.setText(roundedNumber(movingDist/numMovingSecs*3600.0)
+                               + " " + speedUnitsStr);
+                       _movingPaceLabel.setText(
+                               DisplayUtils.buildDurationString((long) (numMovingSecs/movingDist))
+                               + " / " + distUnitsStr);
+               }
+               else
+               {
+                       _movingDurationLabel.setText("");
+                       _movingSpeedLabel.setText("");
+                       _movingPaceLabel.setText("");
+               }
+
+               // Moving gradient and moving climb/descent
+               Altitude firstAlt = null, lastAlt = null;
+               Altitude veryFirstAlt = null, veryLastAlt = null;
+               AltitudeRange altRange = new AltitudeRange();
+               double movingHeightDiff = 0.0;
+               if (movingDist > 0.0)
+               {
+                       for (int pNum = selection.getStart(); pNum <= selection.getEnd(); pNum++)
+                       {
+                               DataPoint p = _app.getTrackInfo().getTrack().getPoint(pNum);
+                               if (p != null && !p.isWaypoint())
+                               {
+                                       // If we're starting a new segment, calculate the height diff of the previous one
+                                       if (p.getSegmentStart())
+                                       {
+                                               if (firstAlt != null && firstAlt.isValid() && lastAlt != null && lastAlt.isValid())
+                                                       movingHeightDiff = movingHeightDiff + lastAlt.getMetricValue() - firstAlt.getMetricValue();
+                                               firstAlt = null; lastAlt = null;
+                                       }
+                                       Altitude alt = p.getAltitude();
+                                       if (alt != null && alt.isValid())
+                                       {
+                                               if (firstAlt == null) firstAlt = alt;
+                                               else lastAlt = alt;
+                                               if (veryFirstAlt == null) veryFirstAlt = alt;
+                                               else veryLastAlt = alt;
+                                       }
+                                       // Keep track of climb and descent too
+                                       if (p.getSegmentStart())
+                                               altRange.ignoreValue(alt);
+                                       else
+                                               altRange.addValue(alt);
+                               }
+                       }
+                       // deal with last segment
+                       if (firstAlt != null && firstAlt.isValid() && lastAlt != null && lastAlt.isValid())
+                               movingHeightDiff = movingHeightDiff + lastAlt.getMetricValue() - firstAlt.getMetricValue();
+                       final double metricMovingDist = movingDist / distUnit.getMultFactorFromStd(); // convert back to metres
+                       final double gradient = movingHeightDiff * 100.0 / metricMovingDist;
+                       _movingGradientLabel.setText(FORMAT_ONE_DP.format(gradient) + " %");
+               }
+               if (!altRange.hasRange()) {
+                       _movingGradientLabel.setText("");
+               }
+               final boolean hasAltitudes = veryFirstAlt != null && veryFirstAlt.isValid() && veryLastAlt != null && veryLastAlt.isValid();
+
+               // Total gradient
+               final double metreDist = selection.getDistance() / distUnit.getMultFactorFromStd(); // convert back to metres
+               if (hasAltitudes && metreDist > 0.0)
                {
                        // got an altitude and range
-                       int altDiffInMetres = lastAlt.getValue(Altitude.Format.METRES) - firstAlt.getValue(Altitude.Format.METRES);
+                       int altDiffInMetres = veryLastAlt.getValue(Altitude.Format.METRES) - veryFirstAlt.getValue(Altitude.Format.METRES);
                        double gradient = altDiffInMetres * 100.0 / metreDist;
-                       _gradientLabel.setText(FORMAT_ONE_DP.format(gradient) + " %");
+                       _totalGradientLabel.setText(FORMAT_ONE_DP.format(gradient) + " %");
                }
                else {
                        // no altitude given
-                       _gradientLabel.setText("");
+                       _totalGradientLabel.setText("");
                }
 
-               // Show moving distance and average even when number of segments is 1
-               final boolean isMetric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
-               final Distance.Units distUnits = isMetric?Distance.Units.KILOMETRES:Distance.Units.MILES;
-               final String distUnitsStr = I18nManager.getText(isMetric?"units.kilometres.short":"units.miles.short");
-               final String speedUnitsStr = I18nManager.getText(isMetric?"units.kmh":"units.mph");
-               // Moving distance
-               _movingDistanceLabel.setText(roundedNumber(selection.getMovingDistance(distUnits)) + " " + distUnitsStr);
-               // Moving average speed
-               long numSecs = selection.getMovingSeconds();
-               if (numSecs > 0) {
-                       _aveMovingSpeedLabel.setText(roundedNumber(selection.getMovingDistance(distUnits)/numSecs*3600.0)
-                               + " " + speedUnitsStr);
+               // Moving climb/descent
+               if (altRange.hasRange()) {
+                       _movingClimbLabel.setText(altRange.getClimb(altUnit) + altUnitsStr);
+                       _movingDescentLabel.setText(altRange.getDescent(altUnit) + altUnitsStr);
                }
                else {
-                       _aveMovingSpeedLabel.setText("");
+                       _movingClimbLabel.setText("");
+                       _movingDescentLabel.setText("");
                }
-
                // Maximum speed
                SpeedData speeds = new SpeedData(_app.getTrackInfo().getTrack());
-               speeds.init();
+               speeds.init(Config.getUnitSet());
                double maxSpeed = 0.0;
-               for (int i=selection.getStart(); i<=selection.getEnd(); i++) {
+               for (int i=selection.getStart(); i<=selection.getEnd(); i++)
+               {
                        if (speeds.hasData(i) && (speeds.getData(i) > maxSpeed)) {
                                maxSpeed = speeds.getData(i);
                        }
@@ -218,6 +407,23 @@ public class FullRangeDetails extends GenericFunction
                else {
                        _maxSpeedLabel.setText("");
                }
+
+               // vertical speed
+               final String vertSpeedUnitsStr = I18nManager.getText(Config.getUnitSet().getVerticalSpeedUnit().getShortnameKey());
+               if (hasAltitudes && metreDist > 0.0 && numSecs > 0)
+               {
+                       // got an altitude and time - do total
+                       final int altDiffInMetres = veryLastAlt.getValue(Altitude.Format.METRES) - veryFirstAlt.getValue(Altitude.Format.METRES);
+                       final double altDiff = altDiffInMetres * altUnit.getMultFactorFromStd();
+                       _totalVertSpeedLabel.setText(roundedNumber(altDiff/numSecs) + " " + vertSpeedUnitsStr);
+                       // and moving
+                       _movingVertSpeedLabel.setText(roundedNumber(movingHeightDiff * altUnit.getMultFactorFromStd() / numMovingSecs) + " " + vertSpeedUnitsStr);
+               }
+               else {
+                       // no vertical speed available
+                       _totalVertSpeedLabel.setText("");
+                       _movingVertSpeedLabel.setText("");
+               }
        }
 
        /**
index dc2da5b3cf37419704e8f08078208bf96210b26f..0e6d9eb5ae432454b505cc8fb8ba4a200b2bf57c 100644 (file)
@@ -120,21 +120,27 @@ public class GetWikipediaFunction extends GenericDownloaderFunction
 
 
        /**
-        * Load the selected track or point
+        * Load the selected point(s)
         */
        protected void loadSelected()
        {
-               // Find the row selected in the table and get the corresponding track
-               int rowNum = _trackTable.getSelectedRow();
-               if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
+               // Find the rows selected in the table and get the corresponding coords
+               int numSelected = _trackTable.getSelectedRowCount();
+               if (numSelected < 1) return;
+               int[] rowNums = _trackTable.getSelectedRows();
+               for (int i=0; i<numSelected; i++)
                {
-                       String coords = _trackListModel.getTrack(rowNum).getDownloadLink();
-                       String[] latlon = coords.split(",");
-                       if (latlon.length == 2)
+                       int rowNum = rowNums[i];
+                       if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
                        {
-                               DataPoint point = new DataPoint(new Latitude(latlon[0]), new Longitude(latlon[1]), null);
-                               point.setFieldValue(Field.WAYPT_NAME, _trackListModel.getTrack(rowNum).getTrackName(), false);
-                               _app.createPoint(point);
+                               String coords = _trackListModel.getTrack(rowNum).getDownloadLink();
+                               String[] latlon = coords.split(",");
+                               if (latlon.length == 2)
+                               {
+                                       DataPoint point = new DataPoint(new Latitude(latlon[0]), new Longitude(latlon[1]), null);
+                                       point.setFieldValue(Field.WAYPT_NAME, _trackListModel.getTrack(rowNum).getTrackName(), false);
+                                       _app.createPoint(point);
+                               }
                        }
                }
                // Close the dialog
diff --git a/tim/prune/function/InterpolateFunction.java b/tim/prune/function/InterpolateFunction.java
new file mode 100644 (file)
index 0000000..8482614
--- /dev/null
@@ -0,0 +1,164 @@
+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;
+import tim.prune.undo.UndoInterpolate;
+
+/**
+ * Function to interpolate between the points in a range
+ */
+public class InterpolateFunction extends GenericFunction
+{
+       /**
+        * Constructor
+        * @param inApp app object
+        */
+       public InterpolateFunction(App inApp) {
+               super(inApp);
+       }
+
+       /** @return name key */
+       public String getNameKey() {
+               return "function.interpolate";
+       }
+
+       /**
+        * Perform the operation
+        */
+       public void begin()
+       {
+               // Firstly, work out whether the selected range only contains waypoints or not
+               final int startIndex = _app.getTrackInfo().getSelection().getStart();
+               final int endIndex   = _app.getTrackInfo().getSelection().getEnd();
+               boolean betweenWaypoints = false;
+               // if there are only waypoints, then ask whether to interpolate them
+               if (!selectedRangeHasTrackpoints(_app.getTrackInfo().getTrack(), startIndex, endIndex))
+               {
+                       int answer = JOptionPane.showConfirmDialog(_parentFrame,
+                               I18nManager.getText("dialog.interpolate.betweenwaypoints"),
+                               I18nManager.getText(getNameKey()), JOptionPane.YES_NO_OPTION);
+                       if (answer == JOptionPane.NO_OPTION) {
+                               // user said no, so nothing to do
+                               return;
+                       }
+                       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 Track track = _app.getTrackInfo().getTrack();
+               final int maxToAdd = (endIndex-startIndex) * numToAdd;
+               final int extendedSize = track.getNumPoints() + maxToAdd;
+               DataPoint[] oldPoints = track.cloneContents();
+               DataPoint[] newPoints = new DataPoint[extendedSize];
+               // Copy points before
+               System.arraycopy(oldPoints, 0, newPoints, 0, startIndex);
+               // Loop, copying points and interpolating
+               int destIndex = startIndex;
+               DataPoint prevPoint = null;
+               for (int i=startIndex; i<= endIndex; i++)
+               {
+                       DataPoint p = _app.getTrackInfo().getTrack().getPoint(i);
+                       if (prevPoint != null && ((p.isWaypoint() && betweenWaypoints) || (!p.isWaypoint() && !p.getSegmentStart())))
+                       {
+                               // interpolate between the previous point and this one
+                               DataPoint[] addition = prevPoint.interpolate(p, numToAdd);
+                               System.arraycopy(addition, 0, newPoints, destIndex, numToAdd);
+                               destIndex += numToAdd;
+                       }
+                       // copy point
+                       newPoints[destIndex] = p;
+                       destIndex++;
+                       if (!p.isWaypoint() || betweenWaypoints)
+                       {
+                               prevPoint = p;
+                       }
+                       else if (!p.isWaypoint()) {
+                               prevPoint = null;
+                       }
+                       // If it's a waypoint, then keep the old prevPoint
+               }
+               final int totalInserted = destIndex - endIndex - 1;
+               // Copy the points after the selected range
+               System.arraycopy(oldPoints, endIndex, newPoints, destIndex-1, track.getNumPoints()-endIndex);
+
+               // If necessary, make a new array of the correct size and do another arraycopy into it
+               final int newTotalPoints = track.getNumPoints() + totalInserted;
+               if (newTotalPoints != newPoints.length)
+               {
+                       DataPoint[] croppedPoints = new DataPoint[newTotalPoints];
+                       System.arraycopy(newPoints, 0, croppedPoints, 0, newTotalPoints);
+                       newPoints = croppedPoints;
+               }
+
+               // Make undo object
+               UndoInterpolate undo = new UndoInterpolate(_app.getTrackInfo(), totalInserted);
+               // Replace track with new points array
+               if (track.replaceContents(newPoints))
+               {
+                       _app.completeFunction(undo, I18nManager.getText("confirm.interpolate"));
+                       // Alter selection
+                       _app.getTrackInfo().getSelection().selectRange(startIndex, endIndex + totalInserted);
+               }
+       }
+
+       /**
+        * Check if the given Track has trackpoints in the specified range
+        * @param inTrack track object
+        * @param inStart start index
+        * @param inEnd end index
+        * @return true if there are any non-waypoints in the range
+        */
+       private static boolean selectedRangeHasTrackpoints(Track inTrack, int inStart, int inEnd)
+       {
+               for (int i=inStart; i<= inEnd; i++)
+               {
+                       DataPoint p = inTrack.getPoint(i);
+                       if (p != null && !p.isWaypoint()) {
+                               return true;
+                       }
+               }
+               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;
+       }
+}
index 23fe8d285afd8af965a47e62f3fcdc0827a12603..7587949b25c893e5a4c9565d69d6a723ae603ec2 100644 (file)
@@ -75,8 +75,8 @@ public class PasteCoordinates extends GenericFunction
                // MAYBE: Paste clipboard into the edit field
                _coordField.setText("");
                _nameField.setText("");
-               boolean metric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
-               _altUnitsDropDown.setSelectedIndex(metric?0:1);
+               boolean useMetres = (Config.getUnitSet().getDefaultAltitudeFormat() == Altitude.Format.METRES);
+               _altUnitsDropDown.setSelectedIndex(useMetres?0:1);
                enableOK();
                _dialog.setVisible(true);
        }
diff --git a/tim/prune/function/PhotoComparer.java b/tim/prune/function/PhotoComparer.java
new file mode 100644 (file)
index 0000000..86252d1
--- /dev/null
@@ -0,0 +1,109 @@
+package tim.prune.function;
+
+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;
+
+       /**
+        * Constructor
+        * @param inMode sort mode
+        */
+       public PhotoComparer(SortMode inMode)
+       {
+               _sortMode = inMode;
+       }
+
+       /**
+        * Main compare method
+        */
+       public int compare(DataPoint inP1, DataPoint inP2)
+       {
+               if (inP2 == null || inP2.getPhoto() == null) return -1; // all nulls at end
+               if (inP1 == null || inP1.getPhoto() == null) return 1;
+               // Sort by name
+               int result = 0;
+               if (_sortMode == SortMode.SORTBY_NAME) {
+                       result = compareNames(inP1, inP2);
+               }
+               if (result == 0) {
+                       result = compareTimes(inP1, inP2);
+               }
+               // check names if times didn't work
+               if (result == 0 && _sortMode == SortMode.SORTBY_TIME) {
+                       result = compareNames(inP1, inP2);
+               }
+               // names and times equal, try width and height
+               if (result == 0) {
+                       result = compareSizes(inP1, inP2);
+               }
+               return 0;
+       }
+
+       /**
+        * Compare the names of the two photo points
+        * @param inP1 first point
+        * @param inP2 second point
+        * @return compare value (-1,0,1)
+        */
+       private int compareNames(DataPoint inP1, DataPoint inP2)
+       {
+               // If the files can't be compared, use the photo names
+               if (inP1.getPhoto().getFile() == null || inP2.getPhoto().getFile() == null) {
+                       return inP1.getPhoto().getName().compareTo(inP2.getPhoto().getName());
+               }
+               // both photos have files, so just compare the files
+               return inP1.getPhoto().getFile().compareTo(inP2.getPhoto().getFile());
+       }
+
+       /**
+        * Compare the timestamps of the two photo points
+        * @param inP1 first point
+        * @param inP2 second point
+        * @return compare value (-1,0,1)
+        */
+       private int compareTimes(DataPoint inP1, DataPoint inP2)
+       {
+               // Photos might not have timestamps
+               if (!inP2.hasTimestamp()) return -1;
+               if (!inP1.hasTimestamp()) return 1;
+               // Compare the timestamps
+               long secDiff = inP1.getPhoto().getTimestamp().getSecondsSince(inP2.getPhoto().getTimestamp());
+               return (secDiff<0?-1:(secDiff==0?0:1));
+       }
+
+       /**
+        * Compare the sizes of the two photos
+        * @param inP1 first point
+        * @param inP2 second point
+        * @return compare value (-1,0,1)
+        */
+       private int compareSizes(DataPoint inP1, DataPoint inP2)
+       {
+               // Try the widths
+               int w1 = inP1.getPhoto().getWidth();
+               int w2 = inP2.getPhoto().getWidth();
+               if (w2 <= 0) return -1;
+               if (w1 <= 0) return 1;
+               if (w1 != w2) return (w2 > w1 ? 1 : -1);
+               // Try the heights
+               int h1 = inP1.getPhoto().getHeight();
+               int h2 = inP2.getPhoto().getHeight();
+               if (h2 <= 0) return -1;
+               if (h1 <= 0) return 1;
+               if (h1 != h2) return (h2 > h1 ? 1 : -1);
+               // sizes same
+               return 0;
+       }
+}
index 1a81be110ad7636a6a314b8e44942be8e71887b1..da9c5ca127d869ca64be4fb56fe63049a800c7b2 100644 (file)
@@ -61,7 +61,14 @@ public class PhotoPopupFunction extends GenericFunction
                        _frame.setLocationRelativeTo(_parentFrame);
                }
                initFrame();
-               _frame.setVisible(true);
+               final Photo photo = _app.getTrackInfo().getCurrentPhoto();
+               if (photo.getWidth() <= 0 || photo.getHeight() <= 0) {
+                       _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.showphoto.failed")
+                        + " : " + photo.getName());
+               }
+               else {
+                       _frame.setVisible(true);
+               }
        }
 
        /**
index 0e14d7c9ad1f9f6c9f4df9f761b7c466838b751d..2ac71e93b6e38bbbaab727aa1a6d7d421c4a117a 100644 (file)
@@ -1,8 +1,12 @@
 package tim.prune.function;
 
+import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
 import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;
 import javax.sound.sampled.Clip;
@@ -58,39 +62,34 @@ public class PlayAudioFunction extends GenericFunction implements Runnable
                {
                        // First choice is to play using java
                        played = playClip(audio);
-                       // Second choice is to try the Desktop library from java 6, if available
+                       // If this didn't work, then try to play the file another way
                        if (!played) {
-                               try {
-                                       Class<?> d = Class.forName("java.awt.Desktop");
-                                       d.getDeclaredMethod("open", new Class[] {File.class}).invoke(
-                                               d.getDeclaredMethod("getDesktop").invoke(null), new Object[] {audioFile});
-                                       //above code mimics: Desktop.getDesktop().open(audioFile);
-                                       played = true;
-                               }
-                               catch (Exception ignore) {
-                                       played = false;
-                               }
+                               played = playAudioFile(audioFile);
                        }
-                       // If the Desktop call failed, need to try backup methods
+               }
+               else if (audioFile == null && audio.getByteData() != null)
+               {
+                       // Try to play audio clip using byte array
+                       played = playClip(audio);
+                       // If this didn't work, then need to copy the byte data to a file and play it from there
                        if (!played)
                        {
-                               // If system looks like a Mac, try open command
-                               String osName = System.getProperty("os.name").toLowerCase();
-                               boolean isMacOsx = osName.indexOf("mac os") >= 0 || osName.indexOf("darwin") >= 0;
-                               if (isMacOsx) {
-                                       String[] command = new String[] {"open", audioFile.getAbsolutePath()};
-                                       try {
-                                               Runtime.getRuntime().exec(command);
-                                               played = true;
-                                       }
-                                       catch (IOException ioe) {}
+                               try
+                               {
+                                       String suffix = getSuffix(audio.getName());
+                                       File tempFile = File.createTempFile("gpsaudio", suffix);
+                                       tempFile.deleteOnExit();
+                                       // Copy byte data to this file
+                                       BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tempFile));
+                                       bos.write(audio.getByteData(), 0, audio.getByteData().length);
+                                       bos.close();
+                                       played = playAudioFile(tempFile);
+                               }
+                               catch (IOException ignore) {
+                                       System.err.println("Error: " + ignore.getClass().getName() + " - " + ignore.getMessage());
                                }
                        }
                }
-               else if (audioFile == null && audio.getByteData() != null) {
-                       // Try to play audio clip using byte array (can't use Desktop or Runtime)
-                       played = playClip(audio);
-               }
                if (!played)
                {
                        // If still not worked, show error message
@@ -134,6 +133,54 @@ public class PlayAudioFunction extends GenericFunction implements Runnable
                return success;
        }
 
+       /**
+        * Try to play the specified audio file
+        * @param inFile file to play
+        * @return true if play was successful
+        */
+       private boolean playAudioFile(File inFile)
+       {
+               boolean played = false;
+               // Try the Desktop library from java 6, if available
+               if (!played)
+               {
+                       try
+                       {
+                               Class<?> d = Class.forName("java.awt.Desktop");
+                               d.getDeclaredMethod("open", new Class[] {File.class}).invoke(
+                                       d.getDeclaredMethod("getDesktop").invoke(null), new Object[] {inFile});
+                               //above code mimics: Desktop.getDesktop().open(audioFile);
+                               played = true;
+                       }
+                       catch (InvocationTargetException e) {
+                               System.err.println("ITE: " + e.getCause().getClass().getName() + " - " + e.getCause().getMessage());
+                               played = false;
+                       }
+                       catch (Exception ignore) {
+                               System.err.println(ignore.getClass().getName() + " - " + ignore.getMessage());
+                               played = false;
+                       }
+               }
+
+               // If the Desktop call failed, need to try backup methods
+               if (!played)
+               {
+                       // If system looks like a Mac, try the open command
+                       String osName = System.getProperty("os.name").toLowerCase();
+                       boolean isMacOsx = osName.indexOf("mac os") >= 0 || osName.indexOf("darwin") >= 0;
+                       if (isMacOsx)
+                       {
+                               String[] command = new String[] {"open", inFile.getAbsolutePath()};
+                               try {
+                                       Runtime.getRuntime().exec(command);
+                                       played = true;
+                               }
+                               catch (IOException ioe) {}
+                       }
+               }
+               return played;
+       }
+
        /**
         * Try to stop a currently playing clip
         */
@@ -163,4 +210,16 @@ public class PlayAudioFunction extends GenericFunction implements Runnable
                }
                return percent;
        }
+
+       /**
+        * @param inName name of audio file
+        * @return suffix (rest of name after the dot) - expect mp3, wav, ogg
+        */
+       private static final String getSuffix(String inName)
+       {
+               if (inName == null || inName.equals("")) {return ".tmp";}
+               final int dotPos = inName.lastIndexOf('.');
+               if (dotPos < 0) {return inName;} // no dot found
+               return inName.substring(dotPos);
+       }
 }
index b8d9312de7dce3c1c349c466ec101b8fe72c3d56..c55928021fcaaa08af3f84fa516bdf8f927f1469 100644 (file)
@@ -5,7 +5,6 @@ import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.Arrays;
-import java.util.Comparator;
 
 import javax.swing.BoxLayout;
 import javax.swing.ButtonGroup;
@@ -193,32 +192,7 @@ public class RearrangePhotosFunction extends GenericFunction
         */
        private static void sortPhotos(DataPoint[] inPhotos, boolean inSortByFile)
        {
-               Comparator<DataPoint> comparator = null;
-               if (inSortByFile)
-               {
-                       // sort by filename
-                       comparator = new Comparator<DataPoint>() {
-                               public int compare(DataPoint inP1, DataPoint inP2) {
-                                       if (inP2 == null) return -1; // all nulls at end
-                                       if (inP1 == null) return 1;
-                                       if (inP1.getPhoto().getFile() == null || inP2.getPhoto().getFile() == null)
-                                               return inP1.getPhoto().getName().compareTo(inP2.getPhoto().getName());
-                                       return inP1.getPhoto().getFile().compareTo(inP2.getPhoto().getFile());
-                               }
-                       };
-               }
-               else
-               {
-                       // sort by photo timestamp
-                       comparator = new Comparator<DataPoint>() {
-                               public int compare(DataPoint inP1, DataPoint inP2) {
-                                       if (inP2 == null) return -1; // all nulls at end
-                                       if (inP1 == null) return 1;
-                                       long secDiff = inP1.getPhoto().getTimestamp().getSecondsSince(inP2.getPhoto().getTimestamp());
-                                       return (secDiff<0?-1:(secDiff==0?0:1));
-                               }
-                       };
-               }
-               Arrays.sort(inPhotos, comparator);
+               PhotoComparer comparer = new PhotoComparer(inSortByFile ? PhotoComparer.SortMode.SORTBY_NAME : PhotoComparer.SortMode.SORTBY_TIME);
+               Arrays.sort(inPhotos, comparer);
        }
 }
index ab24522eb68120168b49c420996f245ea8ffb32d..0458943036ed8007ffecb0838bf66c504aff4e6d 100644 (file)
@@ -130,22 +130,29 @@ public class SearchWikipediaNames extends GenericDownloaderFunction
                _statusLabel.setText(descMessage);
        }
 
+
        /**
-        * Load the selected track or point
+        * Load the selected point(s)
         */
        protected void loadSelected()
        {
-               // Find the row selected in the table and get the corresponding track
-               int rowNum = _trackTable.getSelectedRow();
-               if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
+               // Find the rows selected in the table and get the corresponding coords
+               int numSelected = _trackTable.getSelectedRowCount();
+               if (numSelected < 1) return;
+               int[] rowNums = _trackTable.getSelectedRows();
+               for (int i=0; i<numSelected; i++)
                {
-                       String coords = _trackListModel.getTrack(rowNum).getDownloadLink();
-                       String[] latlon = coords.split(",");
-                       if (latlon.length == 2)
+                       int rowNum = rowNums[i];
+                       if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
                        {
-                               DataPoint point = new DataPoint(new Latitude(latlon[0]), new Longitude(latlon[1]), null);
-                               point.setFieldValue(Field.WAYPT_NAME, _trackListModel.getTrack(rowNum).getTrackName(), false);
-                               _app.createPoint(point);
+                               String coords = _trackListModel.getTrack(rowNum).getDownloadLink();
+                               String[] latlon = coords.split(",");
+                               if (latlon.length == 2)
+                               {
+                                       DataPoint point = new DataPoint(new Latitude(latlon[0]), new Longitude(latlon[1]), null);
+                                       point.setFieldValue(Field.WAYPT_NAME, _trackListModel.getTrack(rowNum).getTrackName(), false);
+                                       _app.createPoint(point);
+                               }
                        }
                }
                // Close the dialog
index 65565987529d3885d72a38d12a7b545317e22ed9..04e3cc14e60e5d01a1cb21f02d8cea9b73fb106b 100644 (file)
@@ -395,18 +395,13 @@ public class ManageCacheFunction extends GenericFunction implements Runnable
                                if (subdir.isDirectory()) {
                                        numDeleted += deleteFilesFrom(subdir, inMaxDays);
                                }
-                               else if (subdir.isFile() && subdir.exists())
+                               else if (subdir.isFile() && subdir.exists() && _TILEFILTER.accept(subdir))
                                {
-                                       boolean isTileFile = _TILEFILTER.accept(subdir);
-                                       boolean isBadFile = !isTileFile && subdir.getName().toLowerCase().endsWith("png");
-                                       if (isTileFile || isBadFile)
+                                       long fileAge = (now - subdir.lastModified()) / 1000 / 60 / 60 / 24;
+                                       if (inMaxDays < 0 || fileAge > inMaxDays)
                                        {
-                                               long fileAge = (now - subdir.lastModified()) / 1000 / 60 / 60 / 24;
-                                               if (inMaxDays < 0 || fileAge > inMaxDays || isBadFile)
-                                               {
-                                                       if (subdir.delete()) {
-                                                               numDeleted++;
-                                                       }
+                                               if (subdir.delete()) {
+                                                       numDeleted++;
                                                }
                                        }
                                }
index dc90abd65066d6cd16868ca3a1ce693ec4a5f642..8c7ee23d00d39133b269268994f9c1174506d91f 100644 (file)
@@ -86,6 +86,7 @@ public class RowInfo
 
        /**
         * Mark that an unexpected file or directory was found
+        * TODO: Is this needed?
         */
        public void foundUnexpected() {
                _unexpected = true;
index 502c91439dc1434085e42cce1165352708e10ab5..0bb377fe2b1c4fe728b1020c1fe6799456ce6a66 100644 (file)
@@ -29,13 +29,13 @@ import tim.prune.ExternalTools;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.config.Config;
-import tim.prune.data.Altitude;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Distance;
 import tim.prune.data.Field;
 import tim.prune.data.Timestamp;
 import tim.prune.data.Track;
-import tim.prune.data.Distance.Units;
+import tim.prune.gui.profile.SpeedData;
+import tim.prune.gui.profile.VerticalSpeedData;
 import tim.prune.load.GenericFileFilter;
 
 /**
@@ -219,7 +219,7 @@ public class Charter extends GenericFunction
        private boolean setupDialog(Track inTrack)
        {
                boolean hasTimes = inTrack.hasData(Field.TIMESTAMP);
-               boolean hasAltitudes = inTrack.getAltitudeRange().hasRange();
+               boolean hasAltitudes = inTrack.hasAltitudeData();
                _timeRadio.setEnabled(hasTimes);
 
                // Add checks to prevent choosing unavailable combinations
@@ -397,9 +397,15 @@ public class Charter extends GenericFunction
                        catch (Exception e) {}
                }
 
+               // Sort out units to use
+               final String distLabel = I18nManager.getText(Config.getUnitSet().getDistanceUnit().getShortnameKey());
+               final String altLabel  = I18nManager.getText(Config.getUnitSet().getAltitudeUnit().getShortnameKey());
+               final String speedLabel = I18nManager.getText(Config.getUnitSet().getSpeedUnit().getShortnameKey());
+               final String vertSpeedLabel = I18nManager.getText(Config.getUnitSet().getVerticalSpeedUnit().getShortnameKey());
+
                // Set x axis label
                if (inDistance) {
-                       inWriter.write("set xlabel '" + I18nManager.getText("fieldname.distance") + " (" + getUnitsLabel("units.kilometres.short", "units.miles.short") + ")'\n");
+                       inWriter.write("set xlabel '" + I18nManager.getText("fieldname.distance") + " (" + distLabel + ")'\n");
                }
                else {
                        inWriter.write("set xlabel '" + I18nManager.getText("fieldname.time") + " (" + I18nManager.getText("units.hours") + ")'\n");
@@ -410,19 +416,19 @@ public class Charter extends GenericFunction
                switch (inYaxis)
                {
                case 0: // y axis is distance
-                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.distance") + " (" + getUnitsLabel("units.kilometres.short", "units.miles.short") + ")'\n");
+                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.distance") + " (" + distLabel + ")'\n");
                        chartTitle = I18nManager.getText("fieldname.distance");
                        break;
                case 1: // y axis is altitude
-                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.altitude") + " (" + getUnitsLabel("units.metres.short", "units.feet.short") + ")'\n");
+                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.altitude") + " (" + altLabel + ")'\n");
                        chartTitle = I18nManager.getText("fieldname.altitude");
                        break;
                case 2: // y axis is speed
-                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.speed") + " (" + getUnitsLabel("units.kmh", "units.mph") + ")'\n");
+                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.speed") + " (" + speedLabel + ")'\n");
                        chartTitle = I18nManager.getText("fieldname.speed");
                        break;
                case 3: // y axis is vertical speed
-                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.verticalspeed") + " (" + getUnitsLabel("units.metrespersec", "units.feetpersec") + ")'\n");
+                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.verticalspeed") + " (" + vertSpeedLabel + ")'\n");
                        chartTitle = I18nManager.getText("fieldname.verticalspeed");
                        break;
                }
@@ -430,18 +436,6 @@ public class Charter extends GenericFunction
                inWriter.write("plot '" + tempFile.getAbsolutePath() + "' title '" + chartTitle + "' with filledcurve y1=0 lt rgb \"#009000\"\n");
        }
 
-       /**
-        * Get the units label for the given keys
-        * @param inMetric key if metric
-        * @param inImperial key if imperial
-        * @return display label with appropriate text
-        */
-       private static String getUnitsLabel(String inMetric, String inImperial)
-       {
-               String key = Config.getConfigBoolean(Config.KEY_METRIC_UNITS)?inMetric:inImperial;
-               return I18nManager.getText(key);
-       }
-
 
        /**
         * Calculate the distance values for each point in the given track
@@ -461,11 +455,10 @@ public class Charter extends GenericFunction
                        {
                                totalRads += DataPoint.calculateRadiansBetween(prevPoint, currPoint);
                        }
-                       if (Config.getConfigBoolean(Config.KEY_METRIC_UNITS)) {
-                               values.setData(i, Distance.convertRadiansToDistance(totalRads, Units.KILOMETRES));
-                       } else {
-                               values.setData(i, Distance.convertRadiansToDistance(totalRads, Units.MILES));
-                       }
+
+                       // distance values use currently configured units
+                       values.setData(i, Distance.convertRadiansToDistance(totalRads));
+
                        prevPoint = currPoint;
                }
                return values;
@@ -506,10 +499,10 @@ public class Charter extends GenericFunction
        private static ChartSeries getAltitudeValues(Track inTrack)
        {
                ChartSeries values = new ChartSeries(inTrack.getNumPoints());
-               Altitude.Format altFormat = Config.getConfigBoolean(Config.KEY_METRIC_UNITS)?Altitude.Format.METRES:Altitude.Format.FEET;
+               final double multFactor = Config.getUnitSet().getAltitudeUnit().getMultFactorFromStd();
                for (int i=0; i<inTrack.getNumPoints(); i++) {
                        if (inTrack.getPoint(i).hasAltitude()) {
-                               values.setData(i, inTrack.getPoint(i).getAltitude().getValue(altFormat));
+                               values.setData(i, inTrack.getPoint(i).getAltitude().getMetricValue() * multFactor);
                        }
                }
                return values;
@@ -522,31 +515,17 @@ public class Charter extends GenericFunction
         */
        private static ChartSeries getSpeedValues(Track inTrack)
        {
-               // Calculate speeds and fill in in values array
-               ChartSeries values = new ChartSeries(inTrack.getNumPoints());
-               DataPoint prevPoint = null, currPoint = null, nextPoint = null;
-               DataPoint[] points = getDataPoints(inTrack, false);
-               final boolean useMetric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
+               // Calculate speeds using the same formula as the profile chart
+               SpeedData speeds = new SpeedData(inTrack);
+
+               final int numPoints = inTrack.getNumPoints();
+               ChartSeries values = new ChartSeries(numPoints);
                // Loop over collected points
-               for (int i=1; i<(points.length-1); i++)
+               for (int i=0; i<numPoints; i++)
                {
-                       prevPoint = points[i-1];
-                       currPoint = points[i];
-                       nextPoint = points[i+1];
-                       if (prevPoint != null && currPoint != null && nextPoint != null
-                               && nextPoint.getTimestamp().isAfter(currPoint.getTimestamp())
-                               && currPoint.getTimestamp().isAfter(prevPoint.getTimestamp()))
+                       if (speeds.hasData(i))
                        {
-                               // Calculate average speed between prevPoint and nextPoint
-                               double rads = DataPoint.calculateRadiansBetween(prevPoint, currPoint)
-                                       + DataPoint.calculateRadiansBetween(currPoint, nextPoint);
-                               double time = nextPoint.getTimestamp().getSecondsSince(prevPoint.getTimestamp()) / 60.0 / 60.0;
-                               // Convert to distance and pass to chartseries
-                               if (useMetric) {
-                                       values.setData(i, Distance.convertRadiansToDistance(rads, Units.KILOMETRES) / time);
-                               } else {
-                                       values.setData(i, Distance.convertRadiansToDistance(rads, Units.MILES) / time);
-                               }
+                               values.setData(i, speeds.getData(i));
                        }
                }
                return values;
@@ -559,59 +538,23 @@ public class Charter extends GenericFunction
         */
        private static ChartSeries getVertSpeedValues(Track inTrack)
        {
-               // Calculate speeds and fill in in values array
-               ChartSeries values = new ChartSeries(inTrack.getNumPoints());
-               Altitude.Format altFormat = Config.getConfigBoolean(Config.KEY_METRIC_UNITS)?Altitude.Format.METRES:Altitude.Format.FEET;
-               DataPoint prevPoint = null, currPoint = null, nextPoint = null;
-               DataPoint[] points = getDataPoints(inTrack, true); // require that points have altitudes too
+               // Calculate speeds using the same formula as the profile chart
+               VerticalSpeedData speeds = new VerticalSpeedData(inTrack);
+
+               final int numPoints = inTrack.getNumPoints();
+               ChartSeries values = new ChartSeries(numPoints);
                // Loop over collected points
-               for (int i=1; i<(points.length-1); i++)
+               for (int i=0; i<numPoints; i++)
                {
-                       prevPoint = points[i-1];
-                       currPoint = points[i];
-                       nextPoint = points[i+1];
-                       if (prevPoint != null && currPoint != null && nextPoint != null
-                               && nextPoint.getTimestamp().isAfter(currPoint.getTimestamp())
-                               && currPoint.getTimestamp().isAfter(prevPoint.getTimestamp()))
+                       if (speeds.hasData(i))
                        {
-                               // Calculate average vertical speed between prevPoint and nextPoint
-                               double vspeed = (nextPoint.getAltitude().getValue(altFormat) - prevPoint.getAltitude().getValue(altFormat))
-                                * 1.0 / nextPoint.getTimestamp().getSecondsSince(prevPoint.getTimestamp());
-                               values.setData(i, vspeed);
+                               values.setData(i, speeds.getData(i));
                        }
                }
                return values;
        }
 
 
-       /**
-        * Get an array of DataPoints with data for the charts
-        * @param inTrack track object containing points
-        * @param inRequireAltitudes true if only points with altitudes are considered
-        * @return array of points with contiguous non-null elements (<= size) with timestamps
-        */
-       private static DataPoint[] getDataPoints(Track inTrack, boolean inRequireAltitudes)
-       {
-               DataPoint[] points = new DataPoint[inTrack.getNumPoints()];
-               DataPoint currPoint = null;
-               int pointNum = 0;
-               // Loop over all points
-               for (int i=0; i<inTrack.getNumPoints(); i++)
-               {
-                       currPoint = inTrack.getPoint(i);
-                       if (currPoint != null && !currPoint.isWaypoint() && currPoint.hasTimestamp()
-                               && (!inRequireAltitudes || currPoint.hasAltitude()))
-                       {
-                               points[pointNum] = currPoint;
-                               pointNum++;
-                       }
-               }
-               // Any elements at the end of the array will stay null
-               // Also note, chronological order is not checked
-               return points;
-       }
-
-
        /**
         * Select a file to write for the SVG output
         * @return selected File object or null if cancelled
index d4ecc8898e59208de1c1aa815c7067860e5bbc7e..5aa76aa76a1c570c89330fafab5192a329082b8a 100644 (file)
@@ -11,6 +11,7 @@ import javax.swing.Box;
 import javax.swing.BoxLayout;
 import javax.swing.JButton;
 import javax.swing.JDialog;
+import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 
 import tim.prune.App;
@@ -169,14 +170,27 @@ public class CompressTrackFunction extends GenericFunction
        {
                boolean[] deleteFlags = preview();
                // All flags are now combined in deleteFlags array
+               int numMarked = 0;
                for (int i=0; i<deleteFlags.length; i++)
                {
                        DataPoint point = _track.getPoint(i);
-                       point.setMarkedForDeletion(deleteFlags[i] && !point.hasMedia());
+                       boolean deletePoint = deleteFlags[i] && !point.hasMedia();
+                       point.setMarkedForDeletion(deletePoint);
+                       if (deletePoint) numMarked++;
                }
 
                // Close dialog and inform listeners
                UpdateMessageBroker.informSubscribers();
                _dialog.dispose();
+               // Show confirmation dialog with OK button (not status bar message)
+               if (numMarked > 0) {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.compress.confirm1")
+                               + " " + numMarked + " " + I18nManager.getText("dialog.compress.confirm2"),
+                               I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+               }
+               else {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.compress.confirmnone"),
+                               I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+               }
        }
 }
index cd1f9dfac0073a10de728a9496cdf371c3bde00c..2cd5d77f9db184ea7d5c2532734601ee7ab589ac 100644 (file)
@@ -78,6 +78,7 @@ public abstract class CompressionAlgorithm
                if (isActivated())
                {
                        // Run the compression and set the deletion flags
+                       _trackDetails.initialise();
                        numDeleted = compress(inFlags);
                        _summaryLabel.setValue(numDeleted);
                }
diff --git a/tim/prune/function/compress/MarkPointsInRectangleFunction.java b/tim/prune/function/compress/MarkPointsInRectangleFunction.java
new file mode 100644 (file)
index 0000000..73c867f
--- /dev/null
@@ -0,0 +1,103 @@
+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
+{
+       /** 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;
+
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public MarkPointsInRectangleFunction(App inApp)
+       {
+               super(inApp);
+       }
+
+       /**
+        * Set the coordinates of the rectangle
+        * @param inLon1 first longitude value
+        * @param inLat1 first latitude value
+        * @param inLon2 second longitude value
+        * @param inLat2 second latitude value
+        */
+       public void setRectCoords(double inLon1, double inLat1, double inLon2, double inLat2)
+       {
+               if (inLon1 == inLon2 || inLat1 == inLat2)
+               {
+                       // Coordinates not valid
+                       _minLat = _maxLat = _minLon = _maxLon = 0.0;
+               }
+               else
+               {
+                       if (inLon2 > inLon1) {
+                               _minLon = inLon1; _maxLon = inLon2;
+                       }
+                       else {
+                               _minLon = inLon2; _maxLon = inLon1;
+                       }
+                       if (inLat2 > inLat1) {
+                               _minLat = inLat1; _maxLat = inLat2;
+                       }
+                       else {
+                               _minLat = inLat2; _maxLat = inLat1;
+                       }
+               }
+       }
+
+       /**
+        * Begin the function using the set parameters
+        */
+       public void begin()
+       {
+               if (_maxLon == _minLon || _maxLat == _minLat) {
+                       return;
+               }
+
+               // Loop over all points in track
+               final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
+               int numMarked = 0;
+               for (int i=0; i<numPoints; i++)
+               {
+                       DataPoint point = _app.getTrackInfo().getTrack().getPoint(i);
+                       // For each point, see if it's within the rectangle
+                       final double pointLon = point.getLongitude().getDouble();
+                       final double pointLat = point.getLatitude().getDouble();
+                       final boolean insideRect = (pointLon >= _minLon && pointLon <= _maxLon
+                               && pointLat >= _minLat && pointLat <= _maxLat);
+                       // If so, then mark it
+                       point.setMarkedForDeletion(insideRect);
+                       if (insideRect) {
+                               numMarked++;
+                       }
+               }
+
+               // Inform subscribers to update display
+               UpdateMessageBroker.informSubscribers();
+               // Confirm message showing how many marked
+               if (numMarked > 0) {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.compress.confirm1")
+                               + " " + numMarked + " " + I18nManager.getText("dialog.compress.confirm2"),
+                               I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+               }
+       }
+
+       /** @return name key */
+       public String getNameKey() {
+               return "menu.track.markrectangle";
+       }
+}
index dada3f0bd510c4e6163460d69b474fbd1679c6dd..5fe6e7e7d9e307d901ad25c23b2a4ea021acc3d3 100644 (file)
@@ -7,6 +7,8 @@ import java.awt.FlowLayout;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
 import java.util.ArrayList;
 
 import javax.swing.BorderFactory;
@@ -123,6 +125,17 @@ public class DistanceFunction extends GenericFunction
                mainPanel.add(scrollPane);
                dialogPanel.add(mainPanel, BorderLayout.CENTER);
 
+               // close window if escape pressed
+               KeyAdapter escListener = new KeyAdapter() {
+                       public void keyReleased(KeyEvent inE) {
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
+                                       _dialog.dispose();
+                               }
+                       }
+               };
+               _pointTable.addKeyListener(escListener);
+               distTable.addKeyListener(escListener);
+
                // button panel at bottom
                JPanel buttonPanel = new JPanel();
                buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
index 48ba1722be3bedf224ec28ed14fe362129d8fedf..faa059ea4b1bb2ada4ce6340b30bc6014a9ce65a 100644 (file)
@@ -4,6 +4,7 @@ import tim.prune.I18nManager;
 import tim.prune.config.Config;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Distance;
+import tim.prune.data.Unit;
 
 /**
  * Class to hold the table model for the distances table
@@ -12,10 +13,6 @@ public class DistanceTableModel extends GenericTableModel
 {
        /** Distances */
        private double[] _distances = null;
-       /** Metric distances? */
-       private boolean _useMetric = true;
-       /** Previous value of metric flag (to spot changes) */
-       private boolean _prevUseMetric = false;
        /** Column heading */
        private static final String _toColLabel = I18nManager.getText("dialog.distances.column.to");
        /** Column heading (depends on metric/imperial settings) */
@@ -68,9 +65,10 @@ public class DistanceTableModel extends GenericTableModel
         */
        public void recalculate(int inIndex)
        {
-               // Use metric or not?
-               _useMetric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
-               _distanceLabel = getDistanceLabel(_useMetric);
+               // Which units to use?
+               Unit distUnit = Config.getUnitSet().getDistanceUnit();
+               _distanceLabel = I18nManager.getText("fieldname.distance") + " (" +
+                       I18nManager.getText(distUnit.getShortnameKey()) + ")";
                // Initialize array of distances
                int numRows = getRowCount();
                if (_distances == null || _distances.length != numRows) {
@@ -83,26 +81,10 @@ public class DistanceTableModel extends GenericTableModel
                        }
                        else {
                                double rads = DataPoint.calculateRadiansBetween(fromPoint, _pointList.get(i));
-                               _distances[i] = Distance.convertRadiansToDistance(rads, _useMetric?Distance.Units.KILOMETRES:Distance.Units.MILES);
+                               _distances[i] = Distance.convertRadiansToDistance(rads);
                        }
                }
-               // Let table know that it has to refresh data (and maybe refresh column headings too)
-               if (_useMetric == _prevUseMetric) {
-                       fireTableDataChanged();
-               }
-               else {
-                       fireTableStructureChanged();
-               }
-               _prevUseMetric = _useMetric;
-       }
-
-       /**
-        * @param inMetric true to use metric distances
-        * @return distance label for column heading
-        */
-       private static String getDistanceLabel(boolean inMetric)
-       {
-               return I18nManager.getText("fieldname.distance") + " (" +
-                       I18nManager.getText(inMetric?"units.kilometres.short" : "units.miles.short") + ")";
+               // Let table know that it has to refresh data (and might as well refresh column headings too)
+               fireTableStructureChanged();
        }
 }
index 3fc630e2b96506789f36ad0ee7878d13fee8bb0d..bfa46c4bf4d54f9df5f4e9101b859903a26cd172 100644 (file)
@@ -125,16 +125,16 @@ public class PointNameEditor extends GenericFunction
                        }
                });
                rightPanel.add(lowerButton);
-               JButton sentenceButton = new JButton(I18nManager.getText("dialog.pointnameedit.sentencecase"));
-               sentenceButton.addActionListener(new ActionListener() {
+               JButton titleButton = new JButton(I18nManager.getText("dialog.pointnameedit.titlecase"));
+               titleButton.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
                        {
-                               _nameField.setText(sentenceCase(_nameField.getText()));
+                               _nameField.setText(titleCase(_nameField.getText()));
                                _okButton.setEnabled(true);
                                _nameField.requestFocus();
                        }
                });
-               rightPanel.add(sentenceButton);
+               rightPanel.add(titleButton);
                panel.add(rightPanel, BorderLayout.EAST);
                // Bottom panel for OK, cancel buttons
                JPanel lowerPanel = new JPanel();
@@ -167,11 +167,11 @@ public class PointNameEditor extends GenericFunction
        }
 
        /**
-        * Turn a String into sentence case by capitalizing each word
+        * Turn a String into title case by capitalizing each word
         * @param inString String to convert
         * @return capitalized String
         */
-       private static String sentenceCase(String inString)
+       private static String titleCase(String inString)
        {
                // Check first for empty strings
                if (inString == null || inString.equals(""))
index e9049a82b9a89705472601eefb0179c4a599729a..cf634c0fbc6b48b932382d21b3695b9e30ddc575 100644 (file)
@@ -18,7 +18,6 @@ import javax.swing.JScrollPane;
 import javax.swing.JSplitPane;
 import javax.swing.JTable;
 import javax.swing.JTextArea;
-import javax.swing.ListSelectionModel;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 
@@ -112,21 +111,20 @@ public abstract class GenericDownloaderFunction extends GenericFunction implemen
                        public void valueChanged(ListSelectionEvent e) {
                                if (!e.getValueIsAdjusting())
                                {
-                                       if (_trackTable.getSelectedRow() >= 0
-                                        && _trackTable.getSelectedRow() < _trackListModel.getRowCount())
+                                       final int numSelected = _trackTable.getSelectedRowCount();
+                                       if (numSelected > 0)
                                        {
-                                               _loadButton.setEnabled(true);
-                                               _showButton.setEnabled(true);
                                                setDescription(_trackListModel.getTrack(_trackTable.getSelectedRow()).getDescription());
                                                _descriptionBox.setCaretPosition(0);
                                        }
                                        else {
                                                _descriptionBox.setText("");
                                        }
+                                       _loadButton.setEnabled(numSelected > 0);
+                                       _showButton.setEnabled(numSelected == 1);
                                }
                        }
                });
-               _trackTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // only allow one to be selected
                _trackTable.getColumnModel().getColumn(0).setPreferredWidth(300);
                if (_trackListModel.getColumnCount() > 1) {
                        _trackTable.getColumnModel().getColumn(1).setPreferredWidth(70);
index 51085cf14742a8ae57b6b4bf75930439ee1fb8b0..e60ed7429c4578e2bbfb44f8e3902dd69e102d63 100644 (file)
@@ -63,7 +63,6 @@ public class GetGpsiesFunction extends GenericDownloaderFunction
                _statusLabel.setText(I18nManager.getText("confirm.running"));
                // Act on callback to update list and send another request if necessary
                double[] coords = _app.getViewport().getBounds();
-               // Example http://www.gpsies.com/api.do?BBOX=10,51,12,53&limit=20&trackTypes=jogging&filetype=kml&device=Run.GPS
                int currPage = 1;
 
                ArrayList<GpsiesTrack> trackList = null;
@@ -73,6 +72,7 @@ public class GetGpsiesFunction extends GenericDownloaderFunction
                // Loop for each page of the results
                do
                {
+                       // Example http://ws.gpsies.com/api.do?BBOX=10,51,12,53&limit=20&resultPage=1&key=oumgvvbckiwpvsnb
                        String urlString = "http://ws.gpsies.com/api.do?BBOX=" +
                                coords[1] + "," + coords[0] + "," + coords[3] + "," + coords[2] +
                                "&limit=" + RESULTS_PER_PAGE + "&resultPage=" + currPage +
@@ -112,23 +112,30 @@ public class GetGpsiesFunction extends GenericDownloaderFunction
        }
 
        /**
-        * Load the selected track or point
+        * Load the selected track(s)
         */
        protected void loadSelected()
        {
-               // Find the row selected in the table and get the corresponding track
-               int rowNum = _trackTable.getSelectedRow();
-               if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
+               // Find the row(s) selected in the table and get the corresponding track
+               int numSelected = _trackTable.getSelectedRowCount();
+               if (numSelected < 1) return;
+               int[] rowNums = _trackTable.getSelectedRows();
+               for (int i=0; i<numSelected; i++)
                {
-                       String url = _trackListModel.getTrack(rowNum).getDownloadLink();
-                       XmlFileLoader xmlLoader = new XmlFileLoader(_app);
-                       ZipFileLoader loader = new ZipFileLoader(_app, xmlLoader);
-                       try
+                       int rowNum = rowNums[i];
+                       if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
                        {
-                               loader.openStream(new URL(url).openStream());
-                       }
-                       catch (IOException ioe) {
-                               System.err.println("IO Exception : " + ioe.getMessage());
+                               String url = _trackListModel.getTrack(rowNum).getDownloadLink();
+                               XmlFileLoader xmlLoader = new XmlFileLoader(_app);
+                               ZipFileLoader loader = new ZipFileLoader(_app, xmlLoader);
+                               if (i>0) _app.autoAppendNextFile();
+                               try
+                               {
+                                       loader.openStream(new URL(url).openStream());
+                               }
+                               catch (IOException ioe) {
+                                       System.err.println("IO Exception : " + ioe.getMessage());
+                               }
                        }
                }
                // Close the dialog
index 3a7326f647409b131d70964ad48279ac086ab275..904dd8bc62248d49fe4e5669ee37fccc19a643a3 100644 (file)
@@ -7,7 +7,7 @@ import javax.swing.table.AbstractTableModel;
 
 import tim.prune.I18nManager;
 import tim.prune.config.Config;
-import tim.prune.data.Distance;
+import tim.prune.data.Unit;
 
 /**
  * Model for list of tracks from gpsies.com
@@ -77,12 +77,11 @@ public class TrackListModel extends AbstractTableModel
                GpsiesTrack track = _trackList.get(inRowNum);
                if (inColNum == 0) {return track.getTrackName();}
                double lengthM = track.getLength();
-               if (Config.getConfigBoolean(Config.KEY_METRIC_UNITS)) {
-                       return _distanceFormatter.format(lengthM / 1000.0) + " " + I18nManager.getText("units.kilometres.short");
-               }
-               // must be imperial
-               return _distanceFormatter.format(Distance.convertMetresToMiles(lengthM))
-                       + " " + I18nManager.getText("units.miles.short");
+               // convert to current distance units
+               Unit distUnit = Config.getUnitSet().getDistanceUnit();
+               double length = lengthM * distUnit.getMultFactorFromStd();
+               // Make text
+               return _distanceFormatter.format(length) + " " + I18nManager.getText(distUnit.getShortnameKey());
        }
 
        /**
@@ -92,10 +91,15 @@ public class TrackListModel extends AbstractTableModel
        public void addTracks(ArrayList<GpsiesTrack> inList)
        {
                if (_trackList == null) {_trackList = new ArrayList<GpsiesTrack>();}
+               final int prevCount = _trackList.size();
                if (inList != null && inList.size() > 0) {
                        _trackList.addAll(inList);
                }
-               fireTableDataChanged();
+               final int updatedCount = _trackList.size();
+               if (prevCount <= 0)
+                       fireTableDataChanged();
+               else
+                       fireTableRowsInserted(prevCount, updatedCount-1);
        }
 
        /**
index 49bd000c79d99e688dfef33c0256210a218a1dd9..586cbb6d72eede3c5a62d77b68c8649b8845c5d8 100644 (file)
@@ -280,7 +280,7 @@ public class UploadGpsiesFunction extends GenericFunction
                                public void run() {
                                        boolean[] saveFlags = {true, true, true, true, false, true}; // export everything
                                        try {
-                                               GpxExporter.exportData(_writer, _app.getTrackInfo(), _nameField.getText(), null, saveFlags, false);
+                                               GpxExporter.exportData(_writer, _app.getTrackInfo(), _nameField.getText(), null, saveFlags, null);
                                        } catch (IOException e) {}
                                        finally {
                                                try {_writer.close();} catch (IOException e) {}
index ec68e162633dc66472138c20d6c1ac3a602c7303..7b3a4ff6843f9b34c4c7df048081a54f92ec17e6 100644 (file)
@@ -26,16 +26,19 @@ import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
 import tim.prune.config.Config;
-import tim.prune.data.Altitude;
+import tim.prune.data.AltitudeRange;
 import tim.prune.data.AudioClip;
 import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
-import tim.prune.data.Distance;
 import tim.prune.data.Field;
-import tim.prune.data.IntegerRange;
 import tim.prune.data.Photo;
 import tim.prune.data.Selection;
+import tim.prune.data.SpeedCalculator;
+import tim.prune.data.SpeedValue;
 import tim.prune.data.TrackInfo;
+import tim.prune.data.Unit;
+import tim.prune.data.UnitSet;
+import tim.prune.data.UnitSetLibrary;
 
 /**
  * Class to hold point details and selection details
@@ -47,7 +50,8 @@ public class DetailsDisplay extends GenericDisplay
        private JLabel _indexLabel = null;
        private JLabel _latLabel = null, _longLabel = null;
        private JLabel _altLabel = null;
-       private JLabel _timeLabel = null, _speedLabel = null;
+       private JLabel _timeLabel = null;
+       private JLabel _speedLabel = null, _vSpeedLabel = null;
        private JLabel _nameLabel = null, _typeLabel = null;
 
        // Range details
@@ -60,6 +64,7 @@ public class DetailsDisplay extends GenericDisplay
        // Photo details
        private JPanel _photoDetailsPanel = null;
        private JLabel _photoLabel = null;
+       private JLabel _photoPathLabel = null;
        private PhotoThumbnail _photoThumbnail = null;
        private JLabel _photoTimestampLabel = null;
        private JLabel _photoConnectedLabel = null;
@@ -69,6 +74,7 @@ public class DetailsDisplay extends GenericDisplay
        // Audio details
        private JPanel _audioDetailsPanel = null;
        private JLabel _audioLabel = null;
+       private JLabel _audioPathLabel = null;
        private JLabel _audioConnectedLabel = null;
        private JLabel _audioTimestampLabel = null;
        private JLabel _audioLengthLabel = null;
@@ -89,6 +95,8 @@ public class DetailsDisplay extends GenericDisplay
        private static final String LABEL_POINT_TIMESTAMP = 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_SPEED        = I18nManager.getText("fieldname.speed") + ": ";
+       private static final String LABEL_POINT_VERTSPEED    = I18nManager.getText("fieldname.verticalspeed") + ": ";
        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") + ": ";
@@ -96,8 +104,7 @@ public class DetailsDisplay extends GenericDisplay
        private static final String LABEL_RANGE_CLIMB = I18nManager.getText("details.range.climb") + ": ";
        private static final String LABEL_RANGE_DESCENT = ", " + I18nManager.getText("details.range.descent") + ": ";
        private static final String LABEL_AUDIO_FILE = I18nManager.getText("details.audio.file") + ": ";
-       private static String LABEL_POINT_ALTITUDE_UNITS = null;
-       private static Altitude.Format LABEL_POINT_ALTITUDE_FORMAT = Altitude.Format.NO_FORMAT;
+       private static final String LABEL_FULL_PATH = I18nManager.getText("details.media.fullpath") + ": ";
 
 
        /**
@@ -130,6 +137,8 @@ public class DetailsDisplay extends GenericDisplay
                pointDetailsPanel.add(_timeLabel);
                _speedLabel = new JLabel("");
                pointDetailsPanel.add(_speedLabel);
+               _vSpeedLabel = new JLabel("");
+               pointDetailsPanel.add(_vSpeedLabel);
                _nameLabel = new JLabel("");
                pointDetailsPanel.add(_nameLabel);
                _typeLabel = new JLabel("");
@@ -156,6 +165,8 @@ public class DetailsDisplay extends GenericDisplay
                _photoDetailsPanel = makeDetailsPanel("details.photodetails", biggerFont);
                _photoLabel = new JLabel(I18nManager.getText("details.nophoto"));
                _photoDetailsPanel.add(_photoLabel);
+               _photoPathLabel = new JLabel("");
+               _photoDetailsPanel.add(_photoPathLabel);
                _photoTimestampLabel = new JLabel("");
                _photoTimestampLabel.setMinimumSize(new Dimension(120, 10));
                _photoDetailsPanel.add(_photoTimestampLabel);
@@ -185,6 +196,8 @@ public class DetailsDisplay extends GenericDisplay
                _audioDetailsPanel = makeDetailsPanel("details.audiodetails", biggerFont);
                _audioLabel = new JLabel(I18nManager.getText("details.noaudio"));
                _audioDetailsPanel.add(_audioLabel);
+               _audioPathLabel = new JLabel("");
+               _audioDetailsPanel.add(_audioPathLabel);
                _audioTimestampLabel = new JLabel("");
                _audioTimestampLabel.setMinimumSize(new Dimension(120, 10));
                _audioDetailsPanel.add(_audioTimestampLabel);
@@ -241,13 +254,17 @@ public class DetailsDisplay extends GenericDisplay
                JLabel unitsLabel = new JLabel(I18nManager.getText("details.distanceunits") + ": ");
                unitsLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
                lowerPanel.add(unitsLabel);
-               String[] distUnits = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.miles")};
-               _distUnitsDropdown = new JComboBox(distUnits);
-               if (!Config.getConfigBoolean(Config.KEY_METRIC_UNITS)) {_distUnitsDropdown.setSelectedIndex(1);}
+               // Make dropdown for distance units
+               _distUnitsDropdown = new JComboBox();
+               final UnitSet currUnits = Config.getUnitSet();
+               for (int i=0; i<UnitSetLibrary.getNumUnitSets(); i++) {
+                       _distUnitsDropdown.addItem(I18nManager.getText(UnitSetLibrary.getUnitSet(i).getDistanceUnit().getNameKey()));
+                       if (UnitSetLibrary.getUnitSet(i) == currUnits) {_distUnitsDropdown.setSelectedIndex(i);}
+               }
                _distUnitsDropdown.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
                        {
-                               Config.setConfigBoolean(Config.KEY_METRIC_UNITS, _distUnitsDropdown.getSelectedIndex() == 0);
+                               Config.selectUnitSet(_distUnitsDropdown.getSelectedIndex());
                                UpdateMessageBroker.informSubscribers(DataSubscriber.UNITS_CHANGED);
                        }
                });
@@ -269,9 +286,9 @@ public class DetailsDisplay extends GenericDisplay
                if ((inUpdateType | DATA_ADDED_OR_REMOVED) > 0) selection.markInvalid();
                int currentPointIndex = selection.getCurrentPointIndex();
                _speedLabel.setText("");
-               Distance.Units distUnits = _distUnitsDropdown.getSelectedIndex()==0?Distance.Units.KILOMETRES:Distance.Units.MILES;
-               String distUnitsStr = I18nManager.getText(_distUnitsDropdown.getSelectedIndex()==0?"units.kilometres.short":"units.miles.short");
-               String speedUnitsStr = I18nManager.getText(_distUnitsDropdown.getSelectedIndex()==0?"units.kmh":"units.mph");
+               UnitSet unitSet = UnitSetLibrary.getUnitSet(_distUnitsDropdown.getSelectedIndex());
+               String distUnitsStr = I18nManager.getText(unitSet.getDistanceUnit().getShortnameKey());
+               String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
                if (_track == null || currentPoint == null)
                {
                        _indexLabel.setText(I18nManager.getText("details.nopointselection"));
@@ -281,6 +298,8 @@ public class DetailsDisplay extends GenericDisplay
                        _timeLabel.setText("");
                        _nameLabel.setText("");
                        _typeLabel.setText("");
+                       _speedLabel.setText("");
+                       _vSpeedLabel.setText("");
                }
                else
                {
@@ -289,34 +308,42 @@ public class DetailsDisplay extends GenericDisplay
                                + " " + _track.getNumPoints());
                        _latLabel.setText(makeCoordinateLabel(LABEL_POINT_LATITUDE, currentPoint.getLatitude(), _coordFormatDropdown.getSelectedIndex()));
                        _longLabel.setText(makeCoordinateLabel(LABEL_POINT_LONGITUDE, currentPoint.getLongitude(), _coordFormatDropdown.getSelectedIndex()));
+                       Unit altUnit = Config.getUnitSet().getAltitudeUnit();
                        _altLabel.setText(currentPoint.hasAltitude()?
-                               (LABEL_POINT_ALTITUDE + currentPoint.getAltitude().getValue() + getAltitudeUnitsLabel(currentPoint.getAltitude().getFormat()))
-                               :"");
-                       if (currentPoint.getTimestamp().isValid())
-                       {
-                               if (currentPointIndex > 0 && currentPointIndex < (_trackInfo.getTrack().getNumPoints()-1))
-                               {
-                                       DataPoint prevPoint = _trackInfo.getTrack().getPoint(currentPointIndex - 1);
-                                       DataPoint nextPoint = _trackInfo.getTrack().getPoint(currentPointIndex + 1);
-                                       if (prevPoint.getTimestamp().isValid() && nextPoint.getTimestamp().isValid())
-                                       {
-                                               // use total distance and total time between neighbouring points
-                                               long diff = nextPoint.getTimestamp().getSecondsSince(prevPoint.getTimestamp());
-                                               if (diff < 1000 && diff > 0)
-                                               {
-                                                       double rads = DataPoint.calculateRadiansBetween(prevPoint, currentPoint) +
-                                                               DataPoint.calculateRadiansBetween(currentPoint, nextPoint);
-                                                       double dist = Distance.convertRadiansToDistance(rads, distUnits);
-                                                       String speed = roundedNumber(3600 * dist / diff) + " " + speedUnitsStr;
-                                                       _speedLabel.setText(I18nManager.getText("fieldname.speed") + ": " + speed);
-                                               }
-                                       }
-                               }
+                               (LABEL_POINT_ALTITUDE + currentPoint.getAltitude().getValue(altUnit) + " " +
+                               I18nManager.getText(altUnit.getShortnameKey()))
+                               : "");
+                       if (currentPoint.hasTimestamp()) {
                                _timeLabel.setText(LABEL_POINT_TIMESTAMP + currentPoint.getTimestamp().getText());
                        }
                        else {
                                _timeLabel.setText("");
                        }
+
+                       // Speed can come from either timestamps and distances, or speed values in data
+                       SpeedValue speedValue = new SpeedValue();
+                       SpeedCalculator.calculateSpeed(_track, currentPointIndex, speedValue);
+                       if (speedValue.isValid())
+                       {
+                               String speed = roundedNumber(speedValue.getValue()) + " " + speedUnitsStr;
+                               _speedLabel.setText(LABEL_POINT_SPEED + speed);
+                       }
+                       else {
+                               _speedLabel.setText("");
+                       }
+
+                       // Now do the vertical speed in the same way
+                       SpeedCalculator.calculateVerticalSpeed(_track, currentPointIndex, speedValue);
+                       if (speedValue.isValid())
+                       {
+                               String vSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
+                               String speed = roundedNumber(speedValue.getValue()) + " " + vSpeedUnitsStr;
+                               _vSpeedLabel.setText(LABEL_POINT_VERTSPEED + speed);
+                       }
+                       else {
+                               _vSpeedLabel.setText("");
+                       }
+
                        // Waypoint name
                        final String name = currentPoint.getWaypointName();
                        if (name != null && !name.equals(""))
@@ -347,27 +374,28 @@ public class DetailsDisplay extends GenericDisplay
                        _rangeLabel.setText(LABEL_RANGE_SELECTED
                                + (selection.getStart()+1) + " " + I18nManager.getText("details.range.to")
                                + " " + (selection.getEnd()+1));
-                       _distanceLabel.setText(LABEL_RANGE_DISTANCE + roundedNumber(selection.getDistance(distUnits)) + " " + distUnitsStr);
+                       _distanceLabel.setText(LABEL_RANGE_DISTANCE + roundedNumber(selection.getDistance()) + " " + distUnitsStr);
                        if (selection.getNumSeconds() > 0)
                        {
                                _durationLabel.setText(LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(selection.getNumSeconds()));
                                _aveSpeedLabel.setText(I18nManager.getText("details.range.avespeed") + ": "
-                                       + roundedNumber(selection.getDistance(distUnits)/selection.getNumSeconds()*3600.0) + " " + speedUnitsStr);
+                                       + roundedNumber(selection.getDistance()/selection.getNumSeconds()*3600.0) + " " + speedUnitsStr);
                        }
                        else {
                                _durationLabel.setText("");
                                _aveSpeedLabel.setText("");
                        }
-                       String altUnitsLabel = getAltitudeUnitsLabel(selection.getAltitudeFormat());
-                       IntegerRange altRange = selection.getAltitudeRange();
-                       if (altRange.getMinimum() >= 0 && altRange.getMaximum() >= 0)
+                       AltitudeRange altRange = selection.getAltitudeRange();
+                       Unit altUnit = Config.getUnitSet().getAltitudeUnit();
+                       String altUnitsLabel = I18nManager.getText(altUnit.getShortnameKey());
+                       if (altRange.hasRange())
                        {
                                _altRangeLabel.setText(LABEL_RANGE_ALTITUDE
-                                       + altRange.getMinimum() + altUnitsLabel + " "
+                                       + altRange.getMinimum(altUnit) + altUnitsLabel + " "
                                        + I18nManager.getText("details.altitude.to") + " "
-                                       + altRange.getMaximum() + altUnitsLabel);
-                               _updownLabel.setText(LABEL_RANGE_CLIMB + selection.getClimb() + altUnitsLabel
-                                       + LABEL_RANGE_DESCENT + selection.getDescent() + altUnitsLabel);
+                                       + altRange.getMaximum(altUnit) + altUnitsLabel);
+                               _updownLabel.setText(LABEL_RANGE_CLIMB + altRange.getClimb(altUnit) + altUnitsLabel
+                                       + LABEL_RANGE_DESCENT + altRange.getDescent(altUnit) + altUnitsLabel);
                        }
                        else
                        {
@@ -382,6 +410,8 @@ public class DetailsDisplay extends GenericDisplay
                {
                        // no photo, hide details
                        _photoLabel.setText(I18nManager.getText("details.nophoto"));
+                       _photoPathLabel.setText("");
+                       _photoPathLabel.setToolTipText("");
                        _photoTimestampLabel.setText("");
                        _photoConnectedLabel.setText("");
                        _photoBearingLabel.setText("");
@@ -392,6 +422,10 @@ public class DetailsDisplay extends GenericDisplay
                {
                        if (currentPhoto == null) {currentPhoto = currentPoint.getPhoto();}
                        _photoLabel.setText(I18nManager.getText("details.photofile") + ": " + currentPhoto.getName());
+                       String fullPath = currentPhoto.getFullPath();
+                       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()):"");
                        _photoConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": "
                                + (currentPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED ?
@@ -412,8 +446,11 @@ public class DetailsDisplay extends GenericDisplay
                // audio details
                _audioDetailsPanel.setVisible(_trackInfo.getAudioList().getNumAudios() > 0);
                AudioClip currentAudio = _trackInfo.getAudioList().getAudio(_trackInfo.getSelection().getCurrentAudioIndex());
-               if (currentAudio == null) {
+               if (currentAudio == null)
+               {
                        _audioLabel.setText(I18nManager.getText("details.noaudio"));
+                       _audioPathLabel.setText("");
+                       _audioPathLabel.setToolTipText("");
                        _audioTimestampLabel.setText("");
                        _audioLengthLabel.setText("");
                        _audioConnectedLabel.setText("");
@@ -421,6 +458,10 @@ public class DetailsDisplay extends GenericDisplay
                else
                {
                        _audioLabel.setText(LABEL_AUDIO_FILE + currentAudio.getName());
+                       String fullPath = currentAudio.getFullPath();
+                       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()):"");
                        int audioLength = currentAudio.getLengthInSeconds();
                        _audioLengthLabel.setText(audioLength < 0?"":LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(audioLength));
@@ -432,22 +473,6 @@ public class DetailsDisplay extends GenericDisplay
        }
 
 
-       /**
-        * Choose the appropriate altitude units label for the specified format
-        * @param inFormat altitude format
-        * @return language-sensitive string
-        */
-       private static String getAltitudeUnitsLabel(Altitude.Format inFormat)
-       {
-               if (inFormat == LABEL_POINT_ALTITUDE_FORMAT && LABEL_POINT_ALTITUDE_UNITS != null)
-                       return LABEL_POINT_ALTITUDE_UNITS;
-               LABEL_POINT_ALTITUDE_FORMAT = inFormat;
-               if (inFormat == Altitude.Format.METRES)
-                       return " " + I18nManager.getText("units.metres.short");
-               return " " + I18nManager.getText("units.feet.short");
-       }
-
-
        /**
         * Construct an appropriate coordinate label using the selected format
         * @param inPrefix prefix of label
@@ -550,4 +575,22 @@ public class DetailsDisplay extends GenericDisplay
                button.addActionListener(new FunctionLauncher(inFunction));
                return button;
        }
+
+       /**
+        * @param inFullPath full file path or URL to be shortened
+        * @return shortened string from beginning of path
+        */
+       private static String shortenPath(String inFullPath)
+       {
+               // Chop off the home path if possible
+               final String homePath = System.getProperty("user.home").toLowerCase();
+               if (inFullPath != null && inFullPath.toLowerCase().startsWith(homePath)) {
+                       inFullPath = inFullPath.substring(homePath.length()+1);
+               }
+               if (inFullPath == null || inFullPath.length() < 21) {
+                       return inFullPath;
+               }
+               // path is too long
+               return inFullPath.substring(0, 20) + "...";
+       }
 }
index 1c0cc7e43d478e86d43073b438b91835284b4d40..72537b4098716d08c2a23a4e61f62465ed3c620f 100644 (file)
@@ -28,6 +28,10 @@ public abstract class IconManager
        public static final String POINTS_CONNECTED_BUTTON = "points_connected.gif";
        /** Icon for points disconnected icon on main map display */
        public static final String POINTS_DISCONNECTED_BUTTON = "points_disconnected.gif";
+        /** Icon for edit mode button on main map display when not selected */
+       public static final String EDIT_MODE_BUTTON = "drag_points_icon.gif";
+        /** Icon for edit mode button on main map display when selected */
+       public static final String EDIT_MODE_BUTTON_ON = "drag_points_icon_on.gif";
        /** Icon for zoom in button on main map display */
        public static final String ZOOM_IN_BUTTON = "zoom_in.gif";
        /** Icon for zoom out button on main map display */
index 3fa7a1a8fdb2ca822f4a7e226d7459f3b80eca5c..d152ecca5b1c78549d6fcb7ea0f65878984a5436 100644 (file)
@@ -70,15 +70,19 @@ public abstract class ImageUtils
         */
        public static Dimension getThumbnailSize(int inOrigWidth, int inOrigHeight, int inMaxWidth, int inMaxHeight)
        {
-               assert(inMaxWidth > 0 && inMaxHeight > 0);
+               if (inMaxWidth <= 0 || inMaxHeight <= 0) {return new Dimension(1, 1);}
                // work out maximum zoom ratio available so that thumbnail isn't too big
                double xZoom = inMaxWidth * 1.0 / inOrigWidth;
                double yZoom = inMaxHeight * 1.0 / inOrigHeight;
                double zoom = (xZoom > yZoom?yZoom:xZoom);
                // Don't make thumbnail bigger than picture
                if (zoom > 1.0) {return new Dimension(inOrigWidth, inOrigHeight);}
+
                // calculate new width and height
-               return new Dimension ((int) (zoom * inOrigWidth), (int) (zoom * inOrigHeight));
+               final int xSize = (int) (zoom * inOrigWidth);
+               final int ySize = (int) (zoom * inOrigHeight);
+               if (xSize <= 0 || ySize <= 0) {return new Dimension(1, 1);}
+               return new Dimension (xSize, ySize);
        }
 
 
index 8229aa322fb58458db950de8eabdb27f9e16ddeb..c5501eee75dd653b3a72685bdd71867e14df7cc4 100644 (file)
@@ -54,7 +54,9 @@ public class MenuManager implements DataSubscriber
        private JMenuItem _editWaypointNameItem = null;
        private JMenuItem _deletePointItem = null;
        private JMenuItem _deleteRangeItem = null;
+       private JMenuItem _cropTrackItem = null;
        private JMenuItem _compressItem = null;
+       private JMenuItem _markRectangleItem = null;
        private JMenuItem _deleteMarkedPointsItem = null;
        private JMenuItem _interpolateItem = null;
        private JMenuItem _averageItem = null;
@@ -109,7 +111,6 @@ public class MenuManager implements DataSubscriber
        private ActionListener _undoAction = null;
        private ActionListener _editPointAction = null;
        private ActionListener _deletePointAction = null;
-       private ActionListener _deleteRangeAction = null;
        private ActionListener _selectStartAction = null;
        private ActionListener _selectEndAction = null;
 
@@ -119,6 +120,7 @@ public class MenuManager implements DataSubscriber
        private JButton _editPointButton = null;
        private JButton _deletePointButton = null;
        private JButton _deleteRangeButton = null;
+       private JButton _cutAndMoveButton = null;
        private JButton _selectStartButton = null;
        private JButton _selectEndButton = null;
        private JButton _connectButton = null;
@@ -250,6 +252,15 @@ public class MenuManager implements DataSubscriber
                _compressItem = makeMenuItem(FunctionLibrary.FUNCTION_COMPRESS, false);
                setShortcut(_compressItem, "shortcut.menu.edit.compress");
                trackMenu.add(_compressItem);
+               _markRectangleItem = new JMenuItem(I18nManager.getText("menu.track.markrectangle"));
+               _markRectangleItem.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               _app.setCurrentMode(App.AppMode.DRAWRECT);
+                               UpdateMessageBroker.informSubscribers();
+                       }
+               });
+               _markRectangleItem.setEnabled(false);
+               trackMenu.add(_markRectangleItem);
                _deleteMarkedPointsItem = new JMenuItem(I18nManager.getText("menu.track.deletemarked"));
                _deleteMarkedPointsItem.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
@@ -343,15 +354,10 @@ public class MenuManager implements DataSubscriber
                _selectEndItem.addActionListener(_selectEndAction);
                rangeMenu.add(_selectEndItem);
                rangeMenu.addSeparator();
-               _deleteRangeItem = new JMenuItem(I18nManager.getText("menu.range.deleterange"));
-               _deleteRangeAction = new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               _app.deleteSelectedRange();
-                       }
-               };
-               _deleteRangeItem.addActionListener(_deleteRangeAction);
-               _deleteRangeItem.setEnabled(false);
+               _deleteRangeItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_RANGE, false);
                rangeMenu.add(_deleteRangeItem);
+               _cropTrackItem = makeMenuItem(FunctionLibrary.FUNCTION_CROP_TRACK, false);
+               rangeMenu.add(_cropTrackItem);
                _reverseItem = new JMenuItem(I18nManager.getText("menu.range.reverse"));
                _reverseItem.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
@@ -375,13 +381,7 @@ public class MenuManager implements DataSubscriber
                _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES, false);
                rangeMenu.add(_deleteFieldValuesItem);
                rangeMenu.addSeparator();
-               _interpolateItem = new JMenuItem(I18nManager.getText("menu.range.interpolate"));
-               _interpolateItem.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               _app.interpolateSelection();
-                       }
-               });
-               _interpolateItem.setEnabled(false);
+               _interpolateItem = makeMenuItem(FunctionLibrary.FUNCTION_INTERPOLATE, false);
                rangeMenu.add(_interpolateItem);
                _averageItem = new JMenuItem(I18nManager.getText("menu.range.average"));
                _averageItem.addActionListener(new ActionListener() {
@@ -449,8 +449,8 @@ public class MenuManager implements DataSubscriber
                _mapCheckbox.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                                Config.setConfigBoolean(Config.KEY_SHOW_MAP, _mapCheckbox.isSelected());
-                               UpdateMessageBroker.informSubscribers();
-                       }
+                               UpdateMessageBroker.informSubscribers(MAPSERVER_CHANGED);
+                       }
                });
                viewMenu.add(_mapCheckbox);
                // Turn off the sidebars
@@ -761,10 +761,24 @@ public class MenuManager implements DataSubscriber
                toolbar.add(_deletePointButton);
                // Delete range
                _deleteRangeButton = new JButton(IconManager.getImageIcon(IconManager.DELETE_RANGE));
-               _deleteRangeButton.setToolTipText(I18nManager.getText("menu.range.deleterange"));
-               _deleteRangeButton.addActionListener(_deleteRangeAction);
+               _deleteRangeButton.setToolTipText(I18nManager.getText("function.deleterange"));
+               _deleteRangeButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               FunctionLibrary.FUNCTION_DELETE_RANGE.begin();
+                       }
+               });
                _deleteRangeButton.setEnabled(false);
                toolbar.add(_deleteRangeButton);
+               // Cut and move
+               _cutAndMoveButton = new JButton(IconManager.getImageIcon(IconManager.CUT_AND_MOVE));
+               _cutAndMoveButton.setToolTipText(I18nManager.getText("menu.range.cutandmove"));
+               _cutAndMoveButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               _app.cutAndMoveSelection();
+                       }
+               });
+               _cutAndMoveButton.setEnabled(false);
+               toolbar.add(_cutAndMoveButton);
                // Select start, end
                _selectStartButton = new JButton(IconManager.getImageIcon(IconManager.SET_RANGE_START));
                _selectStartButton.setToolTipText(I18nManager.getText("menu.range.start"));
@@ -821,6 +835,7 @@ public class MenuManager implements DataSubscriber
                _exportPovItem.setEnabled(hasData);
                _exportSvgItem.setEnabled(hasData);
                _compressItem.setEnabled(hasData);
+               _markRectangleItem.setEnabled(hasData);
                _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints());
                _rearrangeMenu.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
                _selectAllItem.setEnabled(hasData);
@@ -884,8 +899,8 @@ public class MenuManager implements DataSubscriber
                boolean hasRange = (hasData && _selection.hasRangeSelected());
                _deleteRangeItem.setEnabled(hasRange);
                _deleteRangeButton.setEnabled(hasRange);
-               _interpolateItem.setEnabled(hasRange
-                       && (_selection.getEnd() - _selection.getStart()) == 1);
+               _cropTrackItem.setEnabled(hasRange);
+               _interpolateItem.setEnabled(hasRange);
                _averageItem.setEnabled(hasRange);
                _mergeSegmentsItem.setEnabled(hasRange);
                _reverseItem.setEnabled(hasRange);
@@ -895,9 +910,11 @@ public class MenuManager implements DataSubscriber
                _deleteFieldValuesItem.setEnabled(hasRange);
                _fullRangeDetailsItem.setEnabled(hasRange);
                // Is the currently selected point outside the current range?
-               _cutAndMoveItem.setEnabled(hasRange && hasPoint &&
+               boolean canCutAndMove = hasRange && hasPoint &&
                        (_selection.getCurrentPointIndex() < _selection.getStart()
-                               || _selection.getCurrentPointIndex() > (_selection.getEnd()+1)));
+                       || _selection.getCurrentPointIndex() > (_selection.getEnd()+1));
+               _cutAndMoveItem.setEnabled(canCutAndMove);
+               _cutAndMoveButton.setEnabled(canCutAndMove);
                // Has the map been switched on/off?
                boolean mapsOn = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
                if (_mapCheckbox.isSelected() != mapsOn) {
index 73a504a50160efa3581feb9cb43e001898616aae..0f2b66b4610c93a102c7289414fac6af2c27c703 100644 (file)
@@ -253,7 +253,7 @@ public class SelectorDisplay extends GenericDisplay
                {
                        _waypointListModel.fireChanged();
                }
-               if ((inUpdateType |
+               if ((inUpdateType &
                        (DataSubscriber.DATA_ADDED_OR_REMOVED | DataSubscriber.DATA_EDITED | DataSubscriber.PHOTOS_MODIFIED)) > 0)
                {
                        _photoListModel.fireChanged();
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
index e80306b2345eda50aeed511dd12685e4d1dff352..c7be0b1571a8757932c0f4a9850693e250cb8703 100644 (file)
Binary files a/tim/prune/gui/images/autopan.gif and b/tim/prune/gui/images/autopan.gif differ
diff --git a/tim/prune/gui/images/connect_photo_icon.png b/tim/prune/gui/images/connect_photo_icon.png
deleted file mode 100644 (file)
index d32d52e..0000000
Binary files a/tim/prune/gui/images/connect_photo_icon.png and /dev/null differ
index 2249560383dcef3cd1c696c18c5139ca9a5a786b..b2e0afbbeb3938f6d7be728c467a92b65c04fe12 100644 (file)
Binary files a/tim/prune/gui/images/cut_and_move.gif and b/tim/prune/gui/images/cut_and_move.gif differ
diff --git a/tim/prune/gui/images/drag_points_icon.gif b/tim/prune/gui/images/drag_points_icon.gif
new file mode 100644 (file)
index 0000000..67cedba
Binary files /dev/null and b/tim/prune/gui/images/drag_points_icon.gif differ
diff --git a/tim/prune/gui/images/drag_points_icon_on.gif b/tim/prune/gui/images/drag_points_icon_on.gif
new file mode 100644 (file)
index 0000000..d490471
Binary files /dev/null and b/tim/prune/gui/images/drag_points_icon_on.gif differ
index 99b74f4e2643e2600b5423acb6310c9da630fea0..322eb8ef4157cd4c6430a53bd4368cf74b257ea5 100644 (file)
Binary files a/tim/prune/gui/images/scalebar.gif and b/tim/prune/gui/images/scalebar.gif differ
index 99aa809654c810be01d9ba7d80d4e5054593f0c3..17d4efe305916db645bef30bceafba51315ee558 100644 (file)
@@ -8,7 +8,7 @@ public class CloudmadeMapSource extends OsmMapSource
        /** Selected style number */
        private String _style = null;
        /** Server prefix including API-key unique to GpsPrune application */
-       private static final String SERVER_PREFIX = "tile.cloudmade.com/03d86b66f51f4a3b8c236ac06f2a2e57/";
+       private static final String SERVER_PREFIX = "[abc].tile.cloudmade.com/03d86b66f51f4a3b8c236ac06f2a2e57/";
 
        /**
         * Constructor
index 40216f8c1fe532ea1a5f49a3ae3f49c8061019b8..350b3b4b1cdb1f2bbc53ff9f98accadbb1d0bbe5 100644 (file)
@@ -8,6 +8,10 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashSet;
+
+import tim.prune.GpsPrune;
 
 /**
  * Class to control the reading and saving of map tiles
@@ -23,6 +27,8 @@ public class DiskTileCacher implements Runnable
        private ImageObserver _observer = null;
        /** Time limit to cache images for */
        private static final long CACHE_TIME_LIMIT = 20 * 24 * 60 * 60 * 1000; // 20 days in ms
+       /** Hashset of all blocked / 404 tiles to avoid requesting them again */
+       private static final HashSet<String> BLOCKED_URLS = new HashSet<String>();
 
        /**
         * Private constructor
@@ -49,7 +55,8 @@ public class DiskTileCacher implements Runnable
                if (inBasePath == null) {return null;}
                File tileFile = new File(inBasePath, inTilePath);
                Image image = null;
-               if (tileFile.exists() && tileFile.canRead() && tileFile.length() > 0) {
+               if (tileFile.exists() && tileFile.canRead() && tileFile.length() > 0)
+               {
                        long fileStamp = tileFile.lastModified();
                        if (!inCheckAge || ((System.currentTimeMillis()-fileStamp) < CACHE_TIME_LIMIT))
                        {
@@ -72,7 +79,6 @@ public class DiskTileCacher implements Runnable
         */
        public static boolean saveTile(URL inUrl, String inBasePath, String inTilePath, ImageObserver inObserver)
        {
-               // TODO: Check that these are getting blocked properly
                if (inBasePath == null || inTilePath == null) {return false;}
                // save file if possible
                File basePath = new File(inBasePath);
@@ -83,6 +89,8 @@ public class DiskTileCacher implements Runnable
                File tileFile = new File(basePath, inTilePath);
                // Check if this file is already being loaded
                if (isBeingLoaded(tileFile)) {return true;}
+               // Check if it has already failed
+               if (BLOCKED_URLS.contains(inUrl.toString())) {return true;}
 
                File dir = tileFile.getParentFile();
                // Start a new thread to load the image if necessary
@@ -115,7 +123,8 @@ public class DiskTileCacher implements Runnable
                FileOutputStream out = null;
                File tempFile = new File(_file.getAbsolutePath() + ".temp");
                // Use a synchronized block across all threads to make sure this url is only fetched once
-               synchronized (DiskTileCacher.class) {
+               synchronized (DiskTileCacher.class)
+               {
                        if (tempFile.exists()) {return;}
                        try {
                                if (!tempFile.createNewFile()) {return;}
@@ -126,14 +135,20 @@ public class DiskTileCacher implements Runnable
                {
                        // Open streams from URL and to file
                        out = new FileOutputStream(tempFile);
-                       in = _url.openStream();
+                       // Set http user agent on connection
+                       URLConnection conn = _url.openConnection();
+                       conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+                       in = conn.getInputStream();
                        int d = 0;
                        // Loop over each byte in the stream (maybe buffering is more efficient?)
                        while ((d = in.read()) >= 0) {
                                out.write(d);
                        }
                        finished = true;
-               } catch (IOException e) {}
+               } catch (IOException e) {
+                       System.err.println("ioe: " + e.getClass().getName() + " - " + e.getMessage());
+                       BLOCKED_URLS.add(_url.toString());
+               }
                finally
                {
                        // clean up files
index 7fc5c489dd06f4f3ad1cf0860b0f969deab9b6a4..96ff22367e89d5137e1710c96e104a2911ff80de 100644 (file)
@@ -3,6 +3,7 @@ package tim.prune.gui.map;
 import java.awt.BasicStroke;
 import java.awt.BorderLayout;
 import java.awt.Color;
+import java.awt.Cursor;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.FontMetrics;
@@ -44,11 +45,17 @@ import tim.prune.data.Checker;
 import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
 import tim.prune.data.DoubleRange;
+import tim.prune.data.Field;
+import tim.prune.data.FieldList;
 import tim.prune.data.Latitude;
 import tim.prune.data.Longitude;
+import tim.prune.data.MidpointData;
 import tim.prune.data.Selection;
 import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;
+import tim.prune.function.compress.MarkPointsInRectangleFunction;
+import tim.prune.function.edit.FieldEdit;
+import tim.prune.function.edit.FieldEditList;
 import tim.prune.gui.IconManager;
 
 /**
@@ -65,6 +72,10 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        private TrackInfo _trackInfo = null;
        /** Selection object */
        private Selection _selection = null;
+       /** Object to keep track of midpoints */
+       private MidpointData _midpoints = null;
+       /** Index of point clicked at mouseDown */
+       private int _clickedPoint = -1;
        /** Previously selected point */
        private int _prevSelectedPoint = -1;
        /** Tile manager */
@@ -81,6 +92,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        private JCheckBox _autopanCheckBox = null;
        /** Checkbox for connecting track points */
        private JCheckBox _connectCheckBox = null;
+       /** Checkbox for enable edit mode */
+       private JCheckBox _editmodeCheckBox = null;
        /** Right-click popup menu */
        private JPopupMenu _popup = null;
        /** Top component panel */
@@ -97,18 +110,12 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        private boolean _checkBounds = false;
        /** Map position */
        private MapPosition _mapPosition = null;
-       /** x coordinate of drag from point */
-       private int _dragFromX = -1;
-       /** y coordinate of drag from point */
-       private int _dragFromY = -1;
-       /** x coordinate of drag to point */
-       private int _dragToX = -1;
-       /** y coordinate of drag to point */
-       private int _dragToY = -1;
-       /** x coordinate of popup menu */
-       private int _popupMenuX = -1;
-       /** y coordinate of popup menu */
-       private int _popupMenuY = -1;
+       /** coordinates of drag from point */
+       private int _dragFromX = -1, _dragFromY = -1;
+       /** coordinates of drag to point */
+       private int _dragToX = -1, _dragToY = -1;
+       /** coordinates of popup menu */
+       private int _popupMenuX = -1, _popupMenuY = -1;
        /** Flag to prevent showing too often the error message about loading maps */
        private boolean _shownOsmErrorAlready = false;
        /** Current drawing mode */
@@ -129,6 +136,12 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        private static final int MODE_ZOOM_RECT = 1;
        private static final int MODE_DRAW_POINTS_START = 2;
        private static final int MODE_DRAW_POINTS_CONT = 3;
+       private static final int MODE_DRAG_POINT = 4;
+       private static final int MODE_CREATE_MIDPOINT = 5;
+       private static final int MODE_MARK_RECTANGLE = 6;
+
+       private static final int INDEX_UNKNOWN  = -2;
+
 
        /**
         * Constructor
@@ -141,6 +154,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                _trackInfo = inTrackInfo;
                _track = inTrackInfo.getTrack();
                _selection = inTrackInfo.getSelection();
+               _midpoints = new MidpointData();
                _mapPosition = new MapPosition();
                addMouseListener(this);
                addMouseMotionListener(this);
@@ -163,6 +177,10 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                _recalculate = true;
                                Config.setConfigBoolean(Config.KEY_SHOW_MAP, e.getStateChange() == ItemEvent.SELECTED);
                                UpdateMessageBroker.informSubscribers(); // to let menu know
+                               // If the track is only partially visible and you turn the map off, make the track fully visible again
+                               if (e.getStateChange() == ItemEvent.DESELECTED && _transparencySlider.getValue() < 0) {
+                                       _transparencySlider.setValue(0);
+                               }
                        }
                };
                _topPanel = new OverlayPanel();
@@ -225,6 +243,15 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                _connectCheckBox.setFocusable(false); // stop button from stealing keyboard focus
                _topPanel.add(_connectCheckBox);
 
+               // Add checkbox button for edit mode or not
+               _editmodeCheckBox = new JCheckBox(IconManager.getImageIcon(IconManager.EDIT_MODE_BUTTON), false);
+               _editmodeCheckBox.setSelectedIcon(IconManager.getImageIcon(IconManager.EDIT_MODE_BUTTON_ON));
+               _editmodeCheckBox.setOpaque(false);
+               _editmodeCheckBox.setToolTipText(I18nManager.getText("menu.map.editmode"));
+               _editmodeCheckBox.addItemListener(itemListener);
+               _editmodeCheckBox.setFocusable(false); // stop button from stealing keyboard focus
+               _topPanel.add(_editmodeCheckBox);
+
                // Add zoom in, zoom out buttons
                _sidePanel = new OverlayPanel();
                _sidePanel.setLayout(new BoxLayout(_sidePanel, BoxLayout.Y_AXIS));
@@ -396,23 +423,38 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        if (_mapImage != null) {
                                inG.drawImage(_mapImage, 0, 0, getWidth(), getHeight(), null);
                        }
-                       // Draw the zoom rectangle if necessary
-                       if (_drawMode == MODE_ZOOM_RECT)
-                       {
-                               inG.setColor(Color.RED);
-                               inG.drawLine(_dragFromX, _dragFromY, _dragFromX, _dragToY);
-                               inG.drawLine(_dragFromX, _dragFromY, _dragToX, _dragFromY);
-                               inG.drawLine(_dragToX, _dragFromY, _dragToX, _dragToY);
-                               inG.drawLine(_dragFromX, _dragToY, _dragToX, _dragToY);
-                       }
-                       else if (_drawMode == MODE_DRAW_POINTS_CONT)
+
+                       switch (_drawMode)
                        {
-                               // draw line to mouse position to show drawing mode
-                               inG.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT));
-                               int prevIndex = _track.getNumPoints()-1;
-                               int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(prevIndex));
-                               int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(prevIndex));
-                               inG.drawLine(px, py, _dragToX, _dragToY);
+                               case MODE_DRAG_POINT:
+                                       drawDragLines(inG, _selection.getCurrentPointIndex()-1, _selection.getCurrentPointIndex()+1);
+                                       break;
+
+                               case MODE_CREATE_MIDPOINT:
+                                       drawDragLines(inG, _clickedPoint-1, _clickedPoint);
+                                       break;
+
+                               case MODE_ZOOM_RECT:
+                               case MODE_MARK_RECTANGLE:
+                                       if (_dragFromX != -1 && _dragFromY != -1)
+                                       {
+                                               // Draw the zoom rectangle if necessary
+                                               inG.setColor(Color.RED);
+                                               inG.drawLine(_dragFromX, _dragFromY, _dragFromX, _dragToY);
+                                               inG.drawLine(_dragFromX, _dragFromY, _dragToX, _dragFromY);
+                                               inG.drawLine(_dragToX, _dragFromY, _dragToX, _dragToY);
+                                               inG.drawLine(_dragFromX, _dragToY, _dragToX, _dragToY);
+                                       }
+                                       break;
+                                       
+                               case MODE_DRAW_POINTS_CONT:
+                                       // draw line to mouse position to show drawing mode
+                                       inG.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT));
+                                       int prevIndex = _track.getNumPoints()-1;
+                                       int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(prevIndex));
+                                       int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(prevIndex));
+                                       inG.drawLine(px, py, _dragToX, _dragToY);
+                                       break;
                        }
                }
                else
@@ -545,6 +587,15 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                final Color secondColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SECONDARY), opacity);
                final Color textColour   = makeTransparentColour(cs.getColour(ColourScheme.IDX_TEXT), opacity);
 
+               final int winWidth  = getWidth();
+               final int winHeight = getHeight();
+               final int halfWinWidth  = winWidth / 2;
+               final int halfWinHeight = winHeight / 2;
+
+               final int numPoints = _track.getNumPoints();
+               final int[] xPixels = new int[numPoints];
+               final int[] yPixels = new int[numPoints];
+
                // try to set line width for painting
                if (inG instanceof Graphics2D)
                {
@@ -560,11 +611,16 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                boolean prevPointVisible = false, currPointVisible = false;
                boolean anyWaypoints = false;
                boolean isWaypoint = false;
-               for (int i=0; i<_track.getNumPoints(); i++)
+               for (int i=0; i<numPoints; i++)
                {
-                       int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
-                       int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
-                       currPointVisible = px >= 0 && px < getWidth() && py >= 0 && py < getHeight();
+                       // Calculate pixel position of point from its x, y coordinates
+                       int px = halfWinWidth  + _mapPosition.getXFromCentre(_track.getX(i));
+                       int py = halfWinHeight + _mapPosition.getYFromCentre(_track.getY(i));
+                       px = wrapLongitudeValue(px, winWidth, _mapPosition.getZoom());
+                       // Remember these calculated pixel values so they don't have to be recalculated
+                       xPixels[i] = px; yPixels[i] = py;
+
+                       currPointVisible = px >= 0 && px < winWidth && py >= 0 && py < winHeight;
                        isWaypoint = _track.getPoint(i).isWaypoint();
                        anyWaypoints = anyWaypoints || isWaypoint;
                        if (currPointVisible)
@@ -600,16 +656,15 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                inG.setColor(textColour);
                FontMetrics fm = inG.getFontMetrics();
                int nameHeight = fm.getHeight();
-               int width = getWidth();
-               int height = getHeight();
-               if (anyWaypoints) {
+               if (anyWaypoints)
+               {
                        for (int i=0; i<_track.getNumPoints(); i++)
                        {
                                if (_track.getPoint(i).isWaypoint())
                                {
-                                       int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
-                                       int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
-                                       if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
+                                       int px = xPixels[i];
+                                       int py = yPixels[i];
+                                       if (px >= 0 && px < winWidth && py >= 0 && py < winHeight)
                                        {
                                                inG.fillRect(px-3, py-3, 6, 6);
                                                pointsPainted++;
@@ -621,9 +676,9 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        {
                                if (_track.getPoint(i).isWaypoint())
                                {
-                                       int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
-                                       int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
-                                       if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
+                                       int px = xPixels[i];
+                                       int py = yPixels[i];
+                                       if (px >= 0 && px < winWidth && py >= 0 && py < winHeight)
                                        {
                                                // Figure out where to draw waypoint name so it doesn't obscure track
                                                String waypointName = _track.getPoint(i).getWaypointName();
@@ -640,8 +695,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                                        // Check each direction in turn right left up down
                                                        for (int a=0; a<4; a++)
                                                        {
-                                                               if (nameXs[a] > 0 && (nameXs[a] + nameWidth) < width
-                                                                       && nameYs[a] < height && (nameYs[a] - nameHeight) > 0
+                                                               if (nameXs[a] > 0 && (nameXs[a] + nameWidth) < winWidth
+                                                                       && nameYs[a] < winHeight && (nameYs[a] - nameHeight) > 0
                                                                        && !overlapsPoints(nameXs[a], nameYs[a], nameWidth, nameHeight, textColour))
                                                                {
                                                                        // Found a rectangle to fit - draw name here and quit
@@ -661,9 +716,9 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                {
                        if (_track.getPoint(i).hasMedia())
                        {
-                               int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
-                               int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
-                               if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
+                               int px = xPixels[i];
+                               int py = yPixels[i];
+                               if (px >= 0 && px < winWidth && py >= 0 && py < winHeight)
                                {
                                        inG.drawRect(px-1, py-1, 2, 2);
                                        inG.drawRect(px-2, py-2, 4, 4);
@@ -678,30 +733,85 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        inG.setColor(rangeColour);
                        for (int i=_selection.getStart(); i<=_selection.getEnd(); i++)
                        {
-                               int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
-                               int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
+                               int px = xPixels[i];
+                               int py = yPixels[i];
                                inG.drawRect(px-1, py-1, 2, 2);
                        }
                }
 
-               // Draw selected point, crosshairs
+               // Draw crosshairs at selected point
                int selectedPoint = _selection.getCurrentPointIndex();
                if (selectedPoint >= 0)
                {
-                       int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(selectedPoint));
-                       int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(selectedPoint));
+                       int px = xPixels[selectedPoint];
+                       int py = yPixels[selectedPoint];
                        inG.setColor(currentColour);
                        // crosshairs
-                       inG.drawLine(px, 0, px, getHeight());
-                       inG.drawLine(0, py, getWidth(), py);
-                       // oval
-                       inG.drawOval(px - 2, py - 2, 4, 4);
-                       inG.drawOval(px - 3, py - 3, 6, 6);
+                       inG.drawLine(px, 0, px, winHeight);
+                       inG.drawLine(0, py, winWidth, py);
                }
                // Return the number of points painted
                return pointsPainted;
        }
 
+       /**
+        * Wrap the given pixel value if appropriate and possible
+        * @param inPx Pixel x coordinate
+        * @param inWinWidth window width in pixels
+        * @param inZoom zoom level
+        * @return modified pixel x coordinate
+        */
+       private static int wrapLongitudeValue(int inPx, int inWinWidth, int inZoom)
+       {
+               if (inPx > inWinWidth)
+               {
+                       // Pixel is too far right, could we wrap it back onto the screen?
+                       int px = inPx;
+                       while (px > inWinWidth) {
+                               px -= (256 << inZoom);
+                       }
+                       if (px >= 0) {
+                               return px; // successfully wrapped back onto the screen
+                       }
+               }
+               else if (inPx < 0)
+               {
+                       // Pixel is too far left, could we wrap it back onto the screen?
+                       int px = inPx;
+                       while (px < 0) {
+                               px += (256 << inZoom);
+                       }
+                       if (px < inWinWidth) {
+                               return px; // successfully wrapped back onto the screen
+                       }
+               }
+               // Either it's already on the screen or couldn't be wrapped
+               return inPx;
+       }
+
+       /**
+        * Draw the lines while dragging a point
+        * @param inG graphics object
+        * @param inPrevIndex index of point to draw from
+        * @param inNextIndex index of point to draw to
+        */
+       private void drawDragLines(Graphics inG, int inPrevIndex, int inNextIndex)
+       {
+               inG.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT));
+               // line from prev point to cursor
+               if (inPrevIndex > -1 && !_track.getPoint(inPrevIndex+1).getSegmentStart())
+               {
+                       final int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(inPrevIndex));
+                       final int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(inPrevIndex));
+                       inG.drawLine(px, py, _dragToX, _dragToY);
+               }
+               if (inNextIndex < _track.getNumPoints() && !_track.getPoint(inNextIndex).getSegmentStart())
+               {
+                       final int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(inNextIndex));
+                       final int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(inNextIndex));
+                       inG.drawLine(px, py, _dragToX, _dragToY);
+               }
+       }
 
        /**
         * Tests whether there are any dark pixels within the specified x,y rectangle
@@ -827,6 +937,48 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        new Longitude(lon, Coordinate.FORMAT_NONE), null);
        }
 
+       /**
+        * Move a DataPoint object to the given mouse coordinates
+        * @param startX start x coordinate of mouse
+        * @param startY start y coordinate of mouse
+        * @param endX end x coordinate of mouse
+        * @param endY end y coordinate of mouse
+        */
+       private void movePointToMouse(int startX, int startY, int endX, int endY )
+       {
+               double lat1 = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(startY, getHeight()));
+               double lon1 = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(startX, getWidth()));
+               double lat_delta = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(endY, getHeight())) - lat1;
+               double lon_delta = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(endX, getWidth())) - lon1;
+
+               DataPoint point = _trackInfo.getCurrentPoint();
+               if (point == null) {
+                       return;
+               }
+
+               // Make lists for edit and undo, and add each changed field in turn
+               FieldEditList editList = new FieldEditList();
+               FieldEditList undoList = new FieldEditList();
+
+               // Check field list
+               FieldList fieldList = _track.getFieldList();
+               int numFields = fieldList.getNumFields();
+               for (int i=0; i<numFields; i++)
+               {
+                       Field field = fieldList.getField(i);
+                       if (field == Field.LATITUDE) {
+                               editList.addEdit(new FieldEdit(field, Double.toString(point.getLatitude().getDouble() + lat_delta)));
+                               undoList.addEdit(new FieldEdit(field, point.getFieldValue(Field.LATITUDE)));
+                       }
+                       else if (field == Field.LONGITUDE) {
+                               editList.addEdit(new FieldEdit(field, Double.toString(point.getLongitude().getDouble() + lon_delta)));
+                               undoList.addEdit(new FieldEdit(field, point.getFieldValue(Field.LONGITUDE)));
+                       }
+               }
+               _app.completePointEdit(editList, undoList);
+       }
+
+
        /**
         * @see javax.swing.JComponent#getMinimumSize()
         */
@@ -861,10 +1013,15 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                        // single click
                                        if (_drawMode == MODE_DEFAULT)
                                        {
-                                               int pointIndex = _track.getNearestPointIndex(
+                                               int pointIndex = _clickedPoint;
+                                               if (pointIndex == INDEX_UNKNOWN)
+                                               {
+                                                       // index hasn't been calculated yet
+                                                       pointIndex = _track.getNearestPointIndex(
                                                         _mapPosition.getXFromPixels(inE.getX(), getWidth()),
                                                         _mapPosition.getYFromPixels(inE.getY(), getHeight()),
                                                         _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY), false);
+                                               }
                                                // Extend selection for shift-click
                                                if (inE.isShiftDown()) {
                                                        _trackInfo.extendSelection(pointIndex);
@@ -907,6 +1064,9 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                _popup.show(this, _popupMenuX, _popupMenuY);
                        }
                }
+               // Reset app mode
+               _app.setCurrentMode(App.AppMode.NORMAL);
+               if (_drawMode == MODE_MARK_RECTANGLE) _drawMode = MODE_DEFAULT;
        }
 
        /**
@@ -928,12 +1088,55 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        }
 
        /**
-        * Ignore mouse pressed events
+        * React to mouse pressed events to initiate a point drag
         * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
         */
        public void mousePressed(MouseEvent inE)
        {
-               // ignore
+               _clickedPoint = INDEX_UNKNOWN;
+               if (_track == null || _track.getNumPoints() <= 0)
+                       return;
+               if (!inE.isMetaDown())
+               {
+                       // Left mouse drag - check if point is near; if so select it for dragging
+                       if (_drawMode == MODE_DEFAULT)
+                       {
+                               /* Drag points if edit mode is enabled OR ALT is pressed */
+                               if (_editmodeCheckBox.isSelected() || inE.isAltDown() || inE.isAltGraphDown())
+                               {
+                                       final double clickX = _mapPosition.getXFromPixels(inE.getX(), getWidth());
+                                       final double clickY = _mapPosition.getYFromPixels(inE.getY(), getHeight());
+                                       final double clickSens = _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY);
+                                       _clickedPoint = _track.getNearestPointIndex(clickX, clickY, clickSens, false);
+
+                                       if (_clickedPoint >= 0)
+                                       {
+                                               // TODO: maybe use another color of the cross or remove the cross while dragging???
+
+                                               _trackInfo.selectPoint(_clickedPoint);
+                                               if (_trackInfo.getCurrentPoint() != null)
+                                               {
+                                                       _drawMode = MODE_DRAG_POINT;
+                                                       _dragFromX = _dragToX = inE.getX();
+                                                       _dragFromY = _dragToY = inE.getY();
+                                               }
+                                       }
+                                       else
+                                       {
+                                               // Not a click on a point, so check half-way between two (connected) trackpoints
+                                               int midpointIndex = _midpoints.getNearestPointIndex(clickX, clickY, clickSens);
+                                               if (midpointIndex > 0)
+                                               {
+                                                       _drawMode = MODE_CREATE_MIDPOINT;
+                                                       _clickedPoint = midpointIndex;
+                                                       _dragFromX = _dragToX = inE.getX();
+                                                       _dragFromY = _dragToY = inE.getY();
+                                               }
+                                       }
+                               }
+                       }
+               }
+               // else right-press ignored
        }
 
        /**
@@ -943,13 +1146,48 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        public void mouseReleased(MouseEvent inE)
        {
                _recalculate = true;
-               if (_drawMode == MODE_ZOOM_RECT && Math.abs(_dragToX - _dragFromX) > 20
-                       && Math.abs(_dragToY - _dragFromY) > 20)
+
+               if (_drawMode == MODE_DRAG_POINT)
+               {
+                       if (Math.abs(_dragToX - _dragFromX) > 2
+                               || Math.abs(_dragToY - _dragFromY) > 2)
+                       {
+                               movePointToMouse(_dragFromX, _dragFromY, _dragToX, _dragToY );
+                       }
+                       _drawMode = MODE_DEFAULT;
+               }
+               else if (_drawMode == MODE_CREATE_MIDPOINT)
+               {
+                       _drawMode = MODE_DEFAULT;
+                       _app.createPoint(createPointFromClick(_dragToX, _dragToY), _clickedPoint);
+               }
+               else if (_drawMode == MODE_ZOOM_RECT)
                {
-                       _mapPosition.zoomToPixels(_dragFromX, _dragToX, _dragFromY, _dragToY, getWidth(), getHeight());
+                       if (Math.abs(_dragToX - _dragFromX) > 20
+                        && Math.abs(_dragToY - _dragFromY) > 20)
+                       {
+                               _mapPosition.zoomToPixels(_dragFromX, _dragToX, _dragFromY, _dragToY, getWidth(), getHeight());
+                       }
+                       _drawMode = MODE_DEFAULT;
                }
-               if (_drawMode == MODE_ZOOM_RECT) {
+               else if (_drawMode == MODE_MARK_RECTANGLE)
+               {
+                       // Reset app mode
+                       _app.setCurrentMode(App.AppMode.NORMAL);
                        _drawMode = MODE_DEFAULT;
+                       // Call a function to mark the points
+                       MarkPointsInRectangleFunction marker = new MarkPointsInRectangleFunction(_app);
+                       double lon1 = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_dragFromX, getWidth()));
+                       double lat1 = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(_dragFromY, getHeight()));
+                       double lon2 = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_dragToX, getWidth()));
+                       double lat2 = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(_dragToY, getHeight()));
+                       // Invalidate rectangle if pixel coords are (-1,-1)
+                       if (_dragFromX < 0 || _dragFromY < 0) {
+                               lon1 = lon2;
+                               lat1 = lat2;
+                       }
+                       marker.setRectCoords(lon1, lat1, lon2, lat2);
+                       marker.begin();
                }
                _dragFromX = _dragFromY = -1;
                repaint();
@@ -963,19 +1201,40 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        {
                if (!inE.isMetaDown())
                {
-                       // Left mouse drag - pan map by appropriate amount
-                       if (_dragFromX != -1)
+                       // Left mouse drag - either drag the point or pan the map
+                       if (_drawMode == MODE_DRAG_POINT || _drawMode == MODE_CREATE_MIDPOINT)
                        {
-                               panMap(_dragFromX - inE.getX(), _dragFromY - inE.getY());
+                               // move point
+                               _dragToX = inE.getX();
+                               _dragToY = inE.getY();
                                _recalculate = true;
                                repaint();
                        }
-                       _dragFromX = _dragToX = inE.getX();
-                       _dragFromY = _dragToY = inE.getY();
+                       else if (_drawMode == MODE_MARK_RECTANGLE)
+                       {
+                               // draw a rectangle for marking points
+                               if (_dragFromX == -1) {
+                                       _dragFromX = inE.getX();
+                                       _dragFromY = inE.getY();
+                               }
+                               _dragToX = inE.getX();
+                               _dragToY = inE.getY();
+                               repaint();
+                       }
+                       else
+                       {
+                               // regular left-drag pans map by appropriate amount
+                               if (_dragFromX != -1)
+                               {
+                                       panMap(_dragFromX - inE.getX(), _dragFromY - inE.getY());
+                               }
+                               _dragFromX = _dragToX = inE.getX();
+                               _dragFromY = _dragToY = inE.getY();
+                       }
                }
                else
                {
-                       // Right-click and drag - draw rectangle and control zoom
+                       // Right-click and drag - update rectangle
                        _drawMode = MODE_ZOOM_RECT;
                        if (_dragFromX == -1) {
                                _dragFromX = inE.getX();
@@ -993,6 +1252,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
         */
        public void mouseMoved(MouseEvent inEvent)
        {
+               boolean useCrosshairs = false;
+               boolean useResize     = false;
                // Ignore unless we're drawing points
                if (_drawMode == MODE_DRAW_POINTS_CONT)
                {
@@ -1000,6 +1261,29 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        _dragToY = inEvent.getY();
                        repaint();
                }
+               else if (_drawMode == MODE_MARK_RECTANGLE) {
+                       useResize = true;
+               }
+               else if (_editmodeCheckBox.isSelected() || inEvent.isAltDown() || inEvent.isAltGraphDown())
+               {
+                       // Try to find a point or a midpoint at this location, and if there is one
+                       // then change the cursor to crosshairs
+                       final double clickX = _mapPosition.getXFromPixels(inEvent.getX(), getWidth());
+                       final double clickY = _mapPosition.getYFromPixels(inEvent.getY(), getHeight());
+                       final double clickSens = _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY);
+                       useCrosshairs = (_track.getNearestPointIndex(clickX, clickY, clickSens, false) >= 0
+                               || _midpoints.getNearestPointIndex(clickX, clickY, clickSens) >= 0
+                       );
+               }
+               if (useCrosshairs && !isCursorSet()) {
+                       setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+               }
+               else if (useResize && !isCursorSet()) {
+                       setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
+               }
+               else if (!useCrosshairs && !useResize && isCursorSet()) {
+                       setCursor(null);
+               }
        }
 
        /**
@@ -1024,6 +1308,17 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                if ((inUpdateType & DataSubscriber.MAPSERVER_CHANGED) > 0) {
                        _tileManager.resetConfig();
                }
+               if ((inUpdateType & (DataSubscriber.DATA_ADDED_OR_REMOVED + DataSubscriber.DATA_EDITED)) > 0) {
+                       _midpoints.updateData(_track);
+               }
+               // See if rect mode has been activated
+               if (_app.getCurrentMode() == App.AppMode.DRAWRECT)
+               {
+                       _drawMode = MODE_MARK_RECTANGLE;
+                       if (!isCursorSet()) {
+                               setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
+                       }
+               }
                repaint();
                // enable or disable components
                boolean hasData = _track.getNumPoints() > 0;
index d035f10bb3f8bd74ddff6959e13e2a20bfdd1c36..25f05fc1a2a238370d8888ea4b807b7d3f5f8adb 100644 (file)
@@ -9,9 +9,9 @@ public class MapPosition
        private static final int MAP_TILE_SIZE = 256;
 
        /** x position (scale depends on zoom) */
-       private long _xPosition = 0L;
+       private int _xPosition = 0;
        /** y position (scale depends on zoom) */
-       private long _yPosition = 0L;
+       private int _yPosition = 0;
 
        /** Zoom level, from 2 to max */
        private int _zoom = 12;
@@ -48,12 +48,21 @@ public class MapPosition
                }
                if (requiredZoom < 2) requiredZoom = 2;
                // Set position
-               _zoom = requiredZoom;
-               _zoomFactor = 1 << _zoom;
+               setZoom(requiredZoom);
                _xPosition = transformToPixels((inMinX + inMaxX) / 2.0);
                _yPosition = transformToPixels((inMinY + inMaxY) / 2.0);
        }
 
+       /**
+        * Ensure that zoom and zoomFactor remain in sync
+        * @param inZoom zoom level to set
+        */
+       private void setZoom(int inZoom)
+       {
+               _zoom = inZoom;
+               _zoomFactor = 1 << _zoom;
+       }
+
        /**
         * Zoom and pan to show the selected area
         * @param inMinX minimum pixels X
@@ -80,8 +89,7 @@ public class MapPosition
                                break;
                        }
                }
-               _zoom = requiredZoom;
-               _zoomFactor = 1 << _zoom;
+               setZoom(requiredZoom);
                // Set position
                _xPosition = (_xPosition - inWidth/2 + (inMinX + inMaxX) / 2) * multFactor;
                _yPosition = (_yPosition - inHeight/2 + (inMinY + inMaxY) / 2) * multFactor;
@@ -92,7 +100,7 @@ public class MapPosition
         * @param inValue value to transform
         * @return pixels
         */
-       private long transformToPixels(double inValue)
+       private int transformToPixels(double inValue)
        {
                return transformToPixels(inValue, _zoom);
        }
@@ -103,9 +111,9 @@ public class MapPosition
         * @param inZoom zoom value to use
         * @return pixels
         */
-       private static long transformToPixels(double inValue, int inZoom)
+       private static int transformToPixels(double inValue, int inZoom)
        {
-               return (long) (inValue * MAP_TILE_SIZE * (1 << inZoom));
+               return (int) (inValue * MAP_TILE_SIZE * (1 << inZoom));
        }
 
        /**
@@ -137,7 +145,7 @@ public class MapPosition
         */
        public int getXFromCentre(double inValue)
        {
-               return (int) (transformToPixels(inValue) - _xPosition);
+               return transformToPixels(inValue) - _xPosition;
        }
 
        /**
@@ -147,7 +155,7 @@ public class MapPosition
         */
        public int getYFromCentre(double inValue)
        {
-               return (int) (transformToPixels(inValue) - _yPosition);
+               return transformToPixels(inValue) - _yPosition;
        }
 
        /**
@@ -209,19 +217,19 @@ public class MapPosition
         * @param inPosition position of point
         * @return tile index for that point
         */
-       private int getTileIndex(long inPosition)
+       private int getTileIndex(int inPosition)
        {
-               return (int) (inPosition / MAP_TILE_SIZE);
+               return inPosition / MAP_TILE_SIZE;
        }
 
        /**
         * @param inPosition position of point
         * @return pixel offset for that point
         */
-       private int getDisplayOffset(long inPosition)
+       private int getDisplayOffset(int inPosition)
        {
-               return (int) (inPosition % MAP_TILE_SIZE);
-               // Maybe >> 8 would be slightly faster?
+               return inPosition % MAP_TILE_SIZE;
+               // I thought that &255 would be slightly faster, but it gives the wrong result
        }
 
        /**
@@ -231,8 +239,7 @@ public class MapPosition
        {
                if (_zoom < MAX_ZOOM)
                {
-                       _zoom++;
-                       _zoomFactor = 1 << _zoom;
+                       setZoom(_zoom + 1);
                        _xPosition *= 2;
                        _yPosition *= 2;
                }
@@ -243,10 +250,9 @@ public class MapPosition
         */
        public void zoomOut()
        {
-               if (_zoom >= 2)
+               if (_zoom >= 3)
                {
-                       _zoom--;
-                       _zoomFactor = 1 << _zoom;
+                       setZoom(_zoom - 1);
                        _xPosition /= 2;
                        _yPosition /= 2;
                }
index e3ac2ed2227a1825d01526c87800c3abdd9819d4..d838ef34d99d47a7f7fe524b8a63e87cf98609b6 100644 (file)
@@ -2,6 +2,8 @@ package tim.prune.gui.map;
 
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Class to represent any map source, whether an OsmMapSource
@@ -14,6 +16,9 @@ public abstract class MapSource
        /** File extensions */
        protected String[] _extensions = null;
 
+       /** Regular expression for catching server wildcards */
+       protected static final Pattern WILD_PATTERN = Pattern.compile("^(.*)\\[(.*)\\](.*)$");
+
 
        /**
         * @return the number of layers used in this source
@@ -81,9 +86,10 @@ public abstract class MapSource
                String urlstr = inUrl;
                // check prefix
                try {
-                       new URL(urlstr);
+                       new URL(urlstr.replace('[', 'w').replace(']', 'w'));
                }
-               catch (MalformedURLException e) {
+               catch (MalformedURLException e)
+               {
                        // fail if protocol specified
                        if (urlstr.indexOf("://") >= 0) {return null;}
                        // add the http protocol
@@ -95,7 +101,7 @@ public abstract class MapSource
                }
                // Validate current url, return null if not ok
                try {
-                       URL url = new URL(urlstr);
+                       URL url = new URL(urlstr.replace('[', 'w').replace(']', 'w'));
                        // url host must contain a dot
                        if (url.getHost().indexOf('.') < 0) {return null;}
                }
@@ -118,6 +124,17 @@ public abstract class MapSource
                int idx = url.indexOf("://");
                if (idx >= 0) {url = url.substring(idx + 3);}
                if (url.startsWith("www.")) {url = url.substring(4);}
+               // Strip out any "[.*]" as well
+               if (url.indexOf('[') >= 0)
+               {
+                       Matcher matcher = WILD_PATTERN.matcher(url);
+                       if (matcher.matches()) {
+                               url = matcher.group(1) + matcher.group(3);
+                               if (url.length() > 1 && url.charAt(0) == '.') {
+                                       url = url.substring(1);
+                               }
+                       }
+               }
                return url;
        }
 
index eae105515fe7960c1024badaed908fea8777a970..425df6a6cd72b1c1cebe5d417c4469339939a08e 100644 (file)
@@ -38,8 +38,8 @@ public abstract class MapSourceLibrary
         */
        private static void addFixedSources()
        {
-               _sourceList.add(new OsmMapSource("Mapnik", "http://tile.openstreetmap.org/"));
-               _sourceList.add(new OsmMapSource("Cyclemap", "http://tile.opencyclemap.org/cycle/"));
+               _sourceList.add(new OsmMapSource("Mapnik", "http://[abc].tile.openstreetmap.org/"));
+               _sourceList.add(new OsmMapSource("Cyclemap", "http://[abc].tile.opencyclemap.org/cycle/"));
                _sourceList.add(new OsmMapSource("Reitkarte", "http://wanderreitkarte.de/hills/",
                        "http://topo2.wanderreitkarte.de/topo/", 18));
                _sourceList.add(new MffMapSource("Mapsforfree", "http://maps-for-free.com/layer/relief/", "jpg",
index 829f824ffa09d06f6e37e2d3bd64e5bd7121725c..75f170a20f57df5ec5e84d3a2afa904a66d247e1 100644 (file)
@@ -1,7 +1,6 @@
 package tim.prune.gui.map;
 
 import java.awt.Image;
-import java.awt.Toolkit;
 import java.awt.image.ImageObserver;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -24,8 +23,6 @@ public class MapTileManager implements ImageObserver
        private int _numLayers = -1;
        /** Current zoom level */
        private int _zoom = 0;
-       /** Currently blocked zoom level, to prevent looping for non-existent images */
-       private int _blockedZoom = 0;
        /** Number of tiles in each direction for this zoom level */
        private int _numTileIndices = 1;
 
@@ -141,8 +138,9 @@ public class MapTileManager implements ImageObserver
         */
        public Image getTile(int inLayer, int inX, int inY)
        {
-               // Check tile boundaries
-               if (inX < 0 || inX >= _numTileIndices || inY < 0 || inY >= _numTileIndices) return null;
+               if (inY < 0 || inY >= _numTileIndices) return null;
+               // Wrap tile indices which are too big or too small
+               inX = ((inX % _numTileIndices) + _numTileIndices) % _numTileIndices;
 
                // Check first in memory cache for tile
                MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
@@ -161,15 +159,14 @@ public class MapTileManager implements ImageObserver
                        if (tile != null)
                        {
                                // Pass tile to memory cache
-                               tempCache.setTile(tile, inX, inY);
+                               tempCache.setTile(tile, inX, inY, _zoom);
                                if (tile.getWidth(this) > 0) {return tile;}
                                return null;
                        }
                }
                // Tile wasn't in memory or on disk, so if online let's get it
-               if (onlineMode && _blockedZoom != _zoom)
+               if (onlineMode)
                {
-                       _blockedZoom = 0; // reset to try again
                        try
                        {
                                URL tileUrl = new URL(_mapSource.makeURL(inLayer, _zoom, inX, inY));
@@ -181,10 +178,9 @@ public class MapTileManager implements ImageObserver
                                else
                                {
                                        // Load image asynchronously, using observer
-                                       tile = Toolkit.getDefaultToolkit().createImage(tileUrl);
-                                       // Pass to memory cache
-                                       _tempCaches[inLayer].setTile(tile, inX, inY);
-                                       if (tile.getWidth(this) > 0) {return tile;}
+                                       // tile = Toolkit.getDefaultToolkit().createImage(tileUrl);
+                                       // In order to set the http user agent, need to use a TileDownloader instead
+                                       TileDownloader.triggerLoad(this, tileUrl, inLayer, inX, inY, _zoom);
                                }
                        }
                        catch (MalformedURLException urle) {} // ignore
@@ -206,12 +202,31 @@ public class MapTileManager implements ImageObserver
        {
                boolean loaded = (infoflags & ImageObserver.ALLBITS) > 0;
                boolean error = (infoflags & ImageObserver.ERROR) > 0;
-               if (error) {
-                       _blockedZoom = _zoom;
-               }
                if (loaded || error) {
                        _parent.tilesUpdated(loaded);
                }
                return !loaded;
        }
+
+       /**
+        * Callback method from TileDownloader to let us know that an image has been loaded
+        * @param inTile Loaded Image object
+        * @param inLayer layer index from 0
+        * @param inX x coordinate of tile
+        * @param inY y coordinate of tile
+        * @param inZoom zoom level of loaded image
+        */
+       public void notifyImageLoaded(Image inTile, int inLayer, int inX, int inY, int inZoom)
+       {
+               if (inTile != null)
+               {
+                       MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
+                       if (tempCache.getTile(inX, inY) == null)
+                       {
+                               // Check with cache that the zoom level is still valid
+                               tempCache.setTile(inTile, inX, inY, inZoom);
+                               inTile.getWidth(this); // trigger imageUpdate when image is ready
+                       }
+               }
+       }
 }
index 0c6cd1cb2ee5818e96444b944732772ad7fdeafd..78206a3956959f6126ed1568a29893a61ce76e36 100644 (file)
@@ -27,12 +27,16 @@ public abstract class MapUtils
 
        /**
         * Transform an x coordinate into a longitude
-        * @param inX scaled X value from 0 to 1
+        * @param inX scaled X value from 0(-180deg) to 1(+180deg)
         * @return longitude in degrees
         */
        public static double getLongitudeFromX(double inX)
        {
-               return inX * 360.0 - 180.0;
+               // Ensure x is really between 0 and 1 (to wrap longitudes)
+               double x = ((inX % 1.0) + 1.0) % 1.0;
+               // Note: First %1.0 restricts range to (-1,1), then +1.0 shifts to (0,2)
+               // Finally, %1.0 to give (0,1)
+               return x * 360.0 - 180.0;
        }
 
        /**
index 69b5f831b19f3ad65a7a97c2f9a96e51d3f5756e..20d7a213a1a0c1b76001272f862739cec19f5051 100644 (file)
@@ -47,8 +47,8 @@ public class MemTileCacher
                // Mark boundaries as invalid
                for (int i=0; i<GRID_SIZE; i++)
                {
-                       _tiles[getArrayIndex(_tileX + GRID_SIZE/2 + 1, _tileY + i - GRID_SIZE/2)] = null;
-                       _tiles[getArrayIndex(_tileX + i - GRID_SIZE/2, _tileY + GRID_SIZE/2 + 1)] = null;
+                       _tiles[getArrayIndexNoWrap(_tileX + GRID_SIZE/2 + 1, _tileY + i - GRID_SIZE/2)] = null;
+                       _tiles[getArrayIndexNoWrap(_tileX + i - GRID_SIZE/2, _tileY + GRID_SIZE/2 + 1)] = null;
                }
        }
 
@@ -66,18 +66,34 @@ public class MemTileCacher
        }
 
        /**
-        * Get the array index for the given coordinates
+        * Get the array index for the given coordinates, including regular lon wrapping
         * @param inX x coord of tile
         * @param inY y coord of tile
         * @return array index
         */
        private int getArrayIndex(int inX, int inY)
        {
-               //System.out.println("Getting array index for (" + inX + ", " + inY + ") where the centre is at ("  + _tileX + ", " + _tileY
-               //      + ") and grid coords (" + _gridCentreX + ", " + _gridCentreY + ")");
+               final int tileSpan = 1 << _zoom;
+               int deltaX = (inX - _tileX);
+               while (deltaX > (tileSpan/2))  {deltaX -= tileSpan;}
+               while (deltaX < (-tileSpan/2)) {deltaX += tileSpan;}
+
+               int x = getCacheCoordinate(deltaX + _gridCentreX);
+               int y = getCacheCoordinate(inY - _tileY + _gridCentreY);
+               return (x + y * GRID_SIZE);
+       }
+
+       /**
+        * Get the array index for the given coordinates, without wrapping x coords
+        * (used for deletion to avoid deleting the wrong tile)
+        * @param inX x coord of tile
+        * @param inY y coord of tile
+        * @return array index
+        */
+       private int getArrayIndexNoWrap(int inX, int inY)
+       {
                int x = getCacheCoordinate(inX - _tileX + _gridCentreX);
                int y = getCacheCoordinate(inY - _tileY + _gridCentreY);
-               //System.out.println("Transformed to (" + x + ", " + y + ")");
                return (x + y * GRID_SIZE);
        }
 
@@ -86,7 +102,6 @@ public class MemTileCacher
         */
        public void clearAll()
        {
-               // Clear all images if zoom changed
                for (int i=0; i<_tiles.length; i++) {
                        _tiles[i] = null;
                }
@@ -107,9 +122,13 @@ public class MemTileCacher
         * @param inTile image to save
         * @param inX x coordinate of tile
         * @param inY y coordinate of tile
+        * @param inZoom zoom level
         */
-       public void setTile(Image inTile, int inX, int inY)
+       public void setTile(Image inTile, int inX, int inY, int inZoom)
        {
-               _tiles[getArrayIndex(inX, inY)] = inTile;
+               // Ignore images received for a different zoom level
+               if (inZoom == _zoom) {
+                       _tiles[getArrayIndex(inX, inY)] = inTile;
+               }
        }
 }
index 09def4597eb72977357930b9ea492c1c9d198a45..2f8d0ba97980c366ebaf824a515c0912e12f2174 100644 (file)
@@ -1,5 +1,6 @@
 package tim.prune.gui.map;
 
+import java.util.regex.Matcher;
 import tim.prune.I18nManager;
 
 /**
@@ -145,7 +146,9 @@ public class OsmMapSource extends MapSource
         */
        public String makeURL(int inLayerNum, int inZoom, int inX, int inY)
        {
-               return _baseUrls[inLayerNum] + inZoom + "/" + inX + "/" + inY + "." + getFileExtension(inLayerNum);
+               // Check if the base url has a [1234], if so replace at random
+               return pickServerUrl(_baseUrls[inLayerNum])
+                       + inZoom + "/" + inX + "/" + inY + "." + getFileExtension(inLayerNum);
        }
 
        /**
@@ -156,6 +159,35 @@ public class OsmMapSource extends MapSource
                return _maxZoom;
        }
 
+       /**
+        * If the base url contains something like [1234], then pick a server
+        * @param inBaseUrl base url
+        * @return modified base url
+        */
+       protected static final String pickServerUrl(String inBaseUrl)
+       {
+               if (inBaseUrl == null || inBaseUrl.indexOf('[') < 0) {
+                       return inBaseUrl;
+               }
+               // Check for [.*] (once only)
+               // Only need to support one, make things a bit easier
+               final Matcher matcher = WILD_PATTERN.matcher(inBaseUrl);
+               // if not, return base url unchanged
+               if (!matcher.matches()) {
+                       return inBaseUrl;
+               }
+               // if so, pick one at random and replace in the String
+               final String match = matcher.group(2);
+               final int numMatches = match.length();
+               String server = null;
+               if (numMatches > 0)
+               {
+                       int matchNum = (int) Math.floor(Math.random() * numMatches);
+                       server = "" + match.charAt(matchNum);
+               }
+               final String result = matcher.group(1) + (server==null?"":server) + matcher.group(3);
+               return result;
+       }
 
        /**
         * @return semicolon-separated list of all fields
index 3f3f848ee505f55938b88a7d121d644d705d37b0..37e12b65bfbbdebb76216869232d4f3089ee35f3 100644 (file)
@@ -8,6 +8,7 @@ import javax.swing.JPanel;
 import tim.prune.I18nManager;
 import tim.prune.config.ColourScheme;
 import tim.prune.config.Config;
+import tim.prune.data.Unit;
 
 /**
  * Class to show a scale bar on the main map of GpsPrune
@@ -59,8 +60,8 @@ public class ScaleBar extends JPanel
                if (_zoomLevel > -1)
                {
                        try {
-                               boolean useMetric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
-                               double drightSide = LEFT_OFFSET + _metricPixels[_zoomLevel] * (useMetric?1:1.609);
+                               final double distScaleFactor = Config.getUnitSet().getDistanceUnit().getMultFactorFromStd();
+                               double drightSide = LEFT_OFFSET + _metricPixels[_zoomLevel] / 1000.0 / distScaleFactor;
                                int scale = _scales[_zoomLevel];
 
                                // work out cos(latitude) from y position, and apply to scale
@@ -102,7 +103,7 @@ public class ScaleBar extends JPanel
                                inG.drawLine(rightSide, Y_OFFSET+1, rightSide, Y_OFFSET-TICK_HEIGHT);
                                inG.drawLine(rightSide+1, Y_OFFSET+1, rightSide+1, Y_OFFSET-TICK_HEIGHT);
                                // text
-                               String text = getScaleText(scale, useMetric);
+                               String text = getScaleText(scale, Config.getUnitSet().getDistanceUnit());
                                inG.setColor(blankColour);
                                inG.drawString(text, rightSide+MARGIN_WIDTH-1, Y_OFFSET);
                                inG.drawString(text, rightSide+MARGIN_WIDTH+1, Y_OFFSET);
@@ -118,22 +119,19 @@ public class ScaleBar extends JPanel
        /**
         * Get the scale text for the given scale
         * @param inScale scale number
-        * @param inUseMetric true to use km/m, false for miles/ft
+        * @param inDistUnit distance unit
         * @return scale text as string
         */
-       private static String getScaleText(int inScale, boolean inUseMetric)
+       private static String getScaleText(int inScale, Unit inDistUnit)
        {
                if (inScale > 0) {
                        // Positive scale means km or miles
                        return "" + inScale     + " " +
-                               I18nManager.getText(inUseMetric?"units.kilometres.short":"units.miles.short");
+                               I18nManager.getText(inDistUnit.getShortnameKey());
                }
-               if (inUseMetric) {
-                       // negative scale means m
-                       return "" + (-1000 / inScale) + " " + I18nManager.getText("units.metres.short");
-               }
-               // fallen through to feet
-               return "" + (-5280 / inScale) + " " + I18nManager.getText("units.feet.short");
+               // negative scale means a fraction
+               return "" + (-1.0 / inScale) + " " + I18nManager.getText(inDistUnit.getShortnameKey());
+               // might be nice to say 100m instead of 0.1km, 275ft instead of 0.2miles, etc - need to be done by Unit itself?
        }
 
        /**
diff --git a/tim/prune/gui/map/TileDownloader.java b/tim/prune/gui/map/TileDownloader.java
new file mode 100644 (file)
index 0000000..df820fc
--- /dev/null
@@ -0,0 +1,111 @@
+package tim.prune.gui.map;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashSet;
+
+import tim.prune.GpsPrune;
+
+/**
+ * Class to asynchronously download a tile from a url
+ * and populate an Image object with the contents
+ */
+public class TileDownloader implements Runnable
+{
+       private MapTileManager _manager = null;
+       private URL _url = null;
+       private int _layer = 0;
+       private int _x = 0, _y = 0;
+       private int _zoom = 0;
+       /** Hashset of all blocked / 404 tiles to avoid requesting them again */
+       private static final HashSet<String> BLOCKED_URLS = new HashSet<String>();
+       /** Hashset of all currently loading tiles to avoid requesting them again */
+       private static final HashSet<String> LOADING_URLS = new HashSet<String>();
+
+
+       /**
+        * Constructor (private)
+        * @param inManager parent manager for callback
+        * @param inUrl URL to load
+        * @param inLayer layer index from 0
+        * @param inX x coordinate of tile
+        * @param inY y coordinate of tile
+        * @param inZoom zoom level
+        */
+       private TileDownloader(MapTileManager inManager, URL inUrl, int inLayer, int inX, int inY, int inZoom)
+       {
+               _manager = inManager;
+               _url = inUrl;
+               _layer = inLayer;
+               _x = inX; _y = inY;
+               _zoom = inZoom;
+       }
+
+       /**
+        * Trigger a download in a new thread
+        * @param inManager manager to callback when image is loaded
+        * @param inUrl URL to load
+        * @param inLayer layer index from 0
+        * @param inX x coordinate of tile
+        * @param inY y coordinate of tile
+        * @param inZoom current zoom level
+        */
+       public static synchronized void triggerLoad(MapTileManager inManager, URL inUrl, int inLayer,
+               int inX, int inY, int inZoom)
+       {
+               if (inManager != null && inUrl != null)
+               {
+                       String url = inUrl.toString();
+                       if (!BLOCKED_URLS.contains(url) && !LOADING_URLS.contains(url))
+                       {
+                               LOADING_URLS.add(url);
+                               new Thread(new TileDownloader(inManager, inUrl, inLayer, inX, inY, inZoom)).start();
+                       }
+               }
+       }
+
+       /**
+        * Run method, called in separate thread
+        */
+       public void run()
+       {
+               InputStream in = null;
+               try
+               {
+                       // Set http user agent on connection
+                       URLConnection conn = _url.openConnection();
+                       conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+                       in = conn.getInputStream();
+                       int len = conn.getContentLength();
+                       if (len > 0)
+                       {
+                               byte[] data = new byte[len];
+                               int totalRead = 0;
+                               while (totalRead < len)
+                               {
+                                       int numRead = in.read(data, totalRead, len-totalRead);
+                                       totalRead += numRead;
+                               }
+                               Image tile = Toolkit.getDefaultToolkit().createImage(data);
+                               in.close();
+
+                               // Pass back to manager so it can be stored in its memory cache
+                               _manager.notifyImageLoaded(tile, _layer, _x, _y, _zoom);
+                       }
+               }
+               catch (IOException e)
+               {
+                       System.err.println("IOE: " + e.getClass().getName() + " - " + e.getMessage());
+                       synchronized(this.getClass())
+                       {
+                               BLOCKED_URLS.add(_url.toString());
+                       }
+                       try {in.close();} catch (Exception e2) {}
+               }
+               LOADING_URLS.remove(_url.toString());
+       }
+}
index e3bfbea327dea79a2558a09fbe13cdb1976d0a77..9c19862166b2421b4cdaab9d102a75b5eb26dad6 100644 (file)
@@ -1,19 +1,15 @@
 package tim.prune.gui.profile;
 
 import tim.prune.I18nManager;
-import tim.prune.data.Altitude;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Track;
+import tim.prune.data.UnitSet;
 
 /**
  * Class to provide a source of altitude data for the profile chart
  */
 public class AltitudeData extends ProfileData
 {
-       /** Altitude format for values */
-       private Altitude.Format _altitudeFormat = Altitude.Format.NO_FORMAT;
-
-
        /**
         * Constructor
         * @param inTrack track object
@@ -25,11 +21,13 @@ public class AltitudeData extends ProfileData
        /**
         * Get the data and populate the instance arrays
         */
-       public void init()
+       public void init(UnitSet inUnitSet)
        {
+               setUnitSet(inUnitSet);
                initArrays();
                _hasData = false;
-               _altitudeFormat = Altitude.Format.NO_FORMAT;
+               // multiplication factor for unit conversion
+               final double multFactor = _unitSet.getAltitudeUnit().getMultFactorFromStd();
                if (_track != null)
                {
                        for (int i=0; i<_track.getNumPoints(); i++)
@@ -39,17 +37,11 @@ public class AltitudeData extends ProfileData
                                        DataPoint point = _track.getPoint(i);
                                        if (point != null && point.hasAltitude())
                                        {
-                                               // Point has an altitude - if it's the first one, use its format
-                                               if (_altitudeFormat == Altitude.Format.NO_FORMAT)
-                                               {
-                                                       _altitudeFormat = point.getAltitude().getFormat();
-                                                       _minValue = _maxValue = point.getAltitude().getValue();
-                                               }
-                                               // Store the value and maintain max and min values
-                                               double value = point.getAltitude().getValue(_altitudeFormat);
+                                               // Point has an altitude - store value and maintain max and min values
+                                               double value = point.getAltitude().getMetricValue() * multFactor;
                                                _pointValues[i] = value;
-                                               if (value < _minValue) {_minValue = value;}
-                                               if (value > _maxValue) {_maxValue = value;}
+                                               if (value < _minValue || !_hasData) {_minValue = value;}
+                                               if (value > _maxValue || !_hasData) {_maxValue = value;}
 
                                                _hasData = true;
                                                _pointHasData[i] = true;
@@ -69,7 +61,7 @@ public class AltitudeData extends ProfileData
        public String getLabel()
        {
                return I18nManager.getText("fieldname.altitude") + " ("
-                       + I18nManager.getText(_altitudeFormat==Altitude.Format.FEET?"units.feet.short":"units.metres.short")
+                       + I18nManager.getText(_unitSet.getAltitudeUnit().getShortnameKey())
                        + ")";
        }
 
index 17ea32b2a4c004328b1ce1afcaf592b97423780c..770a211ea777535d900bb43bfed109c047b771a5 100644 (file)
@@ -33,7 +33,7 @@ public class ProfileChart extends GenericDisplay implements MouseListener
        private JPopupMenu _popup = null;
 
        /** Possible scales to use */
-       private static final int[] LINE_SCALES = {10000, 5000, 2000, 1000, 500, 200, 100, 50, 10, 5};
+       private static final int[] LINE_SCALES = {10000, 5000, 2000, 1000, 500, 200, 100, 50, 10, 5, 2, 1};
        /** Border width around black line */
        private static final int BORDER_WIDTH = 6;
        /** Minimum size for profile chart in pixels */
@@ -41,7 +41,7 @@ public class ProfileChart extends GenericDisplay implements MouseListener
        /** Colour to use for text if no data found */
        private static final Color COLOR_NODATA_TEXT = Color.GRAY;
        /** Chart type */
-       private static enum ChartType {ALTITUDE, SPEED};
+       private static enum ChartType {ALTITUDE, SPEED, VERT_SPEED};
 
 
        /**
@@ -54,7 +54,7 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                _data = new AltitudeData(inTrackInfo.getTrack());
                addMouseListener(this);
                setLayout(new FlowLayout(FlowLayout.LEFT));
-               _label = new JLabel("Altitude");
+               _label = new JLabel("Altitude"); // text will be replaced later
                add(_label);
                makePopup();
        }
@@ -79,7 +79,6 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                paintBackground(g, colourScheme);
                if (_track != null && _track.getNumPoints() > 0)
                {
-                       _data.init();
                        _label.setText(_data.getLabel());
                        int width = getWidth();
                        int height = getHeight();
@@ -121,11 +120,14 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                        // horizontal lines for scale - set to round numbers eg 500
                        int lineScale = getLineScale(minValue, maxValue);
                        int scaleValue = (int) (minValue/lineScale + 1) * lineScale;
+                       if (minValue < 0.0) {scaleValue -= lineScale;}
                        int x = 0, y = 0;
+                       final int zeroY = height - BORDER_WIDTH - (int) (yScaleFactor * (0.0 - minValue));
+
                        double value = 0.0;
-                       if (lineScale > 1)
+                       g.setColor(lineColour);
+                       if (lineScale >= 1)
                        {
-                               g.setColor(lineColour);
                                while (scaleValue < maxValue)
                                {
                                        y = height - BORDER_WIDTH - (int) (yScaleFactor * (scaleValue - minValue));
@@ -133,6 +135,12 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                                        scaleValue += lineScale;
                                }
                        }
+                       else if (minValue < 0.0)
+                       {
+                               // just draw zero line
+                               y = zeroY;
+                               g.drawLine(BORDER_WIDTH + 1, y, width - BORDER_WIDTH - 1, y);
+                       }
 
                        try
                        {
@@ -148,8 +156,22 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                                        if (_data.hasData(p))
                                        {
                                                value = _data.getData(p);
-                                               y = (int) (yScaleFactor * (value - minValue));
-                                               g.fillRect(BORDER_WIDTH+x, height-BORDER_WIDTH - y, barWidth, y);
+                                               // Normal case is the minimum value greater than zero
+                                               if (minValue >= 0)
+                                               {
+                                                       y = (int) (yScaleFactor * (value - minValue));
+                                                       g.fillRect(BORDER_WIDTH+x, height-BORDER_WIDTH - y, barWidth, y);
+                                               }
+                                               else if (value >= 0.0) {
+                                                       // Bar upwards from the zero line
+                                                       y = height-BORDER_WIDTH - (int) (yScaleFactor * (value - minValue));
+                                                       g.fillRect(BORDER_WIDTH+x, y, barWidth, zeroY - y);
+                                               }
+                                               else {
+                                                       // Bar downwards from the zero line
+                                                       int barHeight = (int) (yScaleFactor * value);
+                                                       g.fillRect(BORDER_WIDTH+x, zeroY, barWidth, -barHeight);
+                                               }
                                        }
                                }
                                // current point (make sure it's drawn last)
@@ -170,10 +192,11 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                        catch (NullPointerException npe) { // ignore, probably due to data being changed
                        }
                        // Draw numbers on top of the graph to mark scale
-                       if (lineScale > 1)
+                       if (lineScale >= 1)
                        {
                                int textHeight = g.getFontMetrics().getHeight();
                                scaleValue = (int) (minValue / lineScale + 1) * lineScale;
+                               if (minValue < 0.0) {scaleValue -= lineScale;}
                                y = 0;
                                g.setColor(currentColour);
                                while (scaleValue < maxValue)
@@ -242,6 +265,13 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                                changeView(ChartType.SPEED);
                        }});
                _popup.add(speedItem);
+               JMenuItem vertSpeedItem = new JMenuItem(I18nManager.getText("fieldname.verticalspeed"));
+               vertSpeedItem.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               changeView(ChartType.VERT_SPEED);
+                       }});
+               _popup.add(vertSpeedItem);
        }
 
        /**
@@ -252,7 +282,7 @@ public class ProfileChart extends GenericDisplay implements MouseListener
         */
        private int getLineScale(double inMin, double inMax)
        {
-               if ((inMax - inMin) < 5 || inMax < 0) {
+               if ((inMax - inMin) < 2.0) {
                        return -1;
                }
                int numScales = LINE_SCALES.length;
@@ -275,7 +305,10 @@ public class ProfileChart extends GenericDisplay implements MouseListener
         */
        public void dataUpdated(byte inUpdateType)
        {
-               _data.init();
+               // Try not to recalculate all the values unless necessary
+               if (inUpdateType != SELECTION_CHANGED) {
+                       _data.init(Config.getUnitSet());
+               }
                repaint();
        }
 
@@ -324,7 +357,10 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                else if (inType == ChartType.SPEED && !(_data instanceof SpeedData)) {
                        _data = new SpeedData(_track);
                }
-               _data.init();
+               else if (inType == ChartType.VERT_SPEED && !(_data instanceof VerticalSpeedData)) {
+                       _data = new VerticalSpeedData(_track);
+               }
+               _data.init(Config.getUnitSet());
                repaint();
        }
 
index b72c43a03b7afd3b5cbb42e4519fd9239471a111..0716b9332282164f28a8eb99ae43170dd8ec1a65 100644 (file)
@@ -1,6 +1,7 @@
 package tim.prune.gui.profile;
 
 import tim.prune.data.Track;
+import tim.prune.data.UnitSet;
 
 /**
  * Abstract class for all sources of profile data,
@@ -10,6 +11,8 @@ public abstract class ProfileData
 {
        /** Track object */
        protected final Track _track;
+       /** Unit set to use */
+       protected UnitSet _unitSet = null;
        /** Flag for availability of any data */
        protected boolean _hasData = false;
        /** Array of booleans for data per point */
@@ -74,7 +77,15 @@ public abstract class ProfileData
        /**
         * Get the data from the track and populate the value arrays
         */
-       public abstract void init();
+       public abstract void init(UnitSet inUnitSet);
+
+       /**
+        * Set the UnitSet to use for the calculations
+        * @param inUnitSet unit set
+        */
+       protected void setUnitSet(UnitSet inUnitSet) {
+               _unitSet = inUnitSet;
+       }
 
        /**
         * @return text for label including units
index d836543420a9e55578363981f685d10224232eca..cd4d49d61c1e1d61793af57ed3f3f63e21fe50d8 100644 (file)
@@ -1,20 +1,16 @@
 package tim.prune.gui.profile;
 
 import tim.prune.I18nManager;
-import tim.prune.config.Config;
-import tim.prune.data.DataPoint;
-import tim.prune.data.Distance;
+import tim.prune.data.SpeedCalculator;
+import tim.prune.data.SpeedValue;
 import tim.prune.data.Track;
-import tim.prune.data.Distance.Units;
+import tim.prune.data.UnitSet;
 
 /**
  * Class to provide a source of speed data for the profile chart
  */
 public class SpeedData extends ProfileData
 {
-       /** Flag for metric units */
-       private boolean _metric = true;
-
        /**
         * Constructor
         * @param inTrack track object
@@ -26,41 +22,28 @@ public class SpeedData extends ProfileData
        /**
         * Get the data and populate the instance arrays
         */
-       public void init()
+       public void init(UnitSet inUnitSet)
        {
+               setUnitSet(inUnitSet);
                initArrays();
-               _metric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
                _hasData = false;
                _minValue = _maxValue = 0.0;
-               if (_track != null) {
-                       DataPoint prevPrevPoint = null, prevPoint = null, point = null;
+               SpeedValue speed = new SpeedValue();
+               if (_track != null)
+               {
                        for (int i=0; i<_track.getNumPoints(); i++)
                        {
-                               point = _track.getPoint(i);
-                               if (prevPrevPoint != null && prevPrevPoint.hasTimestamp()
-                                       && prevPoint != null && prevPoint.hasTimestamp()
-                                       && point != null && point.hasTimestamp())
+                               // Get the speed either from the speed value or from the distances and timestamps
+                               SpeedCalculator.calculateSpeed(_track, i, speed);
+                               if (speed.isValid())
                                {
-                                       // All three points have timestamps
-                                       double seconds = point.getTimestamp().getSecondsSince(prevPrevPoint.getTimestamp());
-                                       if (seconds > 0)
-                                       {
-                                               double distInRads = DataPoint.calculateRadiansBetween(prevPrevPoint, prevPoint)
-                                                       + DataPoint.calculateRadiansBetween(prevPoint, point);
-                                               double dist = Distance.convertRadiansToDistance(distInRads, _metric?Units.KILOMETRES:Units.MILES);
-                                               // Store the value and maintain max and min values
-                                               double value = dist / seconds * 60.0 * 60.0;
-                                               _pointValues[i-1] = value;
-                                               if (value < _minValue || _minValue == 0.0) {_minValue = value;}
-                                               if (value > _maxValue) {_maxValue = value;}
-
-                                               _hasData = true;
-                                               _pointHasData[i-1] = true;
-                                       }
+                                       double speedValue = speed.getValue();
+                                       _pointValues[i] = speedValue;
+                                       if (speedValue < _minValue || _minValue == 0.0) {_minValue = speedValue;}
+                                       if (speedValue > _maxValue) {_maxValue = speedValue;}
+                                       _hasData = true;
                                }
-                               // Exchange points
-                               prevPrevPoint = prevPoint;
-                               prevPoint = point;
+                               _pointHasData[i] = speed.isValid();
                        }
                }
        }
@@ -71,7 +54,7 @@ public class SpeedData extends ProfileData
        public String getLabel()
        {
                return I18nManager.getText("fieldname.speed") + " ("
-                       + I18nManager.getText(_metric?"units.kmh":"units.mph") + ")";
+                       + I18nManager.getText(_unitSet.getSpeedUnit().getShortnameKey()) + ")";
        }
 
        /**
diff --git a/tim/prune/gui/profile/VerticalSpeedData.java b/tim/prune/gui/profile/VerticalSpeedData.java
new file mode 100644 (file)
index 0000000..510de88
--- /dev/null
@@ -0,0 +1,71 @@
+package tim.prune.gui.profile;
+
+import tim.prune.I18nManager;
+import tim.prune.data.SpeedCalculator;
+import tim.prune.data.SpeedValue;
+import tim.prune.data.Track;
+import tim.prune.data.UnitSet;
+
+/**
+ * Class to provide a source of vertical speed data for the profile chart
+ */
+public class VerticalSpeedData extends ProfileData
+{
+       /**
+        * Constructor
+        * @param inTrack track object
+        */
+       public VerticalSpeedData(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++)
+                       {
+                               SpeedCalculator.calculateVerticalSpeed(_track, i, speed);
+                               // Check whether we got a value from either method
+                               if (speed.isValid())
+                               {
+                                       // 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;}
+                                       _hasData = true;
+                               }
+                               _pointHasData[i] = speed.isValid();
+                       }
+               }
+       }
+
+       /**
+        * @return text description including units
+        */
+       public String getLabel()
+       {
+               return I18nManager.getText("fieldname.verticalspeed") + " ("
+                       + I18nManager.getText(_unitSet.getVerticalSpeedUnit().getShortnameKey()) + ")";
+       }
+
+       /**
+        * @return key for message when no speeds present
+        */
+       public String getNoDataKey()
+       {
+               if (!_track.hasAltitudeData()) {
+                       return "display.noaltitudes";
+               }
+               return "display.notimestamps";
+       }
+}
index 58618b8cd32c07ecc960ecaafcd4d03b84b7b3b8..2d0e54afef2a9dd2de1f4ad3e887e20841b2fa28 100644 (file)
@@ -1,5 +1,5 @@
 # Text entries for the GpsPrune application
-# Afrikaans entries as extra
+# Afrikaans entries
 
 # Menu entries
 menu.file=L\u00eaer
@@ -19,8 +19,8 @@ menu.range.all=Selekteer Alles
 menu.range.none=Selekteer Niks
 menu.range.start=Stel Reeks Begin
 menu.range.end=Stel Reeks Einde
-menu.range.deleterange=Reeks Uitvee
-menu.range.interpolate=Interpoleer
+function.deleterange=Reeks Uitvee
+function.interpolate=Interpoleer punte
 menu.range.average=Gemiddelde Seleksie
 menu.range.reverse=Reeks Omkeer
 menu.range.mergetracksegments=Saamvoeg van spoor segmente
@@ -30,7 +30,9 @@ menu.point.editpoint=Redigeer Punt
 menu.point.deletepoint=Punt Uitvee
 menu.photo=Foto
 menu.photo.saveexif=Stoor na EXIF
+menu.audio=Audio
 menu.view=Kyk
+menu.view.showsidebars=Wys kantstawe
 menu.view.browser=Kaart in werf blaaier
 menu.view.browser.google=Google Kaarte
 menu.view.browser.openstreetmap=Openstreetmap
@@ -38,12 +40,15 @@ menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Yahoo Kaarte
 menu.view.browser.bing=Bing Kaarte
 menu.settings=Stellings
+menu.settings.onlinemode=Laai kaarte vanaf internet
+menu.settings.autosave=Autostoor stellings op uitgaan
 menu.help=Hulp
 # Popup menu for map
 menu.map.zoomin=Zoom in
 menu.map.zoomout=Zoom uit
 menu.map.zoomfull=Zoom na vol skaal
 menu.map.newpoint=Skep nuwe punt
+menu.map.drawpoints=Skep reeks van punte
 menu.map.connect=Connekteer baan punte
 menu.map.autopan=Automatiese Skuif van Kyk Venster
 menu.map.showmap=Wys Kaart
@@ -51,10 +56,12 @@ menu.map.showscalebar=Wys SkalleerStaaf
 
 # Alt keys for menus
 altkey.menu.file=L
+altkey.menu.track=B
 altkey.menu.range=R
 altkey.menu.point=P
 altkey.menu.view=K
 altkey.menu.photo=F
+altkey.menu.audio=A
 altkey.menu.settings=S
 altkey.menu.help=H
 
@@ -79,18 +86,22 @@ function.editwaypointname=Redigeer Baken Naam
 function.compress=Kompakteer spoor
 function.addtimeoffset=Voeg tyd spruit by
 function.addaltitudeoffset=Voeg hoogte spruit by
-function.convertnamestotimes=Omskakel baken name na tye
+function.convertnamestotimes=Skakel baken name na tye
+function.deletefieldvalues=Verwyder veld waardes
 function.findwaypoint=Vind Baken
 function.pastecoordinates=Enter nuwe koordinate
 function.charts=Grafieke
-function.show3d=3D Kyk
+function.show3d=3D Vertoon
 function.distances=Afstande
 function.fullrangedetails=Vol reeks besonderhede
 function.setmapbg=Stel Kaart agtergrond
+function.setkmzimagesize=Stel KMZ beeld groote
 function.setpaths=Stel program paaie
 function.getgpsies=Kry GPS spore
+function.lookupsrtm=Kry hoogtes vanaf SRTM
 function.duplicatepoint=Dupliseer Punt
 function.setcolours=Stel kleure
+function.setlinewidth=Stel lyn dikte
 function.setlanguage=Stel tale
 function.connecttopoint=Las foto by huidige punt
 function.disconnectfrompoint=Ontkoppel vanaf huidige punt
@@ -102,12 +113,13 @@ function.rotatephotoright=Roteer foto regs
 function.ignoreexifthumb=Ignoreer EXIF thumbnail
 function.help=Hulp
 function.showkeys=Wys Kortpad sleutels
-function.about=Omtrent Prune
+function.about=Omtrent GpsPrune
 function.checkversion=Kyk vir nuwe weergawe
 function.saveconfig=Stoor Stellings
+function.diskcache=Stoor kaarte na skyf
 
 # Dialogs
-dialog.exit.confirm.title=Uitgaan uit Prune
+dialog.exit.confirm.title=Uitgaan uit GpsPrune
 dialog.exit.confirm.text=Jou data is nie gestoor nie. Is jy seker jy wil uitgaan?
 dialog.openappend.title=Voegby tot bestaande data
 dialog.openappend.text=Voeg hierdie data by die data wat alreeds gelaai is?
@@ -120,7 +132,7 @@ dialog.openoptions.filesnippet=Ekstrak vanuit L\u00eaer
 dialog.load.table.field=Veld
 dialog.load.table.datatype=Data Tipe
 dialog.load.table.description=Beskrywing
-dialog.delimiter.label=Veld Skeinding Karakter
+dialog.delimiter.label=Veld Skeiding Karakter
 dialog.delimiter.comma=Komma ,
 dialog.delimiter.tab=Tab
 dialog.delimiter.space=Spasie
@@ -129,7 +141,10 @@ dialog.delimiter.other=Ander
 dialog.openoptions.deliminfo.records=Rekords, met
 dialog.openoptions.deliminfo.fields=velde
 dialog.openoptions.deliminfo.norecords=Geen rekords
-dialog.openoptions.altitudeunits=Hoogte Eenhede
+dialog.openoptions.altitudeunits=Hoogte eenhede
+dialog.open.contentsdoubled=Hierdie leer bevat twee kopie van elke punt,\n once eenkeer as baken en eenkeer as spoor punt
+dialog.selecttracks.intro=Selekteer die spoor of spore om te laai
+dialog.selecttracks.noname=on benaamd
 dialog.jpegload.subdirectories=Sluit sub-gidse in
 dialog.jpegload.loadjpegswithoutcoords=Sluit fotos sonder koordinate in
 dialog.jpegload.loadjpegsoutsidearea=Sluit foto buitekant huidige area in
@@ -174,15 +189,18 @@ dialog.exportpov.modelstyle=Model styl
 dialog.exportpov.ballsandsticks=Balle en stokkies
 dialog.exportpov.tubesandwalls=Buise en mure
 dialog.exportpov.warningtracksize=Hierdie spoor het 'n groot aantal punte, wat Java3D miskien nie kan vertoon.\nIs jy seker jy wil voortgaan?
+dialog.exportsvg.text=Selekteer die parameters vir die SVG uitvoer
+dialog.exportsvg.phi=Azimuth hoek \u03d5
+dialog.exportsvg.theta=Opstandings angle \u03b8
 dialog.pointtype.desc=Stoor die volgende punt tipes
 dialog.pointtype.track=Spoor punte
 dialog.pointtype.waypoint=Bakens
 dialog.pointtype.photo=Foto punte
+dialog.pointtype.selection=Net seleksie
 dialog.confirmreversetrack.title=Bevestig omkering
 dialog.confirmreversetrack.text=Hierdie spoor bevat tydstempel informasie, wat uit sekwensie/order sal wees na omkering.\nIs jy seker jy wil die spoor omruil vir die seksie?
 dialog.confirmcutandmove.title=Bevestig sny en skuif
 dialog.confirmcutandmove.text=Hierdie spoor het tydstempel informasie, wat uit sekwensie/orde sal wees na skuif.\nIs jy seker jy wil die seksie skuif?
-dialog.interpolate.title=Interpoleer punte
 dialog.interpolate.parameter.text=Aantal punte om bytevoeg tussen geselekteerde punte.
 dialog.undo.title=Herroep aksie(s)
 dialog.undo.pretext=Selekteer asb die aksie(s) om te herroep
@@ -191,9 +209,12 @@ dialog.undo.none.text=Geen operasies om te herroep!
 dialog.clearundo.title=Maak Herroep lys uit skoon
 dialog.clearundo.text=Is jy seker jy wil die herroep lys skoon maak?\nAlle herroep informasie sal verlore gaan!
 dialog.pointedit.title=Redigeer punt
+dialog.pointedit.text=Selekteer elke veld om te wysig en gebruik die 'Wysig' knoppie om die waarde te verander
 dialog.pointedit.table.field=Veld
 dialog.pointedit.table.value=Waarde
 dialog.pointedit.table.changed=Verander
+dialog.pointedit.changevalue.text=Sleutel die nuwe waarde vir hierdie veld.
+dialog.pointedit.changevalue.title=Wysig veld
 dialog.pointnameedit.name=Baken naam
 dialog.pointnameedit.uppercase=Hoof letter
 dialog.pointnameedit.lowercase=Klein letter
@@ -202,7 +223,204 @@ dialog.addtimeoffset.subtract=Vat tyd weg
 dialog.addtimeoffset.days=Dae
 dialog.addtimeoffset.hours=Ure
 dialog.addtimeoffset.minutes=Minute
+dialog.findwaypoint.intro=Sleutel gedeelte van baken naam
 dialog.findwaypoint.search=Soek
 dialog.saveexif.title=Stoor Exif
+dialog.saveexif.table.photoname=Foto naam
 dialog.saveexif.table.status=Status
 dialog.saveexif.table.save=Stoor
+dialog.saveexif.photostatus.connected=Verbind
+dialog.saveexif.photostatus.disconnected=Afgesluit
+dialog.saveexif.photostatus.modified=Verander
+dialog.saveexif.overwrite=Oorskruif leers
+dialog.saveexif.force=Vorseer ten spyte van klein foute
+dialog.charts.xaxis=X as
+dialog.charts.yaxis=Y as
+dialog.charts.output=Uitset
+dialog.charts.screen=Uitset na skerm
+dialog.charts.svg=Uitset na SVG leer
+dialog.charts.svgwidth=SVG wydte
+dialog.charts.svgheight=SVG hoogte
+dialog.charts.needaltitudeortimes=die spoor moet of hoogtes of tyd informasie bevat om grafiek teskep
+dialog.charts.gnuplotnotfound=Kon nie gnuplot find op gegewe pad
+dialog.distances.intro=Reguit lyn afstande tussen punte
+dialog.distances.column.from=Vanaf punt
+dialog.distances.column.to=Na punt
+dialog.distances.currentpoint=Huidige Punt
+dialog.distances.toofewpoints=Hierdie funksie benodig bakens om afstande tussen bakens uit tewerk
+dialog.fullrangedetails.intro=Hier is die besonderhede for die geselekteerde reeks
+dialog.setmapbg.intro=Selekteer een van die kaart bronne, of voeg nuwe een by
+dialog.addmapsource.title=Voeg nuwe kaart bron by
+dialog.addmapsource.sourcename=Naam van bron
+dialog.correlate.notimestamps=Daar is geen tyd stempels in die data punte, so daar is niks om te korreleer met die fotos
+dialog.correlate.nouncorrelatedphotos=Daar is geen ongekorreleerde fotos.\nIs jy seker jy wil voortgaan?
+dialog.correlate.photoselect.intro=Selekteer een van die gekorreleerde fotos om te gebruik as tyd afset
+dialog.correlate.select.photoname=Foto naam
+dialog.correlate.select.timediff=Tyd verskil
+dialog.correlate.select.photolater=Foto later
+dialog.correlate.options.tip=Wenk: Deur een item te verbind, kan die tyd afset bereken word vir jou.
+dialog.correlate.options.intro=Seleketeer die opsies vir automatiese korrelasie
+dialog.correlate.options.offsetpanel=Tyd afset
+dialog.correlate.options.offset=Afset
+dialog.correlate.options.offset.hours=ure,
+dialog.correlate.options.offset.minutes=minute en
+dialog.correlate.options.offset.seconds=sekondes
+dialog.correlate.options.photolater=Foto later as punt
+dialog.correlate.options.pointlaterphoto=Punt later as foto
+dialog.correlate.options.limitspanel=Korrelasie limiete
+dialog.correlate.options.notimelimit=Geen tyd limiet
+dialog.correlate.options.timelimit=Tyd limiet
+dialog.correlate.options.nodistancelimit=Geen afstand limiet
+dialog.correlate.options.distancelimit=Afstand limiet
+dialog.correlate.options.correlate=Korreleer
+dialog.correlate.alloutsiderange=Al die fotos is buitekant die tyd reeks van die spoor, so geen kon gekorreleer word.\nProbeer om afset te stel of omself ten minste een foto te korreleer.
+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.compress.closepoints.title=Naby punt verwydering
+dialog.compress.closepoints.paramdesc=Span faktor
+dialog.compress.wackypoints.title=Gekkige punt verwydering
+dialog.compress.wackypoints.paramdesc=Afstands faktor
+dialog.compress.singletons.paramdesc=Afstand faktor
+dialog.compress.duplicates.title=Duplikaat verwydering
+dialog.compress.douglaspeucker.title=Douglas-Peuker kompressie
+dialog.compress.summarylabel=Punte om te verwyder
+dialog.deletemarked.nonefound=Geen data punt kon verwyder word
+dialog.pastecoordinates.desc=Sleutel of plak die koordinate hier
+dialog.pastecoordinates.coords=Koordinate
+dialog.pastecoordinates.nothingfound=Gaan asseblief koordinate na en probeer weer
+dialog.help.help=Sien asseblief\n http://activityworkshop.net/software/gpsprune/\n vir meer inligting en gebruikers handleidings.
+dialog.about.version=Weergawe
+dialog.about.build=Bou
+dialog.about.summarytext1=GpsPrune is 'n program vir die laai, vertoon en wysiging van data vanaf GPS ontvangers.
+dialog.about.languages=Beskikbare tale
+dialog.about.translatedby=Engelse teks deur activityworkshop
+dialog.about.systeminfo=Stelsel informasie
+dialog.about.systeminfo.os=Beheer Stelsel
+dialog.about.systeminfo.java3d=Java3d geinstalleer
+dialog.about.systeminfo.povray=Povray geinstalleer
+dialog.about.systeminfo.exiftool=Exiftool geinstalleer
+dialog.about.systeminfo.gpsbabel=Gpsbabel geinstalleer
+dialog.about.systeminfo.gnuplot=Gnuplot geinstalleer
+dialog.about.systeminfo.exiflib=Exif biblioteek
+dialog.about.systeminfo.exiflib.internal=Intern
+dialog.about.systeminfo.exiflib.internal.failed=Interne (nie gevind)
+dialog.about.systeminfo.exiflib.external=Ektern
+dialog.about.systeminfo.exiflib.external.failed=Ekstern (nie gevind)
+dialog.about.yes=Ja
+dialog.about.no=Nee
+dialog.about.credits=Krediete
+dialog.about.credits.code=GpsPrune bron kode geskruif deur
+dialog.about.credits.exifcode=Exif bron kode deur
+dialog.about.credits.icons=Somigge ikone gevat vanaf
+dialog.about.credits.translators=Vertalers
+dialog.about.credits.translations=Vertalings gehelp deur
+dialog.about.credits.devtools=Onwikkelings gereedskap
+dialog.about.credits.othertools=Ander gereedskap
+dialog.about.credits.thanks=Dankie aan
+dialog.about.readme=Leesmy
+
+# Field names
+fieldname.latitude=breedtegraad
+fieldname.longitude=lengtegraad
+fieldname.altitude=Hoogte
+fieldname.timestamp=Tyd
+fieldname.time=Tyd
+fieldname.waypointname=Naam
+fieldname.waypointtype=Tipe
+fieldname.newsegment=Segment
+fieldname.custom=Persoonlike
+fieldname.prefix=Veld
+fieldname.distance=Afstand
+fieldname.movingdistance=Beweeg afstand
+fieldname.duration=Tydperk
+fieldname.speed=Spoed
+fieldname.verticalspeed=Vertikale spoed
+fieldname.description=Beskrywing
+
+# Measurement units
+units.original=Oorspronklik
+units.default=Bestek
+units.metres=Meters
+units.metres.short=m
+units.feet=Voet
+units.feet.short=ft
+units.kilometres=Kilo meters
+units.kilometres.short=km
+units.miles=Myl
+units.miles.short=mi
+units.hours=ure
+units.degminsec=Deg-min-sec
+units.degmin=Deg-min
+units.deg=Grade
+units.iso8601=ISO 8601
+
+# Cardinals for 3d plots
+cardinal.n=N
+cardinal.s=S
+cardinal.e=O
+cardinal.w=W
+
+# Undo operations
+undo.load=laai data
+undo.loadphotos=laai fotos
+undo.editpoint=eind punt
+undo.deletepoint=verwyder punt
+undo.removephoto=verwyder foto
+undo.deleterange=verwyder reeks
+undo.insert=voeg punte by
+undo.reverse=keer baan om
+undo.mergetracksegments=smelt baan segmente
+undo.addtimeoffset=voeg tyd afset
+undo.addaltitudeoffset=voeg hoogte afset
+undo.rearrangewaypoints=herrangskik
+undo.cutandmove=beweeg seksie
+undo.connect=verbind
+undo.disconnect=afsluit
+undo.correlatephotos=korreleer fotos
+undo.rearrangephotos=herrangskik fotos
+undo.createpoint=skep punt
+undo.rotatephoto=roteer foto
+undo.convertnamestotimes=skakel name na tye
+undo.lookupsrtm=soek hoogtes vanaf SRTM
+undo.deletefieldvalues=verwyder veld waardes
+
+# Error messages
+error.save.dialogtitle=Fout om data te stoor
+error.save.nodata=Geen data om te stoor
+error.save.failed=Stoor van data na l\u00eaer het misluk
+error.saveexif.filenotfound=Find van foto het misluk
+error.saveexif.cannotoverwrite1=Foto leer
+error.saveexif.cannotoverwrite2=is lees-alleen en kan nie oorskruif word nie. Skyf na kopie?
+error.saveexif.failed1=Stoor het misluk
+error.saveexif.failed2=van die beelde
+error.saveexif.forced2=van die beelde vereis vorsering
+error.load.dialogtitle=Fout met laai van data
+error.load.noread=Kan nie leer lees
+error.load.nopoints=Geen koordinaat informasie gevind in the leer
+error.load.unknownxml=Onherkenbare xml formaat:
+error.load.noxmlinzip=Geen xml leer gevind binne zip leer
+error.jpegload.dialogtitle=Fout met fotos laai
+error.jpegload.nofilesfound=Geen leers gevind
+error.jpegload.nojpegsfound=Geen jpeg leers gevind
+error.jpegload.nogpsfound=Geen GPS informasie gevind
+error.gpsload.unknown=Onbekende fout
+error.function.noop.title=Funksie het geen effek gehad
+error.rearrange.noop=Herrangskikking van punte het geen effek gehad
+error.function.notavailable.title=Funksie nie beskikbaar
+error.3d='n fout het gebeur met die 3d vertoon
+error.readme.notfound=Readme leer nie gevind
+error.osmimage.dialogtitle=Fout met laai van kaart beelde
+error.osmimage.failed=Milsukking met die laai van kaart beelde.Toets internet conneksie
+error.language.wrongfile=Die geselekteerde leer lyk nie soos 'n taal leer vir GpsPrune
+error.convertnamestotimes.nonames=Geen name kon in tye omgeskakel word
+error.lookupsrtm.nonefound=Geen hoogte waardes beskikbaar vir die punte
+error.lookupsrtm.nonerequired=Al die punte het klaar hoogtes, so daar is niks meer om te soek
+error.gpsies.uploadnotok=Die gpsies server het die boodskap terug gestuur
+error.gpsies.uploadfailed=Die oplaai het misluk met die volgende fout boodskap
index 248dfb75b51a74bf0c0bb7a08ecc8e5ad21155d3..17fcadc20d7ab70222bdc3a252efb1fa18672e30 100644 (file)
@@ -10,6 +10,7 @@ menu.file.exit=Konec
 menu.track=Trasa
 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
@@ -20,8 +21,6 @@ menu.range.all=Vybrat v\u0161e
 menu.range.none=Zru\u0161it v\u00fdb\u011br
 menu.range.start=Nastavit za\u010d\u00e1tek rozmez\u00ed
 menu.range.end=Nastavit konec rozmez\u00ed
-menu.range.deleterange=Smazat rozmez\u00ed
-menu.range.interpolate=Interpolovat
 menu.range.average=St\u0159ed z v\u00fdb\u011bru
 menu.range.reverse=Obr\u00e1tit rozmez\u00ed
 menu.range.mergetracksegments=Slou\u010dit \u010d\u00e1sti trasy
@@ -54,6 +53,7 @@ menu.map.connect=Propojit body trasy
 menu.map.autopan=Automatika zorn\u00e9ho pole
 menu.map.showmap=Zobrazit mapu
 menu.map.showscalebar=Zobrazit m\u011b\u0159\u00edtko
+menu.map.editmode=Edita\u010dn\u00ed m\u00f3d
 
 # Alt keys for menus
 altkey.menu.file=S
@@ -86,6 +86,9 @@ function.exportpov=Export POV
 function.exportsvg=Export SVG
 function.editwaypointname=Nastavit n\u00e1zev v\u00fdzna\u010dn\u00e9ho bodu
 function.compress=Komprimovat trasu
+function.deleterange=Smazat rozmez\u00ed
+function.croptrack=O\u0159\u00edznout trasu
+function.interpolate=Interpolovat body
 function.addtimeoffset=P\u0159idat \u010dasov\u00fd posun
 function.addaltitudeoffset=P\u0159idat v\u00fd\u0161kov\u00fd posun
 function.convertnamestotimes=P\u0159ev\u00e9st n\u00e1zvy v\u00fdzna\u010dn\u00fdch bod\u016f na \u010dasy
@@ -140,6 +143,7 @@ dialog.deletepoint.title=Smazat bod
 dialog.deletepoint.deletephoto=Odebrat fotografii p\u0159ipojenou k tomuto bodu?
 dialog.deletephoto.title=Odebrat fotografii
 dialog.deletephoto.deletepoint=Smazat bod p\u0159ipojen\u00fd k t\u00e9to fotografii?
+dialog.deleteaudio.deletepoint=Smazat bod p\u0159ipojen\u00fd k t\u00e9to audionahr\u00e1vce?
 dialog.openoptions.title=Volby p\u0159i na\u010dten\u00ed
 dialog.openoptions.filesnippet=N\u00e1hled souboru
 dialog.load.table.field=Pole
@@ -219,8 +223,8 @@ dialog.confirmreversetrack.title=Potvr\u010fte obr\u00e1cen\u00ed
 dialog.confirmreversetrack.text=Tato trasa obsahuje \u010dasov\u00e9 zna\u010dky, jejich\u017e po\u0159ad\u00ed se obr\u00e1cen\u00edm zm\u011bn\u00ed.\nOpravdu chcete obr\u00e1tit v\u00fdb\u011br?
 dialog.confirmcutandmove.title=Potvr\u010fte p\u0159esun
 dialog.confirmcutandmove.text=Tato trasa obsahuje \u010dasov\u00e9 zna\u010dky, jejich\u017e po\u0159ad\u00ed se p\u0159esunem zm\u011bn\u00ed.\nOpravdu chcete v\u00fdb\u011br p\u0159esunout?
-dialog.interpolate.title=Interpolovat body
 dialog.interpolate.parameter.text=Po\u010det bod\u016f, kter\u00e9 se maj\u00ed p\u0159idat mezi vybran\u00e9 body
+dialog.interpolate.betweenwaypoints=Vlo\u017eit nov\u00e9 body trasy mezi v\u00fdzna\u010dn\u00fdmi body?
 dialog.undo.title=Vr\u00e1tit akci (akce)
 dialog.undo.pretext=Pros\u00edm vyberte akci (akce) k vr\u00e1cen\u00ed
 dialog.undo.none.title=Nelze vr\u00e1tit
@@ -237,7 +241,7 @@ dialog.pointedit.changevalue.title=Upravit pole
 dialog.pointnameedit.name=N\u00e1zev v\u00fdzna\u010dn\u00e9ho bodu
 dialog.pointnameedit.uppercase=VELK\u00c1 p\u00edsmena
 dialog.pointnameedit.lowercase=mal\u00e1 p\u00edsmena
-dialog.pointnameedit.sentencecase=Po\u010d\u00e1te\u010dn\u00ed P\u00edsmena Velk\u00e1
+dialog.pointnameedit.titlecase=Po\u010d\u00e1te\u010dn\u00ed P\u00edsmena Velk\u00e1
 dialog.addtimeoffset.add=P\u0159idat \u010das
 dialog.addtimeoffset.subtract=Odebrat \u010das
 dialog.addtimeoffset.days=Dny
@@ -273,6 +277,8 @@ dialog.distances.column.to=Do bodu
 dialog.distances.currentpoint=Aktu\u00e1ln\u00ed bod
 dialog.distances.toofewpoints=Je t\u0159eba zadat v\u00edce bod\u016f, aby bylo mo\u017en\u00e9 vypo\u010d\u00edst jejich vzd\u00e1lenost
 dialog.fullrangedetails.intro=Zobrazuji detaily vybran\u00e9ho rozmez\u00ed
+dialog.fullrangedetails.coltotal=V\u010detn\u011b p\u0159eru\u0161en\u00ed
+dialog.fullrangedetails.colsegments=Bez p\u0159eru\u0161en\u00ed
 dialog.setmapbg.intro=Vyberte jeden ze zdroj\u016f map nebo p\u0159idejte nov\u00fd
 dialog.addmapsource.title=P\u0159idat nov\u00fd zdroj map
 dialog.addmapsource.sourcename=N\u00e1zev zdroje
@@ -303,6 +309,7 @@ dialog.wikipedia.column.name=N\u00e1zev \u010dl\u00e1nku
 dialog.wikipedia.column.distance=Vzd\u00e1lenost
 dialog.correlate.notimestamps=U bod\u016f nejsou \u010dasov\u00e9 zna\u010dky, tak\u017ee nen\u00ed s \u010d\u00edm fotografie sladit.
 dialog.correlate.nouncorrelatedphotos=V\u0161echny fotografie jsou slad\u011bn\u00e9.\nOpravdu chcete pokra\u010dovat?
+dialog.correlate.nouncorrelatedaudios=V\u0161echny audionahr\u00e1vky jsou slad\u011bn\u00e9.\nOpravdu chcete pokra\u010dovat?
 dialog.correlate.photoselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fdch fotografi\u00ed pro ur\u010den\u00ed \u010dasov\u00e9ho posunu
 dialog.correlate.select.photoname=N\u00e1zev fotografie
 dialog.correlate.select.timediff=\u010casov\u00fd rozd\u00edl
@@ -340,7 +347,6 @@ 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.compress.nonefound=Nemohou b\u00fdt odstran\u011bny \u017e\u00e1dn\u00e9 body trasy
 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
@@ -351,6 +357,10 @@ dialog.compress.duplicates.title=Odstran\u011bn\u00ed zdvojen\u00fdch bod\u016f
 dialog.compress.douglaspeucker.title=Douglasova-Peuckerova komprese
 dialog.compress.douglaspeucker.paramdesc=Povolen\u00e1 odchylka
 dialog.compress.summarylabel=Bod\u016f ke smaz\u00e1n\u00ed
+dialog.compress.confirm1=Bylo ozna\u010deno celkem
+dialog.compress.confirm2=bod\u016f.\nBody je mo\u017en\u00e9 smazat volbou Trasa->Smazat ozna\u010den\u00e9 body
+dialog.compress.confirmnone=Nebyly vybr\u00e1ny \u017e\u00e1dn\u00e9 body.
+dialog.deletemarked.nonefound=Nemohou b\u00fdt odstran\u011bny \u017e\u00e1dn\u00e9 body trasy
 dialog.pastecoordinates.desc=Zadejte sou\u0159adnice
 dialog.pastecoordinates.coords=Sou\u0159adnice
 dialog.pastecoordinates.nothingfound=Pros\u00edm ov\u011b\u0159te sou\u0159adnice a zkuste znovu
@@ -406,7 +416,6 @@ dialog.saveconfig.prune.languagefile=Soubor jazyka
 dialog.saveconfig.prune.gpsdevice=Za\u0159\u00edzen\u00ed GPS
 dialog.saveconfig.prune.gpsformat=Form\u00e1t GPS
 dialog.saveconfig.prune.povrayfont=Font Povray
-dialog.saveconfig.prune.metricunits=Pou\u017e\u00edvat metrick\u00e9 jednotky?
 dialog.saveconfig.prune.gnuplotpath=Cesta k gnuplot
 dialog.saveconfig.prune.gpsbabelpath=Cesta k gpsbabel
 dialog.saveconfig.prune.exiftoolpath=Cesta k exiftool
@@ -447,6 +456,7 @@ dialog.diskcache.save=Ukl\u00e1dat mapov\u00e9 podklady na disk
 dialog.diskcache.dir=Adres\u00e1\u0159 s cache
 dialog.diskcache.createdir=Vytvo\u0159it adres\u00e1\u0159
 dialog.diskcache.nocreate=Adres\u00e1\u0159 nebyl vytvo\u0159en
+dialog.diskcache.cannotwrite=Neda\u0159\u00ed se ulo\u017eit mapov\u00e9 podklady do zvolen\u00e9ho adres\u00e1\u0159e
 dialog.diskcache.table.path=Cesta
 dialog.diskcache.table.usedby=Pou\u017e\u00edv\u00e1
 dialog.diskcache.table.zoom=Zv\u011bt\u0161en\u00ed
@@ -485,6 +495,7 @@ confirm.addaltitudeoffset=V\u00fd\u0161kov\u00fd posun zm\u011bn\u011bn
 confirm.rearrangewaypoints=Body p\u0159euspo\u0159\u00e1d\u00e1ny
 confirm.rearrangephotos=Fotografie p\u0159euspo\u0159\u00e1d\u00e1ny
 confirm.cutandmove=V\u00fdb\u011br p\u0159esunut
+confirm.interpolate=Body p\u0159id\u00e1ny
 confirm.convertnamestotimes=N\u00e1zvy bod\u016f p\u0159evedeny
 confirm.saveexif.ok1=Ulo\u017eeno
 confirm.saveexif.ok2=fotografi\u00ed
@@ -580,7 +591,6 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=d
 details.range.avespeed=Pr\u016fm. rychlost
-details.range.avemovingspeed=Okam\u017e. rychlost
 details.range.maxspeed=Max. rychlost
 details.range.numsegments=Po\u010det segment\u016f
 details.range.pace=Tempo
@@ -593,6 +603,7 @@ details.nophoto=Fotografie nevybr\u00e1na
 details.photo.loading=Na\u010d\u00edt\u00e1m
 details.photo.bearing=Azimut
 details.media.connected=P\u0159ipojeno
+details.media.fullpath=\u00dapln\u00e1 cesta
 details.audiodetails=Detaily audionahr\u00e1vky
 details.noaudio=Audionahr\u00e1vka nevybr\u00e1na
 details.audio.file=Zvukov\u00fd soubor
@@ -626,12 +637,15 @@ units.feet=Stopy
 units.feet.short=stop
 units.kilometres=Kilometry
 units.kilometres.short=km
-units.kmh=km/h
+units.kilometresperhour.short=km/h
 units.miles=M\u00edle
 units.miles.short=mil
-units.mph=mph
-units.metrespersec=m/s
-units.feetpersec=stop/s
+units.milesperhour.short=mph
+units.nauticalmiles=N\u00e1mo\u0159n\u00ed m\u00edle
+units.nauticalmiles.short=N.m.
+units.nauticalmilesperhour.short=uzly
+units.metrespersec.short=m/s
+units.feetpersec.short=stop/s
 units.hours=hodin
 units.degminsec=Deg-min-sec
 units.degmin=Deg-min
@@ -657,7 +671,8 @@ undo.deletepoint=smazat bod
 undo.removephoto=odebrat fotografii
 undo.removeaudio=odebrat audionahr\u00e1vku
 undo.deleterange=smazat rozmez\u00ed
-undo.compress=zkomprimovat trasu
+undo.croptrack=o\u0159\u00edznout trasu
+undo.deletemarked=zkomprimovat trasu
 undo.insert=vlo\u017eit body
 undo.reverse=obr\u00e1tit rozmez\u00ed
 undo.mergetracksegments=slou\u010dit \u010d\u00e1sti trasy
@@ -716,7 +731,9 @@ error.lookupsrtm.nonefound=Pro tyto body nen\u00ed k dispozici informace o nadmo
 error.lookupsrtm.nonerequired=U v\u0161ech bod\u016f u\u017e je informaci o v\u00fd\u0161ce, tak\u017ee nen\u00ed co dohled\u00e1vat
 error.gpsies.uploadnotok=Server gpsies vr\u00e1til hl\u00e1\u0161en\u00ed
 error.gpsies.uploadfailed=Chyba - nepoda\u0159ilo se nahr\u00e1t data.
+error.showphoto.failed=Nepoda\u0159ilo se na\u010d\u00edst fotografii
 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
diff --git a/tim/prune/lang/prune-texts_da.properties b/tim/prune/lang/prune-texts_da.properties
new file mode 100644 (file)
index 0000000..b375fe3
--- /dev/null
@@ -0,0 +1,85 @@
+# Text entries for the GpsPrune application
+# Danish translations
+
+# Menu entries
+menu.file=Fil
+menu.file.addphotos=Tilf\u00f8j billeder
+menu.file.recentfiles=Seneste filer
+menu.file.save=Gem som tekst
+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
+menu.range=Omr\u00e5de
+menu.range.all=V\u00e6lg alle
+menu.range.none=Ingen valgt
+menu.range.start=V\u00e6lg omr\u00e5des startpunkt
+menu.range.end=V\u00e6lg omr\u00e5des slutpunkt
+menu.range.average=Dan gennemsnit af valgte omr\u00e5de
+menu.range.reverse=Dan omvendt r\u00e6kkef\u00f8lge
+menu.range.mergetracksegments=Sammensmelt sporsegmenter
+menu.range.cutandmove=Afsk\u00e6r og flyt valgte omr\u00e5de
+menu.point=Punkt
+menu.point.editpoint=Redig\u00e8r punkt
+menu.point.deletepoint=Fjern punkt
+menu.photo=Foto
+menu.photo.saveexif=Gem Exif-data
+menu.audio=Lyd
+menu.view=Udseende
+menu.view.showsidebars=Vis sidepanel
+menu.view.browser=Kort i browser
+menu.settings=Indstillinger
+menu.settings.onlinemode=Hent kort fra Internettet
+menu.settings.autosave=Gem indstillinger automatisk ved aflutning
+menu.help=Hj\u00e6lp
+# Popup menu for map
+menu.map.zoomin=Zoom ind
+menu.map.zoomout=Zoom ud
+menu.map.newpoint=Skab nyt punkt
+menu.map.drawpoints=Skab serie af punkter
+menu.map.connect=Saml punkter p\u00e5 linie
+menu.map.autopan=Autocentrering
+menu.map.showmap=Vis kort
+menu.map.showscalebar=Vis m\u00e5lestok
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=S
+altkey.menu.range=O
+altkey.menu.point=P
+altkey.menu.view=U
+altkey.menu.photo=T
+altkey.menu.audio=L
+altkey.menu.settings=I
+altkey.menu.help=H
+
+# Functions
+function.open=\u00c5bn fil
+function.importwithgpsbabel=Import\u00e9r fil med GPSBabel
+function.loadfromgps=Hent data fra GPS
+function.sendtogps=Overf\u00f8r data til GPS
+function.exportkml=Eksport\u00e9r KML
+function.exportgpx=Eksport\u00e9r GPX
+function.exportpov=Eksport\u00e9r POV
+function.exportsvg=Eksport\u00e9r SVG
+function.editwaypointname=Ret waypoint-navn
+function.compress=Komprim\u00e9r spor
+function.deleterange=Fjern det valgte omr\u00e5de
+function.croptrack=Afgr\u00e6ns spor
+function.interpolate=Interpol\u00e9r
+function.addtimeoffset=Tilf\u00f8j offset p\u00e5 tiden
+function.addaltitudeoffset=Tilf\u00f8j offset p\u00e5 h\u00f8jde
+function.convertnamestotimes=Ret waypoint-navne til tidspunkter
+function.deletefieldvalues=Fjern feltv\u00e6rdier
+function.findwaypoint=Find waypoint
+function.pastecoordinates=Indf\u00f8j nye koordinater
+function.charts=Kort
+function.show3d=3-D view
+function.distances=Afstande
+function.fullrangedetails=Vis alle detaljer
+function.setmapbg=V\u00e6lg kort som baggrund
+function.setkmzimagesize=V\u00e6lg KMZ billedst\u00f8rrelse
+function.setpaths=V\u00e6lg sti til programmer
+function.getgpsies=Se liste af GPS-spor
index 745331a6c9a8f09e780b41f559559be00d85ba4d..ee57c2152143d5d110b10b8d5e97bd7f3cddb192 100644 (file)
@@ -1,17 +1,18 @@
 # Text entries for the GpsPrune application
-# German entries as extra
+# German entries
 
 # Menu entries
 menu.file=Datei
 menu.file.addphotos=Fotos laden
 menu.file.recentfiles=Zuletzt verwendete Dateien
-menu.file.save=Als Text Speichern
+menu.file.save=Als Text speichern
 menu.file.exit=Beenden
 menu.track=Track
 menu.track.undo=R\u00fcckg\u00e4ngig
 menu.track.clearundo=Liste der letzten \u00c4nderungen l\u00f6schen
-menu.track.deletemarked=Komprimierte Punkte l\u00f6schen
-menu.track.rearrange=Wegpunkte reorganisieren
+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
@@ -20,21 +21,24 @@ menu.range.all=Alles markieren
 menu.range.none=Nichts markieren
 menu.range.start=Startpunkt setzen
 menu.range.end=Endpunkt setzen
-menu.range.deleterange=Bereich l\u00f6schen
-menu.range.interpolate=Interpolieren
 menu.range.average=Durchschnitt berechnen
 menu.range.reverse=Bereich umkehren
 menu.range.mergetracksegments=Trackabschnitte verbinden
-menu.range.cutandmove=Schneiden und verschieben
+menu.range.cutandmove=Auswahl vor den markierten Punkt verschieben
 menu.point=Punkt
 menu.point.editpoint=Punkt bearbeiten
 menu.point.deletepoint=Punkt l\u00f6schen
 menu.photo=Foto
-menu.photo.saveexif=Exif Daten speichern
+menu.photo.saveexif=Exif-Daten speichern
 menu.audio=Audio
 menu.view=Ansicht
 menu.view.showsidebars=Seitenleisten anzeigen
-menu.view.browser=Karte in Browser
+menu.view.browser=Karte in Browser anzeigen
+menu.view.browser.google=Google Maps
+menu.view.browser.openstreetmap=OpenStreetMap
+menu.view.browser.mapquest=Mapquest
+menu.view.browser.yahoo=Yahoo Maps
+menu.view.browser.bing=Bing Maps
 menu.settings=Einstellungen
 menu.settings.onlinemode=Karten aus dem Internet laden
 menu.settings.autosave=Einstellungen automatisch speichern
@@ -49,6 +53,7 @@ menu.map.connect=Trackpunkte mit Linie anzeigen
 menu.map.autopan=Autozentrierung
 menu.map.showmap=Karte zeigen
 menu.map.showscalebar=Ma\u00dfstab anzeigen
+menu.map.editmode=Punkte verschieben
 
 # Alt keys for menus
 altkey.menu.file=D
@@ -74,19 +79,22 @@ shortcut.menu.help.help=H
 function.open=Datei \u00f6ffnen
 function.importwithgpsbabel=Datei mit GPSBabel importieren
 function.loadfromgps=Vom GPS laden
-function.sendtogps=zum GPS schicken
+function.sendtogps=Zum GPS schicken
 function.exportkml=KML exportieren
 function.exportgpx=GPX exportieren
 function.exportpov=POV exportieren
 function.exportsvg=SVG exportieren
-function.editwaypointname=Wegpunkt Name bearbeiten
+function.editwaypointname=Name des Punkts bearbeiten
 function.compress=Track komprimieren
+function.deleterange=Bereich l\u00f6schen
+function.croptrack=Track zuschneiden
+function.interpolate=Punkte interpolieren
 function.addtimeoffset=Zeitverschiebung aufrechnen
 function.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
-function.convertnamestotimes=Wegpunktenamen in Zeitstempel umwandeln
+function.convertnamestotimes=Namen der Wegpunkte in Zeitstempel umwandeln
 function.deletefieldvalues=Werte eines Feldes l\u00f6schen
 function.findwaypoint=Wegpunkt finden
-function.pastecoordinates=Neue Koordinaten eingeben
+function.pastecoordinates=Neuen Wegpunkt anlegen
 function.charts=Diagramme
 function.show3d=3D Ansicht
 function.distances=Entfernungen
@@ -94,12 +102,12 @@ function.fullrangedetails=Zus\u00e4tzliche Bereichdetails
 function.setmapbg=Karte Hintergrund setzen
 function.setkmzimagesize=Bildgr\u00f6\u00dfe im KMZ setzen
 function.setpaths=Programmpfade setzen
-function.getgpsies=Gpsies Tracks holen
-function.uploadgpsies=Daten zum Gpsies hochladen
-function.lookupsrtm=H\u00f6hendaten von SRTM holen
+function.getgpsies=Tracks bei GPSies.com herunterladen
+function.uploadgpsies=Track zu GPSies.com hochladen
+function.lookupsrtm=H\u00f6hendaten von SRTM herunterladen
 function.getwikipedia=Wikipediaartikel in der N\u00e4he nachschlagen
 function.searchwikipedianames=Wikipedia mit Name durchsuchen
-function.downloadosm=OSM Daten f\u00fcr dieses Gebiet herunterladen
+function.downloadosm=OSM-Daten f\u00fcr dieses Gebiet herunterladen
 function.duplicatepoint=Punkt verdoppeln
 function.setcolours=Farben einstellen
 function.setlinewidth=Liniendicke einstellen
@@ -108,14 +116,14 @@ function.connecttopoint=Mit Punkt verkn\u00fcpfen
 function.disconnectfrompoint=Vom Punkt trennen
 function.removephoto=Foto entfernen
 function.correlatephotos=Fotos korrelieren
-function.rearrangephotos=Fotos reorganisieren
-function.rotatephotoleft=Foto nach Links drehen
-function.rotatephotoright=Foto nach Rechts drehen
+function.rearrangephotos=Fotos neu anordnen
+function.rotatephotoleft=Foto nach links drehen
+function.rotatephotoright=Foto nach rechts drehen
 function.photopopup=Fotofenster anzeigen
-function.ignoreexifthumb=Exif Vorschaubild ignorieren
+function.ignoreexifthumb=Exif-Vorschaubild ignorieren
 function.loadaudio=Audiodateien laden
 function.removeaudio=Audiodatei entfernen
-function.correlateaudios=Audios korrelieren
+function.correlateaudios=Audiodateien korrelieren
 function.playaudio=Audiodatei abspielen
 function.stopaudio=Abspielen abbrechen
 function.help=Hilfe
@@ -135,30 +143,31 @@ dialog.deletepoint.title=Punkt l\u00f6schen
 dialog.deletepoint.deletephoto=Das zu diesem Punkt geh\u00f6rende Foto ebenfalls l\u00f6schen?
 dialog.deletephoto.title=Foto entfernen
 dialog.deletephoto.deletepoint=Den zu diesem Foto geh\u00f6renden Punkt auch l\u00f6schen?
+dialog.deleteaudio.deletepoint=Den zu dieser Audiodatei geh\u00f6renden Punkt auch l\u00f6schen?
 dialog.openoptions.title=\u00d6ffnen
 dialog.openoptions.filesnippet=Dateiausschnitt
 dialog.load.table.field=Feld
 dialog.load.table.datatype=Datentyp
 dialog.load.table.description=Beschreibung
-dialog.delimiter.label=Feld Trennzeichen
+dialog.delimiter.label=Feld-Trennzeichen
 dialog.delimiter.comma=Komma ,
-dialog.delimiter.tab=Tab
+dialog.delimiter.tab=Tabulator
 dialog.delimiter.space=Leerzeichen
 dialog.delimiter.semicolon=Strichpunkt ;
 dialog.delimiter.other=Andere
 dialog.openoptions.deliminfo.records=Datens\u00e4tze, mit
 dialog.openoptions.deliminfo.fields=Feldern
 dialog.openoptions.deliminfo.norecords=Keine Datens\u00e4tze
-dialog.openoptions.altitudeunits=H\u00f6he Ma\u00dfeinheiten
-dialog.open.contentsdoubled=Diese Datei enth\u00e4lt zwei Kopien von jedem Punkt,\neinmal als Waypoint und einmal als Trackpunkt.
+dialog.openoptions.altitudeunits=Ma\u00dfeinheiten f\u00fcr die H\u00f6he
+dialog.open.contentsdoubled=Diese Datei enth\u00e4lt zwei Kopien jedes Punkts,\neinmal als Waypoint und einmal als Trackpunkt.
 dialog.selecttracks.intro=W\u00e4hlen Sie den Track oder die Tracks aus, die Sie laden m\u00f6chten
 dialog.selecttracks.noname=Unbenannt
 dialog.jpegload.subdirectories=Unterordner mit durchsuchen
 dialog.jpegload.loadjpegswithoutcoords=Auch Fotos ohne Koordinaten laden
-dialog.jpegload.loadjpegsoutsidearea=Auch Fotos ausserhalb des Tracks laden
+dialog.jpegload.loadjpegsoutsidearea=Auch Fotos au\u00dferhalb des Tracks laden
 dialog.jpegload.progress.title=Fotos werden geladen
-dialog.jpegload.progress=Bitte warten w\u00e4hrend die Fotos durchsucht werden
-dialog.gpsload.nogpsbabel=Programm gpsbabel wurde nicht gefunden. Weiter?
+dialog.jpegload.progress=Bitte warten, w\u00e4hrend die Fotos durchsucht werden
+dialog.gpsload.nogpsbabel=Programm GPSBabel wurde nicht gefunden. Weiter?
 dialog.gpsload.device=Ger\u00e4tename
 dialog.gpsload.format=Format
 dialog.gpsload.getwaypoints=Wegpunkte laden
@@ -173,25 +182,25 @@ dialog.save.table.field=Feld
 dialog.save.table.hasdata=Enth\u00e4lt Daten
 dialog.save.table.save=Speichern
 dialog.save.headerrow=Titelzeile speichern
-dialog.save.coordinateunits=Koordinaten Ma\u00dfeinheiten
-dialog.save.altitudeunits=H\u00f6he Ma\u00dfeinheiten
+dialog.save.coordinateunits=Format der Koordinaten
+dialog.save.altitudeunits=Ma\u00dfeinheiten f\u00fcr die H\u00f6he
 dialog.save.timestampformat=Zeitstempelformat
 dialog.save.overwrite.title=Datei schon vorhanden
 dialog.save.overwrite.text=Diese Datei gibt es schon. Wollen Sie die vorhandene Datei \u00fcberschreiben?
 dialog.save.notypesselected=Keine Punktetypen sind ausgew\u00e4hlt
 dialog.exportkml.text=Titel f\u00fcr die Daten
 dialog.exportkml.altitude=Absolute H\u00f6heninformation (f\u00fcr Luftfahrt)
-dialog.exportkml.kmz=Daten in kmz Datei komprimieren
-dialog.exportkml.exportimages=Vorschaubilder mit in kmz exportieren
+dialog.exportkml.kmz=Daten in KMZ-Datei komprimieren
+dialog.exportkml.exportimages=Vorschaubilder mit in KMZ-Datei exportieren
 dialog.exportkml.trackcolour=Trackfarbe
 dialog.exportgpx.name=Name
 dialog.exportgpx.desc=Beschreibung
 dialog.exportgpx.includetimestamps=Zeitstempel mit exportieren
-dialog.exportgpx.copysource=Xml von Quelle kopieren
+dialog.exportgpx.copysource=XML von Quelle kopieren
 dialog.exportgpx.encoding=Enkodierung
 dialog.exportgpx.encoding.system=System
 dialog.exportgpx.encoding.utf8=UTF-8
-dialog.exportpov.text=Geben Sie die Parameter f\u00fcr den POV Export ein
+dialog.exportpov.text=Geben Sie die Parameter f\u00fcr den POV-Export ein
 dialog.exportpov.font=Font
 dialog.exportpov.camerax=Kamera X
 dialog.exportpov.cameray=Kamera Y
@@ -200,7 +209,7 @@ dialog.exportpov.modelstyle=Modellstil
 dialog.exportpov.ballsandsticks=B\u00e4lle und Stangen
 dialog.exportpov.tubesandwalls=R\u00f6hren und W\u00e4nde
 dialog.exportpov.warningtracksize=Dieser Track hat sehr viele Punkte, die Java3D vielleicht nicht bearbeiten kann.\nM\u00f6chten Sie den Vorgang trotzdem fortsetzen?
-dialog.exportsvg.text=W\u00e4hlen Sie die Parameter f\u00fcr den SVG Export aus
+dialog.exportsvg.text=W\u00e4hlen Sie die Parameter f\u00fcr den SVG-Export aus
 dialog.exportsvg.phi=Richtungswinkel \u03d5
 dialog.exportsvg.theta=Neigungswinkel \u03b8
 dialog.exportsvg.gradients=Farbverl\u00e4ufe verwenden
@@ -214,8 +223,8 @@ dialog.confirmreversetrack.title=Umkehrung best\u00e4tigen
 dialog.confirmreversetrack.text=Diese Daten enthalten Zeitangaben, die bei einer Umkehrung in falscher Reihenfolge erscheinen w\u00fcrden.\nSind Sie sicher, dass Sie diesen Bereich umkehren wollen?
 dialog.confirmcutandmove.title=Verschieben best\u00e4tigen
 dialog.confirmcutandmove.text=Diese Daten enthalten Zeitangaben, die nach dem Verschieben in falscher Reihenfolge erscheinen w\u00fcrden.\nSind Sie sicher, dass Sie diesen Bereich verschieben wollen?
-dialog.interpolate.title=Punkte interpolieren
 dialog.interpolate.parameter.text=Anzahl Punkte, die zwischen den gew\u00e4hlten Punkten eingef\u00fcgt werden sollen
+dialog.interpolate.betweenwaypoints=Zwischen den Wegpunkten interpolieren?
 dialog.undo.title=Aktionen R\u00fcckg\u00e4ngig
 dialog.undo.pretext=Bitte die Operationen, die r\u00fcckg\u00e4ngig gemacht werden sollen, ausw\u00e4hlen.
 dialog.undo.none.title=Undo nicht m\u00f6glich
@@ -223,16 +232,16 @@ dialog.undo.none.text=Keine Operationen k\u00f6nnen r\u00fcckg\u00e4ngig gemacht
 dialog.clearundo.title=Undo-Liste l\u00f6schen
 dialog.clearundo.text=Wollen Sie wirklich die Undo-Liste l\u00f6schen?\nAlle Undo- Informationen werden verloren gehen!
 dialog.pointedit.title=Punkt bearbeiten
-dialog.pointedit.text=W\u00e4hlen Sie die Felder aus, die Sie bearbeiten m\u00f6chten, und verwenden Sie den 'Bearbeiten' Knopf, um den Wert zu \u00e4ndern
+dialog.pointedit.text=W\u00e4hlen Sie die Felder aus, die Sie bearbeiten m\u00f6chten, und verwenden Sie den 'Bearbeiten'-Button, um den Wert zu \u00e4ndern
 dialog.pointedit.table.field=Feld
 dialog.pointedit.table.value=Wert
 dialog.pointedit.table.changed=Ge\u00e4ndert
 dialog.pointedit.changevalue.text=Geben Sie den neuen Wert f\u00fcr dieses Feld ein
 dialog.pointedit.changevalue.title=Feld bearbeiten
-dialog.pointnameedit.name=Wegpunkt Name
-dialog.pointnameedit.uppercase=GRO\u00df geschrieben
+dialog.pointnameedit.name=Name des Wegpunkts
+dialog.pointnameedit.uppercase=GROSS geschrieben
 dialog.pointnameedit.lowercase=klein geschrieben
-dialog.pointnameedit.sentencecase=Gemischt geschrieben
+dialog.pointnameedit.titlecase=Gemischt geschrieben
 dialog.addtimeoffset.add=Zeit addieren
 dialog.addtimeoffset.subtract=Zeit subtrahieren
 dialog.addtimeoffset.days=Tage
@@ -244,8 +253,8 @@ dialog.findwaypoint.search=Suche
 dialog.saveexif.title=Exif speichern
 dialog.saveexif.intro=W\u00e4hlen Sie die Fotos zum Speichern aus
 dialog.saveexif.nothingtosave=Koordinaten sind unver\u00e4ndert. Es gibt nichts zu speichern.
-dialog.saveexif.noexiftool=Kein exiftool Programm gefunden. Trotzdem fortfahren?
-dialog.saveexif.table.photoname=Foto Name
+dialog.saveexif.noexiftool=Kein ExifTool-Programm gefunden. Trotzdem fortfahren?
+dialog.saveexif.table.photoname=Name des Fotos
 dialog.saveexif.table.status=Status
 dialog.saveexif.table.save=Speichern
 dialog.saveexif.photostatus.connected=Verbunden
@@ -253,21 +262,23 @@ dialog.saveexif.photostatus.disconnected=Getrennt
 dialog.saveexif.photostatus.modified=Modifiziert
 dialog.saveexif.overwrite=Dateien \u00fcberschreiben
 dialog.saveexif.force=Erzwingen trotz geringf\u00fcgiger Fehler
-dialog.charts.xaxis=X Achse
-dialog.charts.yaxis=Y Achse
+dialog.charts.xaxis=X-Achse
+dialog.charts.yaxis=Y-Achse
 dialog.charts.output=Ausgabe
 dialog.charts.screen=Ausgabe auf Bildschirm
-dialog.charts.svg=Ausgabe in SVG Datei
-dialog.charts.svgwidth=SVG Breite
-dialog.charts.svgheight=SVG H\u00f6he
-dialog.charts.needaltitudeortimes=Ohne Daten \u00fcber H\u00f6he und Zeit kann kein Diagramm erzeugt werden.
+dialog.charts.svg=Ausgabe in SVG-Datei
+dialog.charts.svgwidth=SVG-Breite
+dialog.charts.svgheight=SVG-H\u00f6he
+dialog.charts.needaltitudeortimes=Ohne Daten zu H\u00f6he und Zeit kann kein Diagramm erzeugt werden.
 dialog.charts.gnuplotnotfound=Gnuplot konnte im angegebenen Pfad nicht gefunden werden
 dialog.distances.intro=Luftlinienentfernung zwischen Punkten
 dialog.distances.column.from=Vom Punkt
 dialog.distances.column.to=Zum Punkt
 dialog.distances.currentpoint=Aktueller Punkt
-dialog.distances.toofewpoints=Diese Funktion braucht Wegpunkte um die Distanzen zu berechnen
-dialog.fullrangedetails.intro=Hier sind die Details des markierten Bereichs
+dialog.distances.toofewpoints=Diese Funktion braucht Wegpunkte, um die Distanzen zu berechnen
+dialog.fullrangedetails.intro=Detaillierte Angaben zum markierten Bereich
+dialog.fullrangedetails.coltotal=Mit L\u00fccken
+dialog.fullrangedetails.colsegments=Ohne L\u00fccken
 dialog.setmapbg.intro=Eine der Quellen ausw\u00e4hlen oder eine neue hinzuf\u00fcgen
 dialog.addmapsource.title=Neue Kartenquelle hinzuf\u00fcgen
 dialog.addmapsource.sourcename=Name der Quelle
@@ -276,28 +287,29 @@ dialog.addmapsource.layer2url=URL f\u00fcr obere Ebene (falls n\u00f6tig)
 dialog.addmapsource.maxzoom=Maximales Zoom
 dialog.addmapsource.cloudstyle=Stilnummer
 dialog.addmapsource.noname=Unbenannt
-dialog.gpsies.column.name=Track Name
+dialog.gpsies.column.name=Name des Tracks
 dialog.gpsies.column.length=L\u00e4nge
 dialog.gpsies.description=Beschreibung
 dialog.gpsies.nodescription=Keine Beschreibung
 dialog.gpsies.nonefound=Keine Tracks gefunden
-dialog.gpsies.username=Gpsies Username
-dialog.gpsies.password=Gpsies Passwort
+dialog.gpsies.username=Username bei GPSies.com
+dialog.gpsies.password=Passwort bei GPSies.com
 dialog.gpsies.keepprivate=Track privat halten
 dialog.gpsies.confirmopenpage=Webseite f\u00fcr den hochgeladenen Track \u00f6ffnen?
 dialog.gpsies.activities=Aktivit\u00e4ten
 dialog.gpsies.activity.trekking=Wandern
 dialog.gpsies.activity.walking=Walking
 dialog.gpsies.activity.jogging=Laufen
-dialog.gpsies.activity.biking=Fahrradtour
-dialog.gpsies.activity.motorbiking=Motorrad
-dialog.gpsies.activity.snowshoe=Schneeschuh
+dialog.gpsies.activity.biking=Fahrradfahren
+dialog.gpsies.activity.motorbiking=Motorradfahren
+dialog.gpsies.activity.snowshoe=Schneeschuhwandern
 dialog.gpsies.activity.sailing=Segeln
 dialog.gpsies.activity.skating=Inline-Skating
 dialog.wikipedia.column.name=Artikelname
 dialog.wikipedia.column.distance=Entfernung
 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?
 dialog.correlate.photoselect.intro=W\u00e4hlen Sie eines dieser Fotos aus, um die Zeitdifferenz zu berechnen
 dialog.correlate.select.photoname=Bezeichnung des Fotos
 dialog.correlate.select.timediff=Zeitdifferenz
@@ -313,20 +325,20 @@ dialog.correlate.options.photolater=Foto sp\u00e4ter als Punkt
 dialog.correlate.options.pointlaterphoto=Punkt sp\u00e4ter als Foto
 dialog.correlate.options.audiolater=Audio sp\u00e4ter als Punkt
 dialog.correlate.options.pointlateraudio=Punkt sp\u00e4ter als Audio
-dialog.correlate.options.limitspanel=Korrelation Grenzen
+dialog.correlate.options.limitspanel=Grenzen der Korrelation
 dialog.correlate.options.notimelimit=Keine Zeitgrenzen
 dialog.correlate.options.timelimit=Zeitgrenzen
 dialog.correlate.options.nodistancelimit=Keine Distanzgrenzen
 dialog.correlate.options.distancelimit=Distanzgrenzen
 dialog.correlate.options.correlate=Korrelieren
-dialog.correlate.alloutsiderange=Alle Fotos sind au\u00dferhalb des Track Zeitraums. Sie k\u00f6nnen nicht korreliert werden.\nVersuchen Sie es mit einem anderen Offset oder binden Sie manuell mindestens ein Foto ein.
+dialog.correlate.alloutsiderange=Alle Elemente liegen au\u00dferhalb des Track-Zeitraums und k\u00f6nnen deshalb nicht korreliert werden.\nVersuchen Sie es mit einem anderen Offset oder binden Sie manuell mindestens ein Element ein.
 dialog.correlate.filetimes=Die Datei Zeitstempel zeigen:
 dialog.correlate.filetimes2=der Tonspuren
-dialog.correlate.correltimes=F\u00fcr das Korrelieren, folgendes verwenden:
+dialog.correlate.correltimes=F\u00fcr das Korrelieren Folgendes verwenden:
 dialog.correlate.timestamp.beginning=Anfang
 dialog.correlate.timestamp.middle=Mitte
 dialog.correlate.timestamp.end=Ende
-dialog.correlate.audioselect.intro=W\u00e4hlen Sie eines dieser Audios aus, um die Zeitdifferenz zu berechnen
+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.rearrangephotos.desc=Setzen Sie das Ziel und die Reihenfolge der Fotopunkte
@@ -335,62 +347,65 @@ dialog.rearrangephotos.toend=Am Ende
 dialog.rearrangephotos.nosort=Nicht sortieren
 dialog.rearrangephotos.sortbyfilename=per Dateiname sortieren
 dialog.rearrangephotos.sortbytime=per Zeitstempel sortieren
-dialog.compress.nonefound=Es konnten keine Punkte entfernt werden
 dialog.compress.closepoints.title=Nahegelegene Punkte entfernen
-dialog.compress.closepoints.paramdesc=Span Faktor
+dialog.compress.closepoints.paramdesc=Span-Faktor
 dialog.compress.wackypoints.title=Ungew\u00f6hnliche Punkte entfernen
 dialog.compress.wackypoints.paramdesc=Distanzfaktor
 dialog.compress.singletons.title=Singletons (isolierte Punkte) entfernen
 dialog.compress.singletons.paramdesc=Distanzfaktor
 dialog.compress.duplicates.title=Duplikate entfernen
-dialog.compress.douglaspeucker.title=Douglas-Peucker Komprimierung
-dialog.compress.douglaspeucker.paramdesc=Span Faktor
-dialog.compress.summarylabel=Punkte zu entfernen
-dialog.pastecoordinates.desc=Geben Sie die Koordinaten ein
+dialog.compress.douglaspeucker.title=Douglas-Peucker-Komprimierung
+dialog.compress.douglaspeucker.paramdesc=Span-Faktor
+dialog.compress.summarylabel=Zu entfernende Punkte
+dialog.compress.confirm1=Es wurden
+dialog.compress.confirm2=Punkte markiert.\nMit Track->Markierte Punkte l\u00f6schen werden sie gel\u00f6scht
+dialog.compress.confirmnone=es wurden keine Punkte markiert
+dialog.deletemarked.nonefound=Es konnten keine Punkte entfernt werden
+dialog.pastecoordinates.desc=Koordinaten eingeben oder einf\u00fcgen
 dialog.pastecoordinates.coords=Koordinaten
 dialog.pastecoordinates.nothingfound=Bitte pr\u00fcfen Sie die Koordinaten und versuchen Sie es nochmals
 dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n http://activityworkshop.net/software/gpsprune/
 dialog.about.version=Version
 dialog.about.build=Build
-dialog.about.summarytext1=GpsPrune ist ein Programm zum Laden, Darstellen und Editieren von Daten von GPS Ger\u00e4ten.
-dialog.about.summarytext2=Es wird unter der Gnu GPL zur Verf\u00fcgung gestellt, zum freien, kostenlosen und offenen Gebrauch und zur Weiterentwicklung.<br>Kopieren, Weiterverbreitung und Ver\u00e4nderungen sind erlaubt und willkommen<br>unter den in der <code>license.txt</code> Datei enthaltenen Bedingungen.
+dialog.about.summarytext1=GpsPrune ist ein Programm zum Laden, Darstellen und Editieren der Daten von GPS- Ger\u00e4ten.
+dialog.about.summarytext2=Es wird unter der Gnu GPL zur Verf\u00fcgung gestellt zum freien, kostenlosen und offenen Gebrauch und zur Weiterentwicklung.<br>Das Kopieren, Weiterverbreiten und Ver\u00e4ndern ist erlaubt und willkommen<br>unter den in der Datei <code>license.txt</code> enthaltenen Bedingungen.
 dialog.about.summarytext3=Auf der Seite <code style="font-weight:bold">http://activityworkshop.net/</code> finden Sie weitere Informationen und Bedienungsanleitungen.
 dialog.about.languages=Verf\u00fcgbare Sprachen
 dialog.about.translatedby=Deutsche \u00dcbersetzung von activityworkshop.
-dialog.about.systeminfo=System Informationen
+dialog.about.systeminfo=System-Informationen
 dialog.about.systeminfo.os=Betriebssystem
 dialog.about.systeminfo.java=Java Runtime
 dialog.about.systeminfo.java3d=Java3d installiert
 dialog.about.systeminfo.povray=Povray installiert
-dialog.about.systeminfo.exiftool=Exiftool installiert
-dialog.about.systeminfo.gpsbabel=Gpsbabel installiert
+dialog.about.systeminfo.exiftool=ExifTool installiert
+dialog.about.systeminfo.gpsbabel=GPSBabel installiert
 dialog.about.systeminfo.gnuplot=Gnuplot installiert
-dialog.about.systeminfo.exiflib=Exif Bibliothek
+dialog.about.systeminfo.exiflib=Exif-Bibliothek
 dialog.about.systeminfo.exiflib.internal=Intern
 dialog.about.systeminfo.exiflib.internal.failed=Intern (nicht gefunden)
 dialog.about.systeminfo.exiflib.external=Extern
 dialog.about.systeminfo.exiflib.external.failed=Extern (nicht gefunden)
 dialog.about.yes=Ja
 dialog.about.no=Nein
-dialog.about.credits=Credits
-dialog.about.credits.code=GpsPrune Code geschrieben von
-dialog.about.credits.exifcode=Exif Code von
+dialog.about.credits=Danksagung
+dialog.about.credits.code=GpsPrune-Code geschrieben von
+dialog.about.credits.exifcode=Exif-Code von
 dialog.about.credits.icons=Einige Bilder von
 dialog.about.credits.translators=Dolmetscher
 dialog.about.credits.translations=\u00dcbersetzungen mit Hilfe von
 dialog.about.credits.devtools=Entwicklungsprogramme
 dialog.about.credits.othertools=Andere Programme
 dialog.about.credits.thanks=Dank an
-dialog.about.readme=Liesmich
-dialog.checkversion.error=Die Versionnummer konnte nicht ermittelt werden.\nBitte pr\u00fcfen Sie die Internet Verbindung.
+dialog.about.readme=Lies mich
+dialog.checkversion.error=Die Versionnummer konnte nicht ermittelt werden.\nBitte pr\u00fcfen Sie die Internet-Verbindung.
 dialog.checkversion.uptodate=Sie haben schon die neueste Version von GpsPrune.
-dialog.checkversion.newversion1=Eine neue Version vom GpsPrune ist jetzt verf\u00fcgbar! Die neue Version ist Version
+dialog.checkversion.newversion1=Eine neue Version von GpsPrune ist jetzt verf\u00fcgbar! Die neue Version ist Version
 dialog.checkversion.newversion2=.
 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://activityworkshop.net/software/gpsprune/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>Vorherigen oder n\u00e4chsten 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>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.normalmodifier=Strg
 dialog.keys.macmodifier=Kommando
 dialog.saveconfig.desc=Die folgende Einstellungen k\u00f6nnen gespeichert werden:
@@ -398,33 +413,32 @@ dialog.saveconfig.prune.trackdirectory=Datenverzeichnis
 dialog.saveconfig.prune.photodirectory=Fotoverzeichnis
 dialog.saveconfig.prune.languagecode=Sprachcode (DE)
 dialog.saveconfig.prune.languagefile=Sprachdatei
-dialog.saveconfig.prune.gpsdevice=GPS Ger\u00e4tename
-dialog.saveconfig.prune.gpsformat=GPS Format
-dialog.saveconfig.prune.povrayfont=Povray Font
-dialog.saveconfig.prune.metricunits=Metrische Einheiten verwenden?
-dialog.saveconfig.prune.gnuplotpath=Gnuplot Pfad
-dialog.saveconfig.prune.gpsbabelpath=Gpsbabel Pfad
-dialog.saveconfig.prune.exiftoolpath=Exiftool Pfad
-dialog.saveconfig.prune.mapsource=Kartenserver Index
+dialog.saveconfig.prune.gpsdevice=GPS-Ger\u00e4tename
+dialog.saveconfig.prune.gpsformat=GPS-Format
+dialog.saveconfig.prune.povrayfont=Povray-Font
+dialog.saveconfig.prune.gnuplotpath=Gnuplot-Pfad
+dialog.saveconfig.prune.gpsbabelpath=GPSBabel-Pfad
+dialog.saveconfig.prune.exiftoolpath=ExifTool-Pfad
+dialog.saveconfig.prune.mapsource=Kartenserver-Index
 dialog.saveconfig.prune.mapsourcelist=Kartenserver
 dialog.saveconfig.prune.diskcache=Kartenordner
 dialog.saveconfig.prune.kmzimagewidth=Bildbreite in KMZ
 dialog.saveconfig.prune.kmzimageheight=Bildh\u00f6he in KMZ
 dialog.saveconfig.prune.colourscheme=Farbschema
 dialog.saveconfig.prune.linewidth=Liniedicke
-dialog.saveconfig.prune.kmltrackcolour=KML Trackfarbe
+dialog.saveconfig.prune.kmltrackcolour=KML-Trackfarbe
 dialog.saveconfig.prune.autosavesettings=Einstellungen speichern
-dialog.setpaths.intro=Sie k\u00f6nnen hier die Pfade f\u00fcr externe Applikationen setzen:
+dialog.setpaths.intro=Sie k\u00f6nnen hier die Pfade f\u00fcr externe Programme setzen:
 dialog.setpaths.found=Pfad gefunden?
 dialog.addaltitude.noaltitudes=Der markierte Bereich enth\u00e4lt keine H\u00f6henangaben
 dialog.addaltitude.desc=Hinzuzurechnende H\u00f6henverschiebung
 dialog.lookupsrtm.overwritezeros=H\u00f6henangaben von null \u00fcberschreiben?
-dialog.setcolours.intro=Klicken Sie auf eine Farbe um sie zu \u00e4ndern
+dialog.setcolours.intro=Klicken Sie auf eine Farbe, um sie zu \u00e4ndern
 dialog.setcolours.background=Hintergrund
 dialog.setcolours.borders=Umrandungen
 dialog.setcolours.lines=Linien
 dialog.setcolours.primary=Prim\u00e4r
-dialog.setcolours.secondary=Second\u00e4r
+dialog.setcolours.secondary=Sekund\u00e4r
 dialog.setcolours.point=Punkte
 dialog.setcolours.selection=Bereich
 dialog.setcolours.text=Texte
@@ -433,15 +447,16 @@ dialog.colourchooser.red=Rot
 dialog.colourchooser.green=Gr\u00fcn
 dialog.colourchooser.blue=Blau
 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.secondintro=Sie m\u00fcssen Ihre Einstellungen speichern und dann<p>GpsPrune neu starten, um die Sprache zu \u00e4ndern.
 dialog.setlanguage.language=Sprache
 dialog.setlanguage.languagefile=Sprachdatei
-dialog.setlanguage.endmessage=Speichern Sie nun Ihre Einstellungen und starten Sie GpsPrune neu\num die neue Sprache zu verwenden.
-dialog.setlanguage.endmessagewithautosave=Starten Sie GpsPrune neu um die neue Sprache zu verwenden.
+dialog.setlanguage.endmessage=Speichern Sie nun Ihre Einstellungen und starten Sie GpsPrune neu,\num die neue Sprache zu verwenden.
+dialog.setlanguage.endmessagewithautosave=Starten Sie GpsPrune neu, um die neue Sprache zu verwenden.
 dialog.diskcache.save=Karten auf Festplatte speichern
 dialog.diskcache.dir=Kartenordner
 dialog.diskcache.createdir=Ordner anlegen
 dialog.diskcache.nocreate=Ordner wurde nicht angelegt
+dialog.diskcache.cannotwrite=Kacheln k\u00f6nnen nicht im Ordner gespeichert werden
 dialog.diskcache.table.path=Pfad
 dialog.diskcache.table.usedby=Anwender
 dialog.diskcache.table.zoom=Zoom
@@ -454,15 +469,16 @@ dialog.diskcache.maximumage=Maximales Alter (Tage)
 dialog.diskcache.deleteall=Alle Kacheln l\u00f6schen
 dialog.diskcache.deleted1=Es wurden
 dialog.diskcache.deleted2=Dateien aus dem Ordner gel\u00f6scht
-dialog.deletefieldvalues.intro=W\u00e4hlen Sie das Feld aus, die Sie l\u00f6schen m\u00f6chten
+dialog.deletefieldvalues.intro=W\u00e4hlen Sie das Feld aus, das Sie l\u00f6schen m\u00f6chten
+dialog.deletefieldvalues.nofields=Es sind keine Felder zu l\u00f6schen f\u00fcr diesen Bereich
 dialog.setlinewidth.text=Geben Sie die Dicke der Linien ein (1-4)
-dialog.downloadosm.desc=Best\u00e4tigen um rohe OSM Daten f\u00fcr den Gebiet herunterzuladen:
+dialog.downloadosm.desc=Die OpenStreetMap-Daten f\u00fcr das folgende Gebiet werden heruntergeladen (.osm-Datei):
 dialog.searchwikipedianames.search=Suche nach:
 
 # 3d window
-dialog.3d.title=GpsPrune 3D Ansicht
+dialog.3d.title=GpsPrune-3D-Ansicht
 dialog.3d.altitudefactor=Vervielfachungsfaktor f\u00fcr H\u00f6hen
-dialog.3dlines.title=GpsPrune Gitterlinien
+dialog.3dlines.title=GpsPrune-Gitterlinien
 dialog.3dlines.empty=Keine Linien zum Anzeigen!
 dialog.3dlines.intro=Hier sind die Linien f\u00fcr die 3D Ansicht
 
@@ -477,9 +493,10 @@ confirm.mergetracksegments=Trackabschnitte verbunden
 confirm.reverserange=Bereich umgekehrt
 confirm.addtimeoffset=Zeitverschiebung aufgerechnet
 confirm.addaltitudeoffset=H\u00f6henverschiebung aufgerechnet
-confirm.rearrangewaypoints=Wegpunkte reorganisiert
-confirm.rearrangephotos=Fotos reorganisiert
+confirm.rearrangewaypoints=Wegpunkte neu angeordnet
+confirm.rearrangephotos=Fotos neu angeordnet
 confirm.cutandmove=Bereich verschoben
+confirm.interpolate=Punkte eingef\u00fcgt
 confirm.convertnamestotimes=Wegpunktnamen umgewandelt
 confirm.saveexif.ok1=Es wurden
 confirm.saveexif.ok2=Fotodateien geschrieben
@@ -506,7 +523,7 @@ confirm.correlateaudios.multi=Audios wurden korreliert
 # Buttons
 button.ok=OK
 button.back=Zur\u00fcck
-button.next=Vorw\u00e4rts
+button.next=Weiter
 button.finish=Fertig
 button.cancel=Abbrechen
 button.overwrite=\u00dcberschreiben
@@ -537,15 +554,15 @@ button.delete=Entfernen
 button.manage=Verwalten
 
 # File types
-filetype.txt=TXT Dateien
-filetype.jpeg=JPG Dateien
-filetype.kmlkmz=KML, KMZ Dateien
-filetype.kml=KML Dateien
-filetype.kmz=KMZ Dateien
-filetype.gpx=GPX Dateien
-filetype.pov=POV Dateien
-filetype.svg=SVG Dateien
-filetype.audio=MP3, OGG, WAV Dateien
+filetype.txt=TXT-Dateien
+filetype.jpeg=JPG-Dateien
+filetype.kmlkmz=KML-, KMZ-Dateien
+filetype.kml=KML-Dateien
+filetype.kmz=KMZ-Dateien
+filetype.gpx=GPX-Dateien
+filetype.pov=POV-Dateien
+filetype.svg=SVG-Dateien
+filetype.audio=MP3-, OGG-, WAV-Dateien
 
 # Display components
 display.nodata=Keine Daten geladen
@@ -559,7 +576,7 @@ details.track.numfiles=Anzahl Dateien
 details.pointdetails=Details des Punkts
 details.index.selected=Index
 details.index.of=von
-details.nopointselection=Nichts selektiert
+details.nopointselection=Nichts ausgew\u00e4hlt
 details.photofile=Fotodatei
 details.norangeselection=Kein Bereich ausgew\u00e4hlt
 details.rangedetails=Details der Auswahl
@@ -575,7 +592,6 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=T
 details.range.avespeed=Durchschnittsgeschwindigkeit
-details.range.avemovingspeed=Durchschnittsgeschwindigkeit gleitend
 details.range.maxspeed=H\u00f6chstgeschwindigkeit
 details.range.numsegments=Anzahl Abschnitte
 details.range.pace=Tempo
@@ -583,11 +599,12 @@ details.range.gradient=Gef\u00e4lle
 details.lists.waypoints=Wegpunkte
 details.lists.photos=Fotos
 details.lists.audio=Audio
-details.photodetails=Fotodetails
+details.photodetails=Details des Fotos
 details.nophoto=Kein Foto ausgew\u00e4hlt
 details.photo.loading=Laden
 details.photo.bearing=Richtung
 details.media.connected=Verbunden
+details.media.fullpath=Ganzer Pfad
 details.audiodetails=Audiodetails
 details.noaudio=Keine Audiodatei ausgew\u00e4hlt
 details.audio.file=Audiodatei
@@ -616,11 +633,15 @@ fieldname.description=Beschreibung
 units.original=Original
 units.default=Standard
 units.metres=Meter
+units.metres.short=m
 units.kilometres=Kilometer
 units.kilometres.short=km
-units.kmh=km/h
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.kilometresperhour.short=km/h
+units.nauticalmiles=Seemeilen
+units.nauticalmiles.short=sm
+units.nauticalmilesperhour.short=kn
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=Std
 units.degminsec=Grad-Min-Sek
 units.degmin=Grad-Min
@@ -646,18 +667,19 @@ undo.deletepoint=Punkt l\u00f6schen
 undo.removephoto=Foto entfernen
 undo.removeaudio=Audiodatei entfernen
 undo.deleterange=Bereich l\u00f6schen
-undo.compress=Track komprimieren
+undo.croptrack=Track zuschneiden
+undo.deletemarked=Punkte l\u00f6schen
 undo.insert=Punkte hinzuf\u00fcgen
 undo.reverse=Bereich umdrehen
 undo.mergetracksegments=Trackabschnitte verbinden
 undo.addtimeoffset=Zeitverschiebung aufrechnen
 undo.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
-undo.rearrangewaypoints=Wegpunkte reorganisieren
+undo.rearrangewaypoints=Wegpunkte neu anordnen
 undo.cutandmove=Bereich verschieben
 undo.connect=verbinden
 undo.disconnect=trennen
 undo.correlatephotos=Fotos korrelieren
-undo.rearrangephotos=Fotos reorganisieren
+undo.rearrangephotos=Fotos neu anordnen
 undo.createpoint=Punkt erzeugen
 undo.rotatephoto=Foto umdrehen
 undo.convertnamestotimes=Namen in Zeitstempel umwandeln
@@ -669,8 +691,8 @@ undo.correlateaudios=Audios korrelieren
 error.save.dialogtitle=Fehler beim Speichern
 error.save.nodata=Keine Daten zum Speichern vorhanden
 error.save.failed=Speichern von Daten in Datei fehlgeschlagen
-error.saveexif.filenotfound=Foto Datei nicht gefunden
-error.saveexif.cannotoverwrite1=Foto Datei
+error.saveexif.filenotfound=Bilddatei nicht gefunden
+error.saveexif.cannotoverwrite1=Bilddatei
 error.saveexif.cannotoverwrite2=ist schreibgesch\u00fctzt. Als Kopie speichern?
 error.saveexif.failed1=
 error.saveexif.failed2=Bilder konnten nicht gespeichert werden
@@ -679,24 +701,24 @@ error.saveexif.forced2=der Bilder musste das Speichern erzwungen werden
 error.load.dialogtitle=Fehler beim Laden
 error.load.noread=Datei konnte nicht gelesen werden
 error.load.nopoints=Keine g\u00fcltigen Daten in Datei gefunden
-error.load.unknownxml=Unbekanntes xml Format:
-error.load.noxmlinzip=Keine xml Datei in Zip Datei gefunden
+error.load.unknownxml=Unbekanntes XML-Format:
+error.load.noxmlinzip=Keine XML-Datei in Zip-Datei gefunden
 error.load.othererror=Fehler beim Lesen der Datei:
 error.jpegload.dialogtitle=Fehler beim Laden von Fotos
 error.jpegload.nofilesfound=Keine Dateien gefunden
-error.jpegload.nojpegsfound=Keine Jpeg Dateien gefunden
-error.jpegload.nogpsfound=Keine GPS Information gefunden
-error.jpegload.exifreadfailed=EXIF Aufruf fehlgeschlagen. Keine EXIF Information k\u00f6nnen gelesen werden\nohne einen internen oder externen Bibliothek.
+error.jpegload.nojpegsfound=Keine JPG-Dateien gefunden
+error.jpegload.nogpsfound=Keine GPS-Information gefunden
+error.jpegload.exifreadfailed=Exif-Aufruf fehlgeschlagen. Exif-Information k\u00f6nnen nicht gelesen werden\nwenn nicht eine interne oder externe Bibliothek vorhanden ist.
 error.audioload.nofilesfound=Keine Audiodateien gefunden
 error.gpsload.unknown=Unbekannter Fehler
 error.undofailed.title=Undo fehlgeschlagen
 error.undofailed.text=Operation konnte nicht r\u00fcckg\u00e4ngig gemacht werden
 error.function.noop.title=Funktion hat nichts bewirkt
-error.rearrange.noop=Die Punkte zu reorganisieren hatte keinen Effekt
+error.rearrange.noop=Die Neuanordnung der Punkte hatte keinen Effekt
 error.function.notavailable.title=Funktion nicht verf\u00fcgbar
-error.function.nojava3d=Diese Funktion ben\u00f6tigt die Java3d Library,\nvon Sun.com erh\u00e4ltlich.
+error.function.nojava3d=Diese Funktion ben\u00f6tigt die Java3d-Library,\ndie bei Sun.com erh\u00e4ltlich ist.
 error.3d=Ein Fehler ist bei der 3D Darstellung aufgetreten
-error.readme.notfound=Liesmich Datei nicht gefunden
+error.readme.notfound=Liesmich-Datei nicht gefunden
 error.osmimage.dialogtitle=Laden von Karten-Bildern fehlgeschlagen
 error.osmimage.failed=Laden von Karten-Bildern fehlgeschlagen. Bitte pr\u00fcfen Sie die Internetverbindung.
 error.language.wrongfile=Die ausgew\u00e4hlte Datei scheint keine Sprachdatei f\u00fcr GpsPrune zu sein
@@ -705,7 +727,9 @@ error.lookupsrtm.nonefound=Keine H\u00f6hendaten verf\u00fcgbar f\u00fcr diese P
 error.lookupsrtm.nonerequired=Alle Punkte haben schon H\u00f6hendaten
 error.gpsies.uploadnotok=Der Gpsies Server hat geantwortet
 error.gpsies.uploadfailed=Das Hochladen ist fehlgeschlagen
+error.showphoto.failed=Das Foto konnte nicht geladen werden
 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
index 38f0538b5599cc58b37b35a6ec830946fbcfc466..caa5bd6a17faadb83a67042c2f89dbdaa999929a 100644 (file)
@@ -3,37 +3,37 @@
 
 # Menu entries
 menu.file=File
-menu.file.addphotos=Fötelis innätue
+menu.file.addphotos=F\u00f6telis inn\u00e4tue
 menu.file.recentfiles=Letzschti aagluegte Files
 menu.file.save=Als Text Speichere
-menu.file.exit=Beände
+menu.file.exit=Be\u00e4nde
 menu.track=Track
 menu.track.undo=Undo
-menu.track.clearundo=Undo-Liste lösche
-menu.track.deletemarked=Komprimierte Punkte lösche
+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 Ã„nde
-menu.track.rearrange.nearest=Jede zum nöchsti Trackpunkt
+menu.track.rearrange.nearest=Jede zum n\u00f6chsti Trackpunkt
 menu.range=Beriich
 menu.range.all=Alles selektiere
-menu.range.none=Nüüt selektiere
-menu.range.start=Start setzä
-menu.range.end=Stopp setzä
-menu.range.deleterange=Beriich lösche
-menu.range.interpolate=Interpoliere
-menu.range.average=Durchschnitt uusrächne
-menu.range.reverse=Beriich umdrähie
-menu.range.mergetracksegments=Track Segmänte merge
+menu.range.none=N\u00fc\u00fct selektiere
+menu.range.start=Start setz\u00e4
+menu.range.end=Stopp setz\u00e4
+function.interpolate=P\u00fcnkte interpoliere
+menu.range.average=Durchschnitt uusr\u00e4chne
+menu.range.reverse=Beriich umdr\u00e4hie
+menu.range.mergetracksegments=Track Segm\u00e4nte merge
 menu.range.cutandmove=Schniide und move
 menu.point=Punkt
 menu.point.editpoint=Punkt editiere
-menu.point.deletepoint=Punkt lösche
-menu.photo=Föteli
-menu.photo.saveexif=Exif Date speicherä
-function.connecttopoint=Mitem Punkt verbindä
-function.disconnectfrompoint=Vonem Punkt trännä
-function.removephoto=Föteli entfernä
+menu.point.deletepoint=Punkt l\u00f6sche
+menu.photo=F\u00f6teli
+menu.photo.saveexif=Exif Date speicher\u00e4
+function.connecttopoint=Mitem Punkt verbind\u00e4
+function.disconnectfrompoint=Vonem Punkt tr\u00e4nn\u00e4
+function.removephoto=F\u00f6teli entfern\u00e4
 menu.audio=Audio
 menu.view=Aasicht
 menu.view.showsidebars=Seiteleischten aazeige
@@ -47,11 +47,12 @@ menu.map.zoomin=Innezoome
 menu.map.zoomout=Uusezoome
 menu.map.zoomfull=Zoome zum ganzes Bild
 menu.map.newpoint=Noii Punkt
-menu.map.drawpoints=Noii Punkte uufzeichnä
-menu.map.connect=Trackpünktli verbindä
+menu.map.drawpoints=Noii P\u00fcnktli uufzeichn\u00e4
+menu.map.connect=Trackp\u00fcnktli verbind\u00e4
 menu.map.autopan=Autopan
-menu.map.showmap=Karte zeigä
+menu.map.showmap=Karte zeig\u00e4
 menu.map.showscalebar=Massstab aazeige
+menu.map.editmode=P\u00fcnkte verschiebe
 
 # Alt keys for menus
 altkey.menu.file=F
@@ -59,7 +60,7 @@ altkey.menu.track=T
 altkey.menu.range=B
 altkey.menu.point=P
 altkey.menu.view=A
-altkey.menu.photo=F
+altkey.menu.photo=L
 altkey.menu.audio=U
 altkey.menu.settings=I
 altkey.menu.help=H
@@ -74,50 +75,52 @@ shortcut.menu.range.all=A
 shortcut.menu.help.help=H
 
 # Functions
-function.open=File Ã¶ffne
+function.open=File \u00f6ffne
 function.importwithgpsbabel=mit GPSBabel importiere
 function.loadfromgps=uusem GPS lade
 function.sendtogps=zum GPS schicke
-function.exportkml=KML exportierä
-function.exportgpx=GPX exportierä
-function.exportpov=POV exportierä
-function.exportsvg=SVG exportierä
+function.exportkml=KML exportier\u00e4
+function.exportgpx=GPX exportier\u00e4
+function.exportpov=POV exportier\u00e4
+function.exportsvg=SVG exportier\u00e4
 function.editwaypointname=Waypoint Name editiere
-function.compress=Track komprimierä
+function.compress=Track komprimier\u00e4
+function.deleterange=Beriich l\u00f6sche
+function.croptrack=Track zuschniide
 function.addtimeoffset=Ziitverschiebig zutue
-function.addaltitudeoffset=Höchiverschiebig zutue
-function.findwaypoint=Waypoint suechä
-function.convertnamestotimes=Waypointname ins Ziitstämple verwondle
-function.deletefieldvalues=Werte von nem Fäld lösche
+function.addaltitudeoffset=H\u00f6chiverschiebig zutue
+function.findwaypoint=Waypoint suech\u00e4
+function.convertnamestotimes=Waypointname ins Ziitst\u00e4mple verwondle
+function.deletefieldvalues=Werte von nem F\u00e4ld l\u00f6sche
 function.pastecoordinates=Noii Koordinaten iigebe
 function.charts=Diagramme
-function.show3d=Drüü-D Aasicht
-function.distances=Entfärnige
-function.fullrangedetails=Zuesätzlichi Beriichinfos
-function.setmapbg=Karte Hintegrund setzä
-function.getgpsies=Gpsies Tracks holä
-function.uploadgpsies=Date zum Gpsies uufaladä
-function.lookupsrtm=Höhendate vonem SRTM hole
-function.getwikipedia=Im Wikipedia in dr Nöchi naaluege
+function.show3d=Dr\u00fc\u00fc-D Aasicht
+function.distances=Entf\u00e4rnige
+function.fullrangedetails=Zues\u00e4tzlichi Beriichinfos
+function.setmapbg=Karte Hintegrund setz\u00e4
+function.getgpsies=Gpsies Tracks hol\u00e4
+function.uploadgpsies=Date zum Gpsies uufalad\u00e4
+function.lookupsrtm=H\u00f6hendate vonem SRTM hole
+function.getwikipedia=Im Wikipedia in dr N\u00f6chi naaluege
 function.searchwikipedianames=Wikipedia mit Name durasueche
-function.downloadosm=OSM-Date für dere Gebiet abaladä
-function.duplicatepoint=Punkt verdopplä
-function.correlatephotos=Fötelis korrelierä
-function.rearrangephotos=Fötelis reorganisierä
-function.rotatephotoleft=Föteli nach Links dräyä
-function.rotatephotoright=Föteli nach Rächts dräyä
-function.photopopup=Fötelifänschter aazeigä
-function.ignoreexifthumb=Exif Vorschaubildli ignorierä
+function.downloadosm=OSM-Date f\u00fcr dere Gebiet abalad\u00e4
+function.duplicatepoint=Punkt verdoppl\u00e4
+function.correlatephotos=F\u00f6telis korrelier\u00e4
+function.rearrangephotos=F\u00f6telis reorganisier\u00e4
+function.rotatephotoleft=F\u00f6teli nach Links dr\u00e4y\u00e4
+function.rotatephotoright=F\u00f6teli nach R\u00e4chts dr\u00e4y\u00e4
+function.photopopup=F\u00f6telif\u00e4nschter aazeig\u00e4
+function.ignoreexifthumb=Exif Vorschaubildli ignorier\u00e4
 function.loadaudio=Audiofiles lade
-function.removeaudio=Audiodatei entfernä
-function.correlateaudios=Audios korrelierä
-function.playaudio=Audiofile abspielä
-function.stopaudio=Abspielen abbrächä
-function.setkmzimagesize=Bildligrösse inem KMZ setzä
-function.setpaths=Programmepfade setzä
-function.setcolours=Farben setzä
-function.setlinewidth=Liniedicke setzä
-function.setlanguage=Sproch setzä
+function.removeaudio=Audiodatei entfern\u00e4
+function.correlateaudios=Audios korrelier\u00e4
+function.playaudio=Audiofile abspiel\u00e4
+function.stopaudio=Abspielen abbr\u00e4ch\u00e4
+function.setkmzimagesize=Bildligr\u00f6sse inem KMZ setz\u00e4
+function.setpaths=Programmepfade setz\u00e4
+function.setcolours=Farben setz\u00e4
+function.setlinewidth=Liniedicke setz\u00e4
+function.setlanguage=Sproch setz\u00e4
 function.help=Hilfe
 function.showkeys=Tastekombinatione aazeige
 function.about=Ãœber GpsPrune
@@ -127,164 +130,167 @@ function.diskcache=Karten uufem Disk speichere
 function.managetilecache=Kartebildli verwolte
 
 # Dialogs
-dialog.exit.confirm.title=GpsPrune beände
-dialog.exit.confirm.text=Ihri Date sind nonig gspeicheret worde. Wend Sie trotzdem s Programm beände?
-dialog.openappend.title=Date aahänge oder ersätze
-dialog.openappend.text=Häng diese Date zur aktuelli Daten aa?
-dialog.deletepoint.title=Punkt löschä
-dialog.deletepoint.deletephoto=s Föteli vonem Punkt au löschä?
-dialog.deletephoto.title=Föteli entfernä
-dialog.deletephoto.deletepoint=Punkt vonem Föteli au löschä?
+dialog.exit.confirm.title=GpsPrune be\u00e4nde
+dialog.exit.confirm.text=Ihri Date sind nonig gspeicheret worde. Wend Sie trotzdem s Programm be\u00e4nde?
+dialog.openappend.title=Date aah\u00e4nge oder ers\u00e4tze
+dialog.openappend.text=H\u00e4ng diese Date zur aktuelli Daten aa?
+dialog.deletepoint.title=Punkt l\u00f6sch\u00e4
+dialog.deletepoint.deletephoto=s F\u00f6teli vonem Punkt au l\u00f6sch\u00e4?
+dialog.deletephoto.title=F\u00f6teli entfern\u00e4
+dialog.deletephoto.deletepoint=Punkt vonem F\u00f6teli au l\u00f6sch\u00e4?
+dialog.deleteaudio.deletepoint=Punkt vonem Audio au l\u00f6sch\u00e4?
 dialog.openoptions.title=Öffne Optionen
 dialog.openoptions.filesnippet=Extrakt vom File
-dialog.load.table.field=Fäld
+dialog.load.table.field=F\u00e4ld
 dialog.load.table.datatype=Date Typ
 dialog.load.table.description=Beschriibig
-dialog.delimiter.label=Fäld Trennzeiche
+dialog.delimiter.label=F\u00e4ld Trennzeiche
 dialog.delimiter.comma=Komma ,
 dialog.delimiter.tab=Tab
 dialog.delimiter.space=Abstand
 dialog.delimiter.semicolon=Strichpunkt ;
 dialog.delimiter.other=Andere
 dialog.openoptions.deliminfo.records=Rekords, mit
-dialog.openoptions.deliminfo.fields=Fäldere
+dialog.openoptions.deliminfo.fields=F\u00e4ldere
 dialog.openoptions.deliminfo.norecords=Kei Rekords
-dialog.openoptions.altitudeunits=Höchi Masseiheite
-dialog.open.contentsdoubled=Dieses File hät zwei Kopien von jädem Punkt,\neimol als Waypoint und eimol als Trackpunkt.
-dialog.selecttracks.intro=Wählet Sie die Tracks uus zum ladä
+dialog.openoptions.altitudeunits=H\u00f6chi Masseiheite
+dialog.open.contentsdoubled=Dieses File h\u00e4t zwei Kopien von j\u00e4dem Punkt,\neimol als Waypoint und eimol als Trackpunkt.
+dialog.selecttracks.intro=W\u00e4hlet Sie die Tracks uus zum lad\u00e4
 dialog.selecttracks.noname=Unbenannt
 dialog.jpegload.subdirectories=Subordnern au
-dialog.jpegload.loadjpegswithoutcoords=Au Fötelis ohni Koordinate
-dialog.jpegload.loadjpegsoutsidearea=Au Fötelis uuserhalb vonem Track
-dialog.jpegload.progress.title=Fötelis lade
-dialog.jpegload.progress=Bitte warte während die Fötelis durägsucht werde
+dialog.jpegload.loadjpegswithoutcoords=Au F\u00f6telis ohni Koordinate
+dialog.jpegload.loadjpegsoutsidearea=Au F\u00f6telis uuserhalb vonem Track
+dialog.jpegload.progress.title=F\u00f6telis lade
+dialog.jpegload.progress=Bitte warte w\u00e4hrend die F\u00f6telis dur\u00e4gsucht werde
 dialog.gpsload.nogpsbabel=Kei gpsbabel Programm gfunde. Wiiter?
 dialog.gpsload.device=Device Name
 dialog.gpsload.format=Format
 dialog.gpsload.getwaypoints=Waypoints lade
 dialog.gpsload.gettracks=Tracks lade
-dialog.gpsload.save=nach nem File speicherä
+dialog.gpsload.save=nach nem File speicher\u00e4
 dialog.gpssend.sendwaypoints=Waypoints schicke
 dialog.gpssend.sendtracks=Tracks schicke
 dialog.gpssend.trackname=Track Name
-dialog.saveoptions.title=File speicherä
-dialog.save.fieldstosave=Fälder zu speicherä
-dialog.save.table.field=Fäld
+dialog.saveoptions.title=File speicher\u00e4
+dialog.save.fieldstosave=F\u00e4lder zu speicher\u00e4
+dialog.save.table.field=F\u00e4ld
 dialog.save.table.hasdata=Het Date
-dialog.save.table.save=Speicherä
-dialog.save.headerrow=Titel Ziile speicherä
+dialog.save.table.save=Speicher\u00e4
+dialog.save.headerrow=Titel Ziile speicher\u00e4
 dialog.save.coordinateunits=Koordinate Massiiheite
-dialog.save.altitudeunits=Höchi Massiiheite
-dialog.save.timestampformat=Ziitstämpelformat
+dialog.save.altitudeunits=H\u00f6chi Massiiheite
+dialog.save.timestampformat=Ziitst\u00e4mpelformat
 dialog.save.overwrite.title=s'File existiert scho
-dialog.save.overwrite.text=s'File existiert scho. Sind Sie sicher, Sie wend s'File Ã¼berschriibe?
-dialog.save.notypesselected=Kei Punktetype sin uusgewählt worde
-dialog.exportkml.text=Titel für die Date
-dialog.exportkml.altitude=Absolut Höchiinformation (fürs Fliege)
-dialog.exportkml.kmz=Date ins kmz File komprimierä
-dialog.exportkml.exportimages=Bildli ins Kmz exportierä
+dialog.save.overwrite.text=s'File existiert scho. Sind Sie sicher, Sie wend s'File \u00fcberschriibe?
+dialog.save.notypesselected=Kei Punktetype sin uusgew\u00e4hlt worde
+dialog.exportkml.text=Titel f\u00fcr die Date
+dialog.exportkml.altitude=Absolut H\u00f6chiinformation (f\u00fcrs Fliege)
+dialog.exportkml.kmz=Date ins kmz File komprimier\u00e4
+dialog.exportkml.exportimages=Bildli ins Kmz exportier\u00e4
 dialog.exportkml.trackcolour=Trackfarb
 dialog.exportgpx.name=Name
 dialog.exportgpx.desc=Beschriibig
-dialog.exportgpx.includetimestamps=Au Ziitstämpel
-dialog.exportgpx.copysource=Xml-Quälle kopierä
+dialog.exportgpx.includetimestamps=Au Ziitst\u00e4mpel
+dialog.exportgpx.copysource=Xml-Qu\u00e4lle kopier\u00e4
 dialog.exportgpx.encoding=Enkodierig
 dialog.exportgpx.encoding.system=System
 dialog.exportgpx.encoding.utf8=UTF-8
-dialog.exportpov.text=Gäbet Sie die Parameter ii fürs POV Export
+dialog.exportpov.text=G\u00e4bet Sie die Parameter ii f\u00fcrs POV Export
 dialog.exportpov.font=Font
 dialog.exportpov.camerax=Kamera X
 dialog.exportpov.cameray=Kamera Y
 dialog.exportpov.cameraz=Kamera Z
 dialog.exportpov.modelstyle=Modellstil
-dialog.exportpov.ballsandsticks=Bälle und Schtange
-dialog.exportpov.tubesandwalls=Röhre und Wände
-dialog.exportpov.warningtracksize=Dieser Track hät mega viele Punkte, die Java3D villiicht nöd chann bearbeite.\nSind Sie sicher, Sie wend trotzdem fortsetze?
-dialog.exportsvg.text=Wählet Sie die Parameter fürs SVG Export uus
+dialog.exportpov.ballsandsticks=B\u00e4lle und Schtange
+dialog.exportpov.tubesandwalls=R\u00f6hre und W\u00e4nde
+dialog.exportpov.warningtracksize=Dieser Track h\u00e4t mega viele P\u00fcnkte, die Java3D villiicht n\u00f6d chann bearbeite.\nSind Sie sicher, Sie wend trotzdem fortsetze?
+dialog.exportsvg.text=W\u00e4hlet Sie die Parameter f\u00fcrs SVG Export uus
 dialog.exportsvg.phi=Richtigswinkel \u03D5
 dialog.exportsvg.theta=Neigigswinkel \u03B8
-dialog.exportsvg.gradients=Farbeverläufe verwände
+dialog.exportsvg.gradients=Farbeverl\u00e4ufe verw\u00e4nde
 dialog.pointtype.desc=Folgende Punkttype speichere:
-dialog.pointtype.track=Trackpunkte
+dialog.pointtype.track=Trackp\u00fcnkte
 dialog.pointtype.waypoint=Waypoints
-dialog.pointtype.photo=Fötelipunkte
-dialog.pointtype.audio=Audiopunkte
+dialog.pointtype.photo=F\u00f6telip\u00fcnkte
+dialog.pointtype.audio=Audiop\u00fcnkte
 dialog.pointtype.selection=Nur aktuelli Beriich
-dialog.confirmreversetrack.title=Umdrehig bestätige
-dialog.confirmreversetrack.text=Diese Daten enthalte Ziitstämpel Informatione, die bei dr Umkehrig usser Reihefolge erschiene würdi.\nSind Sie sicher, Sie wend dn Beriich umkehre?
-dialog.confirmcutandmove.title=Move bestätige
-dialog.confirmcutandmove.text=Diese Daten enthalte Ziitstämpel Informatione, die bei dr Move usser Reihefolge erschiene würdi.\nSind Sie sicher, Sie wend dn Beriich move?
-dialog.interpolate.title=Punkte interpoliere
-dialog.interpolate.parameter.text=Aazahl Punkte zum innätue zwüschet den selektierten Punkten
+dialog.confirmreversetrack.title=Umdrehig best\u00e4tige
+dialog.confirmreversetrack.text=Diese Daten enthalte Ziitst\u00e4mpel Informatione, die bei dr Umkehrig usser Reihefolge erschiene w\u00fcrdi.\nSind Sie sicher, Sie wend dn Beriich umkehre?
+dialog.confirmcutandmove.title=Move best\u00e4tige
+dialog.confirmcutandmove.text=Diese Daten enthalte Ziitst\u00e4mpel Informatione, die bei dr Move usser Reihefolge erschiene w\u00fcrdi.\nSind Sie sicher, Sie wend dn Beriich move?
+dialog.interpolate.parameter.text=Aazahl P\u00fcnkte zum inn\u00e4tue zw\u00fcschet den selektierten P\u00fcnkten
+dialog.interpolate.betweenwaypoints=Zw\u00fcschet d Waypoints interpoliere?
 dialog.undo.title=Undo Operation(e)
-dialog.undo.pretext=Selektiere die Operatione die rückgängig gmacht söllti werde.
-dialog.undo.none.title=Undo nöd möglich
-dialog.undo.none.text=Keini Operatione könne rückgängig gmacht werde.
-dialog.clearundo.title=Undo-Liste löschä
-dialog.clearundo.text=Sind Sie sicher, Sie wend die Undo-Liste lösche?\nAlle Undo Infos werdet verlore gah!
-dialog.pointedit.title=Punkt editierä
-dialog.pointedit.text=Wählet Sie jäden Fäld uus zu editiere, und mitem 'Editierä' Chnopf den Wert Ã¤ndere
-dialog.pointedit.table.field=Fäld
+dialog.undo.pretext=Selektiere die Operatione die r\u00fcckg\u00e4ngig gmacht s\u00f6llti werde.
+dialog.undo.none.title=Undo n\u00f6d m\u00f6glich
+dialog.undo.none.text=Keini Operatione k\u00f6nne r\u00fcckg\u00e4ngig gmacht werde.
+dialog.clearundo.title=Undo-Liste l\u00f6sch\u00e4
+dialog.clearundo.text=Sind Sie sicher, Sie wend die Undo-Liste l\u00f6sche?\nAlle Undo Infos werdet verlore gah!
+dialog.pointedit.title=Punkt editier\u00e4
+dialog.pointedit.text=W\u00e4hlet Sie j\u00e4den F\u00e4ld uus zu editiere, und mitem 'Editier\u00e4' Chnopf den Wert \u00e4ndere
+dialog.pointedit.table.field=F\u00e4ld
 dialog.pointedit.table.value=Wert
-dialog.pointedit.table.changed=Geändert
-dialog.pointedit.changevalue.text=Gebet Sie den neuen Wert für diesen Fäld ina
-dialog.pointedit.changevalue.title=Fäld editiere
+dialog.pointedit.table.changed=Ge\u00e4ndert
+dialog.pointedit.changevalue.text=Gebet Sie den neuen Wert f\u00fcr diesen F\u00e4ld ina
+dialog.pointedit.changevalue.title=F\u00e4ld editiere
 dialog.pointnameedit.name=Waypoint Name
 dialog.pointnameedit.uppercase=GROSS gschriebe
 dialog.pointnameedit.lowercase=chli gschriebe
-dialog.pointnameedit.sentencecase=Gmischt Gschriebe
+dialog.pointnameedit.titlecase=Gmischt Gschriebe
 dialog.addtimeoffset.add=Ziit zutue
 dialog.addtimeoffset.subtract=Ziit davo neh
 dialog.addtimeoffset.days=Tage
 dialog.addtimeoffset.hours=Schtunde
 dialog.addtimeoffset.minutes=Minute
-dialog.addtimeoffset.notimestamps=Ziitverschiebig nöd möglech wil dr Beriich kei Ziitinfo hät
+dialog.addtimeoffset.notimestamps=Ziitverschiebig n\u00f6d m\u00f6glech wil dr Beriich kei Ziitinfo h\u00e4t
 dialog.findwaypoint.intro=Gebet Sie en Teil vonem Namen ina
 dialog.findwaypoint.search=Sueche
-dialog.saveexif.title=Exif go speicherä
-dialog.saveexif.intro=Wählet Sie die Fötelis uus zum speicherä
-dialog.saveexif.nothingtosave=Koordinaten sin nöd geänderet, nüüt zum speicherä
+dialog.saveexif.title=Exif go speicher\u00e4
+dialog.saveexif.intro=W\u00e4hlet Sie die F\u00f6telis uus zum speicher\u00e4
+dialog.saveexif.nothingtosave=Koordinaten sin n\u00f6d ge\u00e4nderet, n\u00fc\u00fct zum speicher\u00e4
 dialog.saveexif.noexiftool=Kei exiftool Programm gfunde. Wiiter?
-dialog.saveexif.table.photoname=Föteli Name
+dialog.saveexif.table.photoname=F\u00f6teli Name
 dialog.saveexif.table.status=Status
-dialog.saveexif.table.save=Speicherä
-dialog.saveexif.photostatus.connected=Verbundä
-dialog.saveexif.photostatus.disconnected=Gtrännt
-dialog.saveexif.photostatus.modified=Gänderet
-dialog.saveexif.overwrite=Files Ã¼berschriebä
-dialog.saveexif.force=Forzierä trotz Warnige
+dialog.saveexif.table.save=Speicher\u00e4
+dialog.saveexif.photostatus.connected=Verbund\u00e4
+dialog.saveexif.photostatus.disconnected=Gtr\u00e4nnt
+dialog.saveexif.photostatus.modified=G\u00e4nderet
+dialog.saveexif.overwrite=Files \u00fcberschrieb\u00e4
+dialog.saveexif.force=Forzier\u00e4 trotz Warnige
 dialog.charts.xaxis=X Achse
 dialog.charts.yaxis=Y Achse
 dialog.charts.output=Uusgabe
 dialog.charts.screen=Bildschirm
 dialog.charts.svg=SVG File
 dialog.charts.svgwidth=SVG Breiti
-dialog.charts.svgheight=SVG Höhi
-dialog.charts.needaltitudeortimes=Ohni Höhi Date und au ohne Ziit, isch es nöd möglech, Diagramme z zeigä.
-dialog.charts.gnuplotnotfound=Gnuplot isch mit dem Pfad nöd gfunde worde
-dialog.distances.intro=Entfärnige per Luftlinie zwüschet Punkte
+dialog.charts.svgheight=SVG H\u00f6hi
+dialog.charts.needaltitudeortimes=Ohni H\u00f6hi Date und au ohne Ziit, isch es n\u00f6d m\u00f6glech, Diagramme z zeig\u00e4.
+dialog.charts.gnuplotnotfound=Gnuplot isch mit dem Pfad n\u00f6d gfunde worde
+dialog.distances.intro=Entf\u00e4rnige per Luftlinie zw\u00fcschet P\u00fcnkte
 dialog.distances.column.from=Vom Punkt
 dialog.distances.column.to=Zum Punkt
 dialog.distances.currentpoint=Aktuelli Punkt
-dialog.distances.toofewpoints=d'Funktion bruucht Waypoints um die Dischtanze z berächne
+dialog.distances.toofewpoints=d'Funktion bruucht Waypoints um die Dischtanze z ber\u00e4chne
 dialog.fullrangedetails.intro=Hier sind die Infos vonem aktuelli Beriich
-dialog.setmapbg.intro=Eini von den Quällen uuswähle, oder eini neui hinzuefüge
-dialog.addmapsource.title=Neui Kartequälle hinzuefüge
+dialog.fullrangedetails.coltotal=Inklusiv L\u00fccke
+dialog.fullrangedetails.colsegments=Ohni L\u00fccke
+dialog.setmapbg.intro=Eini von den Qu\u00e4llen uusw\u00e4hle, oder eini neui hinzuef\u00fcge
+dialog.addmapsource.title=Neui Kartequ\u00e4lle hinzuef\u00fcge
 dialog.addmapsource.sourcename=Sourcename
-dialog.addmapsource.layer1url=URL für erschti Ebene
-dialog.addmapsource.layer2url=URL für oberi Ebene (falls nötig)
+dialog.addmapsource.layer1url=URL f\u00fcr erschti Ebene
+dialog.addmapsource.layer2url=URL f\u00fcr oberi Ebene (falls n\u00f6tig)
 dialog.addmapsource.maxzoom=Maximali Zoom
 dialog.addmapsource.cloudstyle=Stilnummere
 dialog.addmapsource.noname=Unbenannt
 dialog.gpsies.column.name=Track Name
-dialog.gpsies.column.length=Länge
+dialog.gpsies.column.length=L\u00e4nge
 dialog.gpsies.description=Beschriebig
 dialog.gpsies.nodescription=Kei Beschriebig
 dialog.gpsies.nonefound=Kei Tracks gfunde
 dialog.gpsies.username=Gpsies Username
 dialog.gpsies.password=Gpsies Passwort
 dialog.gpsies.keepprivate=Track privat halte
-dialog.gpsies.confirmopenpage=Websiite fürn uufageladenen Track Ã¶ffne?
+dialog.gpsies.confirmopenpage=Websiite f\u00fcrn uufageladenen Track \u00f6ffne?
 dialog.gpsies.activities=Aktivit\u00e4ten
 dialog.gpsies.activity.trekking=Wandere
 dialog.gpsies.activity.walking=Z'Fuess gah
@@ -295,68 +301,72 @@ dialog.gpsies.activity.snowshoe=Schneeschuh
 dialog.gpsies.activity.sailing=Segle
 dialog.gpsies.activity.skating=Inline-Skate
 dialog.wikipedia.column.name=Artikelname
-dialog.wikipedia.column.distance=Entfärnig
-dialog.correlate.notimestamps=Es hät kei Ziitstämpel inem Track innä, so s'isch nöd möglech die Fötelis zu korrelierä.
-dialog.correlate.nouncorrelatedphotos=Alle Fötelis sin scho korreliert.\nWend Sie trotzdem fortsetzä?
-dialog.correlate.photoselect.intro=Wählet Sie eini vo deren Föteli uus, um die Ziitdifferänz zu berächnä
-dialog.correlate.select.photoname=Föteli Name
-dialog.correlate.select.timediff=Ziitdifferänz
-dialog.correlate.select.photolater=Föteli spöter
-dialog.correlate.options.tip=Tipp: Mit mindeschtens einem verbundenen Elemänt kann die Ziitdifferänz automatisch berächnet werdä.
-dialog.correlate.options.intro=Wählet Sie die Optione uus für die Korrelierig
+dialog.wikipedia.column.distance=Entf\u00e4rnig
+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?
+dialog.correlate.photoselect.intro=W\u00e4hlet Sie eini vo deren F\u00f6teli uus, um die Ziitdiffer\u00e4nz zu ber\u00e4chn\u00e4
+dialog.correlate.select.photoname=F\u00f6teli Name
+dialog.correlate.select.timediff=Ziitdiffer\u00e4nz
+dialog.correlate.select.photolater=F\u00f6teli sp\u00f6ter
+dialog.correlate.options.tip=Tipp: Mit mindeschtens einem verbundenen Elem\u00e4nt kann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
+dialog.correlate.options.intro=W\u00e4hlet Sie die Optione uus f\u00fcr die Korrelierig
 dialog.correlate.options.offsetpanel=Ziitunterschied
 dialog.correlate.options.offset=Unterschied
-dialog.correlate.options.offset.hours=Schtundä,
-dialog.correlate.options.offset.minutes=Minutä und
-dialog.correlate.options.offset.seconds=Sekundä
-dialog.correlate.options.photolater=Föteli spöter alsem Punkt
-dialog.correlate.options.pointlaterphoto=Punkt spöter alsem Föteli
-dialog.correlate.options.audiolater=Audio spöter alsem Punkt
-dialog.correlate.options.pointlateraudio=Punkt spöter alsem Audio
-dialog.correlate.options.limitspanel=Korrelation Gränzä
-dialog.correlate.options.notimelimit=Kei Ziitgränzä
-dialog.correlate.options.timelimit=Ziitgränzä
-dialog.correlate.options.nodistancelimit=Kei Distanzgränzä
-dialog.correlate.options.distancelimit=Distanzgränzä
-dialog.correlate.options.correlate=Korrelierä
-dialog.correlate.alloutsiderange=Alli Fötelis sin uusserhalb vonem Track Ziitruum, so chönne nöd korreliert werdä.\nVersuechet Sie mitenem anderen Offset oder verbindet Sie manuell mindeschtens eis Föteli.
+dialog.correlate.options.offset.hours=Schtund\u00e4,
+dialog.correlate.options.offset.minutes=Minut\u00e4 und
+dialog.correlate.options.offset.seconds=Sekund\u00e4
+dialog.correlate.options.photolater=F\u00f6teli sp\u00f6ter alsem Punkt
+dialog.correlate.options.pointlaterphoto=Punkt sp\u00f6ter alsem F\u00f6teli
+dialog.correlate.options.audiolater=Audio sp\u00f6ter alsem Punkt
+dialog.correlate.options.pointlateraudio=Punkt sp\u00f6ter alsem Audio
+dialog.correlate.options.limitspanel=Korrelation Gr\u00e4nz\u00e4
+dialog.correlate.options.notimelimit=Kei Ziitgr\u00e4nz\u00e4
+dialog.correlate.options.timelimit=Ziitgr\u00e4nz\u00e4
+dialog.correlate.options.nodistancelimit=Kei Distanzgr\u00e4nz\u00e4
+dialog.correlate.options.distancelimit=Distanzgr\u00e4nz\u00e4
+dialog.correlate.options.correlate=Korrelier\u00e4
+dialog.correlate.alloutsiderange=Alli Elem\u00e4nte sin uusserhalb vonem Track Ziitruum, so ch\u00f6nne n\u00f6d korreliert werd\u00e4.\nVersuechet Sie mitenem anderen Offset oder verbindet Sie manuell mindeschtens eis Elem\u00e4nt.
 dialog.correlate.filetimes=Die Datei Zeitstempel zeigen:
 dialog.correlate.filetimes2=der Tonspuren
-dialog.correlate.correltimes=Fürs Korreliere, folgendes verwände:
+dialog.correlate.correltimes=F\u00fcrs Korreliere, folgendes verw\u00e4nde:
 dialog.correlate.timestamp.beginning=Aafang
 dialog.correlate.timestamp.middle=Mitti
 dialog.correlate.timestamp.end=Ände
-dialog.correlate.audioselect.intro=Wählet Sie eini vo deren Audios uus, um die Ziitdifferänz zu berächnä
+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öter
-dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von den Punkten setze
+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 Ã„nde
-dialog.rearrangephotos.nosort=Nöd sortiere
+dialog.rearrangephotos.nosort=N\u00f6d sortiere
 dialog.rearrangephotos.sortbyfilename=per Filename sortiere
 dialog.rearrangephotos.sortbytime=per Ziit sortiere
-dialog.compress.nonefound=Kei Punkte hätte gelöscht werde könne
-dialog.compress.duplicates.title=Duplikate entfärnä
-dialog.compress.closepoints.title=Nöchiglägeni Punkte entfärnä
+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
-dialog.compress.wackypoints.title=Komischi Punkte entfärnä
+dialog.compress.wackypoints.title=Komischi P\u00fcnkte entf\u00e4rn\u00e4
 dialog.compress.wackypoints.paramdesc=Distanz Faktor
-dialog.compress.singletons.title=Singletons entfärnä
+dialog.compress.singletons.title=Singletons entf\u00e4rn\u00e4
 dialog.compress.singletons.paramdesc=Distanz faktor
 dialog.compress.douglaspeucker.title=Douglas-Peucker Komprimierig
 dialog.compress.douglaspeucker.paramdesc=Span Faktor
-dialog.compress.summarylabel=Punkte zu entfärnä
-dialog.pastecoordinates.desc=Gäbet Sie hier die Koordinaten innä
+dialog.compress.summarylabel=P\u00fcnkte zu entf\u00e4rn\u00e4
+dialog.compress.confirm1=Es sin
+dialog.compress.confirm2=P\u00fcnkt markiert.\nMit Track->Markierte P\u00fcnkte l\u00f6sche werdet sie gl\u00f6scht
+dialog.compress.confirmnone=es sin kei P\u00fcnkte markiert worde
+dialog.deletemarked.nonefound=Kei P\u00fcnkte h\u00e4tte gel\u00f6scht werde k\u00f6nne
+dialog.pastecoordinates.desc=G\u00e4bet Sie hier die Koordinaten inn\u00e4
 dialog.pastecoordinates.coords=Koordinate
-dialog.pastecoordinates.nothingfound=Prüefet Sie die Koordinate und versuechet nomal
-dialog.help.help=Bitte lueg na\n http://activityworkshop.net/software/gpsprune/\nfür wiitere Information und Benutzeraaleitige.
+dialog.pastecoordinates.nothingfound=Pr\u00fcefet Sie die Koordinate und versuechet nomal
+dialog.help.help=Bitte lueg na\n http://activityworkshop.net/software/gpsprune/\nf\u00fcr wiitere Information und Benutzeraaleitige.
 dialog.about.version=Version
 dialog.about.build=Build
-dialog.about.summarytext1=GpsPrune isch s Programm fürs Lade, Darstelle und Editiere vo Date von GPS Geräte.
-dialog.about.summarytext2=Es isch unter den Gnu GPL zur Verfüegig gstellt,für frei, gratis und offen Gebruuch und Wiiterentwicklig.<br>Kopiere, Wiiterverbreitig und Veränderige sin erlaubt und willkomme<br>unter die Bedingige im enthaltene <code>license.txt</code> File.
-dialog.about.summarytext3=Bitte lueget Sie na <code style="font-weight:bold">http://activityworkshop.net/</code> für wiitere Informatione und Benutzeraaleitige.
-dialog.about.languages=Verfüegbare Sproche
-dialog.about.translatedby=Schwiizerdüütschi Ãœbersetzig vo activityworkshop.
+dialog.about.summarytext1=GpsPrune isch s Programm f\u00fcrs Lade, Darstelle und Editiere vo Date von GPS Ger\u00e4te.
+dialog.about.summarytext2=Es isch unter den Gnu GPL zur Verf\u00fcegig gstellt,f\u00fcr frei, gratis und offen Gebruuch und Wiiterentwicklig.<br>Kopiere, Wiiterverbreitig und Ver\u00e4nderige sin erlaubt und willkomme<br>unter die Bedingige im enthaltene <code>license.txt</code> File.
+dialog.about.summarytext3=Bitte lueget Sie na <code style="font-weight:bold">http://activityworkshop.net/</code> f\u00fcr wiitere Informatione und Benutzeraaleitige.
+dialog.about.languages=Verf\u00fcegbare Sproche
+dialog.about.translatedby=Schwiizerd\u00fc\u00fctschi Ãœbersetzig vo activityworkshop.
 dialog.about.systeminfo=Syschtem Info
 dialog.about.systeminfo.os=Betriebsyschtem
 dialog.about.systeminfo.java=Version vonem Java
@@ -367,41 +377,40 @@ dialog.about.systeminfo.gpsbabel=Gpsbabel inschtalliert
 dialog.about.systeminfo.gnuplot=Gnuplot inschtalliert
 dialog.about.systeminfo.exiflib=Exif Bibliothek
 dialog.about.systeminfo.exiflib.internal=Intern
-dialog.about.systeminfo.exiflib.internal.failed=Intern (nöd gfunde)
-dialog.about.systeminfo.exiflib.external=Extärn
-dialog.about.systeminfo.exiflib.external.failed=Extärn (nöd gfunde)
+dialog.about.systeminfo.exiflib.internal.failed=Intern (n\u00f6d gfunde)
+dialog.about.systeminfo.exiflib.external=Ext\u00e4rn
+dialog.about.systeminfo.exiflib.external.failed=Ext\u00e4rn (n\u00f6d gfunde)
 dialog.about.yes=Ja
 dialog.about.no=Nei
 dialog.about.credits=Credits
-dialog.about.credits.code=GpsPrune Code gschriebä vo
+dialog.about.credits.code=GpsPrune Code gschrieb\u00e4 vo
 dialog.about.credits.exifcode=Exif Code vo
 dialog.about.credits.icons=Einigi Bilder vo
-dialog.about.credits.translators=Dolmätscher
+dialog.about.credits.translators=Dolm\u00e4tscher
 dialog.about.credits.translations=Ãœbersetzige mit dr Hilfe vo
-dialog.about.credits.devtools=Entwicklungswärkzüüge
-dialog.about.credits.othertools=Anderi Wärkzüüge
+dialog.about.credits.devtools=Entwicklungsw\u00e4rkz\u00fc\u00fcge
+dialog.about.credits.othertools=Anderi W\u00e4rkz\u00fc\u00fcge
 dialog.about.credits.thanks=Danke an
-dialog.about.readme=Läsmi
-dialog.checkversion.error=Die Versionnummer könne nöd gefprüft werdä.\nGits ne internet Verbindig?
+dialog.about.readme=L\u00e4smi
+dialog.checkversion.error=Die Versionnummer k\u00f6nne n\u00f6d gefpr\u00fcft werd\u00e4.\nGits ne internet Verbindig?
 dialog.checkversion.uptodate=Sie han die noischti Version vonem GpsPrune scho.
 dialog.checkversion.newversion1=Ne noii Version vonem GpsPrune isch jetzt usse! Die heisst jetzt Version
 dialog.checkversion.newversion2=.
 dialog.checkversion.releasedate1=Die noii Version isch am
 dialog.checkversion.releasedate2=ussecho.
 dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://activityworkshop.net/software/gpsprune/download.html.
-dialog.keys.intro=Aastatt d'Muus könnet Sie diese Tastekombinationen nutze
-dialog.keys.keylist=<table><tr><td>Pfiil Taste</td><td>Karte verschiebe</td></tr><tr><td>Strg + links, rächts Pfiil</td><td>Vorherigi oder nöchsti Punkt markiere</td></tr><tr><td>Strg + uuf, aba Pfiil</td><td>Ii- oder Uusezoome</td></tr><tr><td>Strg + Bild uuf, ab</td><td>Vorherigi oder nöchsti Segmänt markiere</td></tr><tr><td>Strg + Pos1, Ã„nde</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt lösche</td></tr></table>
+dialog.keys.intro=Aastatt d'Muus k\u00f6nnet Sie diese Tastekombinationen nutze
+dialog.keys.keylist=<table><tr><td>Pfiil Taste</td><td>Karte verschiebe</td></tr><tr><td>Strg + links, r\u00e4chts Pfiil</td><td>Vorherigi oder n\u00f6chsti Punkt markiere</td></tr><tr><td>Strg + uuf, aba Pfiil</td><td>Ii- oder Uusezoome</td></tr><tr><td>Strg + Bild uuf, ab</td><td>Vorherigi oder n\u00f6chsti Segm\u00e4nt markiere</td></tr><tr><td>Strg + Pos1, Ã„nde</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt l\u00f6sche</td></tr></table>
 dialog.keys.normalmodifier=Strg
 dialog.keys.macmodifier=Kommando
-dialog.saveconfig.desc=Die folgendi Iinstellige könne gspeicheret werde :
+dialog.saveconfig.desc=Die folgendi Iinstellige k\u00f6nne gspeicheret werde :
 dialog.saveconfig.prune.trackdirectory=Trackverzeichnis
-dialog.saveconfig.prune.photodirectory=Föteliverzeichnis
+dialog.saveconfig.prune.photodirectory=F\u00f6teliverzeichnis
 dialog.saveconfig.prune.languagecode=Sprochecode (DE_ch)
 dialog.saveconfig.prune.languagefile=Sprochedatei
-dialog.saveconfig.prune.gpsdevice=GPS Gerätename
+dialog.saveconfig.prune.gpsdevice=GPS Ger\u00e4tename
 dialog.saveconfig.prune.gpsformat=GPS Format
 dialog.saveconfig.prune.povrayfont=Povray Font
-dialog.saveconfig.prune.metricunits=Metrischi Einheite verwende?
 dialog.saveconfig.prune.gnuplotpath=Gnuplot Pfad
 dialog.saveconfig.prune.gpsbabelpath=Gpsbabel Pfad
 dialog.saveconfig.prune.exiftoolpath=Exiftool Pfad
@@ -409,41 +418,42 @@ dialog.saveconfig.prune.mapsource=Kartenserver Index
 dialog.saveconfig.prune.mapsourcelist=Kartenservers
 dialog.saveconfig.prune.diskcache=Kartenordner
 dialog.saveconfig.prune.kmzimagewidth=Bildbreiti im KMZ
-dialog.saveconfig.prune.kmzimageheight=Bildhöchi im KMZ
+dialog.saveconfig.prune.kmzimageheight=Bildh\u00f6chi im KMZ
 dialog.saveconfig.prune.colourscheme=Farbeschema
 dialog.saveconfig.prune.linewidth=Liniedicke
 dialog.saveconfig.prune.kmltrackcolour=KML Trackfarb
 dialog.saveconfig.prune.autosavesettings=Iistellige speichere
-dialog.setpaths.intro=Sie könnet dann die Pfade für dia Applikatione setzä:
+dialog.setpaths.intro=Sie k\u00f6nnet dann die Pfade f\u00fcr dia Applikatione setz\u00e4:
 dialog.setpaths.found=Pfad gfunde?
-dialog.addaltitude.noaltitudes=Dr seläktierte Beriich hät keini Höchiinformation
-dialog.addaltitude.desc=Höchiverschiebig zuzutue
-dialog.lookupsrtm.overwritezeros=Höchiwärte von null Ã¼berschriebä?
-dialog.setcolours.intro=Klicket Sie uuf ne Farb um sie z'verändere
+dialog.addaltitude.noaltitudes=Dr sel\u00e4ktierte Beriich h\u00e4t keini H\u00f6chiinformation
+dialog.addaltitude.desc=H\u00f6chiverschiebig zuzutue
+dialog.lookupsrtm.overwritezeros=H\u00f6chiw\u00e4rte von null \u00fcberschrieb\u00e4?
+dialog.setcolours.intro=Klicket Sie uuf ne Farb um sie z'ver\u00e4ndere
 dialog.setcolours.background=Hintergrund
 dialog.setcolours.borders=Rande
 dialog.setcolours.lines=Linie
-dialog.setcolours.primary=Primär
-dialog.setcolours.secondary=Secondär
-dialog.setcolours.point=Punkte
+dialog.setcolours.primary=Prim\u00e4r
+dialog.setcolours.secondary=Second\u00e4r
+dialog.setcolours.point=P\u00fcnkte
 dialog.setcolours.selection=Beriich
 dialog.setcolours.text=Texte
-dialog.colourchooser.title=Farbe uuswähle
+dialog.colourchooser.title=Farbe uusw\u00e4hle
 dialog.colourchooser.red=Rot
-dialog.colourchooser.green=Grüen
+dialog.colourchooser.green=Gr\u00fcen
 dialog.colourchooser.blue=Blau
-dialog.setlanguage.firstintro=Sie könnet entweder eini vo den iigebouti Sproche<p>oder ne Text-Datei uuswähle.
-dialog.setlanguage.secondintro=Sie münt Ihri Iistellige speichere und dann<p>GpsPrune wieder neustarte um die Sproch z'ändere.
+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
 dialog.setlanguage.languagefile=Sproch Datei
-dialog.setlanguage.endmessage=Jetze speicheret Sie Ihri Iistellige und startet Sie GpsPrune neu\num t noii Sproch z' verwände.
-dialog.setlanguage.endmessagewithautosave=Startet Sie GpsPrune neu um t noii Sproch z' verwände.
+dialog.setlanguage.endmessage=Jetze speicheret Sie Ihri Iistellige und startet Sie GpsPrune neu\num t noii Sproch z' verw\u00e4nde.
+dialog.setlanguage.endmessagewithautosave=Startet Sie GpsPrune neu um t noii Sproch z' verw\u00e4nde.
 dialog.diskcache.save=Karten uufem Disk speichere
 dialog.diskcache.dir=Kartenordner
 dialog.diskcache.createdir=Ordner kreiere
-dialog.diskcache.nocreate=Ordner isch nöd kreiert worde
+dialog.diskcache.nocreate=Ordner isch n\u00f6d kreiert worde
+dialog.diskcache.cannotwrite=Kachle k\u00f6nned n\u00f6d im Ordner gspeicheret werde
 dialog.diskcache.table.path=Pfad
-dialog.diskcache.table.usedby=Aawänder
+dialog.diskcache.table.usedby=Aaw\u00e4nder
 dialog.diskcache.table.zoom=Zoom
 dialog.diskcache.table.tiles=Kachle
 dialog.diskcache.table.megabytes=Megabytes
@@ -454,87 +464,90 @@ dialog.diskcache.maximumage=Maximali Alter (Tag)
 dialog.diskcache.deleteall=Alli Kachle l\u00f6sche
 dialog.diskcache.deleted1=Es sin
 dialog.diskcache.deleted2=Files uusem Ordner gl\u00f6scht worde
-dialog.deletefieldvalues.intro=Wählet Sie s Fäld uus zum lösche
-dialog.setlinewidth.text=Gäbet Sie die Dicke vonen Linien ii (1-4)
-dialog.downloadosm.desc=Best\ätige um rohi OSM Date fürn Gebiet aba zlade:
+dialog.deletefieldvalues.intro=W\u00e4hlet Sie s F\u00e4ld uus zum l\u00f6sche
+dialog.deletefieldvalues.nofields=Es sin kei F\u00e4lder z'l\u00f6sche f\u00fcr dere Beriich
+dialog.setlinewidth.text=G\u00e4bet Sie die Dicke vonen Linien ii (1-4)
+dialog.downloadosm.desc=Best\u00e4tige um rohi OSM Date f\u00fcrn Gebiet aba zlade:
 dialog.searchwikipedianames.search=Sueche na:
 
 # 3d window
-dialog.3d.title=GpsPrune Drüü-d Aasicht
-dialog.3d.altitudefactor=Höchivervilfachigsfaktor
+dialog.3d.title=GpsPrune Dr\u00fc\u00fc-d Aasicht
+dialog.3d.altitudefactor=H\u00f6chivervilfachigsfaktor
 dialog.3dlines.title=GpsPrune Gitterlinie
-dialog.3dlines.empty=Kei Linie zum aazeigä!
-dialog.3dlines.intro=Hier sin die Linie für die drüü-D Aasicht
+dialog.3dlines.empty=Kei Linie zum aazeig\u00e4!
+dialog.3dlines.intro=Hier sin die Linie f\u00fcr die dr\u00fc\u00fc-D Aasicht
 
 # Confirm messages
 confirm.loadfile=Date glade vom
 confirm.save.ok1=Es sin
-confirm.save.ok2=Punkte gspeicheret worde na
+confirm.save.ok2=P\u00fcnkte gspeicheret worde na
 confirm.deletepoint.single=Punkt isch entfernt worde
-confirm.deletepoint.multi=Punkte sin entfernt worde
+confirm.deletepoint.multi=P\u00fcnkte sin entfernt worde
 confirm.point.edit=Punkt editiert
-confirm.mergetracksegments=Segmänte gmerged
-confirm.reverserange=Beriich umgdrähet
+confirm.mergetracksegments=Segm\u00e4nte gmerged
+confirm.reverserange=Beriich umgdr\u00e4het
 confirm.addtimeoffset=Ziitverschiebig zutue
-confirm.addaltitudeoffset=Höchiverschiebig zutue
+confirm.addaltitudeoffset=H\u00f6chiverschiebig zutue
 confirm.rearrangewaypoints=Waypoints umorganisiert
 confirm.rearrangephotos=Fotos umorganisiert
 confirm.cutandmove=Beriich gmoved
+confirm.interpolate=P\u00fcnkte iigf\u00fcgt worde
 confirm.convertnamestotimes=Waypointname verwondlet
 confirm.saveexif.ok1=Es sin
-confirm.saveexif.ok2=Fötelis gschriebe worde
-confirm.undo.single=Operation rückgängig gmacht worde.
-confirm.undo.multi=Operatione rückgängig gmacht worde.
-confirm.jpegload.single=Föteli isch glade worde
-confirm.jpegload.multi=Fötelis sin glade worde
-confirm.media.connect=Media verbundä
-confirm.photo.disconnect=Föteli gtrännt
-confirm.audio.disconnect=Audio gtrännt
-confirm.correlatephotos.single=Föteli isch korreliert worde
-confirm.correlatephotos.multi=Fötelis sin korreliert worde
+confirm.saveexif.ok2=F\u00f6telis gschriebe worde
+confirm.undo.single=Operation r\u00fcckg\u00e4ngig gmacht worde.
+confirm.undo.multi=Operatione r\u00fcckg\u00e4ngig gmacht worde.
+confirm.jpegload.single=F\u00f6teli isch glade worde
+confirm.jpegload.multi=F\u00f6telis sin glade worde
+confirm.media.connect=Media verbund\u00e4
+details.media.fullpath=Ganzi Pfad
+confirm.photo.disconnect=F\u00f6teli gtr\u00e4nnt
+confirm.audio.disconnect=Audio gtr\u00e4nnt
+confirm.correlatephotos.single=F\u00f6teli isch korreliert worde
+confirm.correlatephotos.multi=F\u00f6telis sin korreliert worde
 confirm.createpoint=Punkt kreiert worde
-confirm.rotatephoto=Föteli umgedräit worde
+confirm.rotatephoto=F\u00f6teli umgedr\u00e4it worde
 confirm.running=Am Laufe ...
 confirm.lookupsrtm1=Es sin
-confirm.lookupsrtm2=Höhenwerte gfunde
-confirm.deletefieldvalues=Feldwärte glöscht worde
+confirm.lookupsrtm2=H\u00f6henwerte gfunde
+confirm.deletefieldvalues=Feldw\u00e4rte gl\u00f6scht worde
 confirm.audioload=Audiofiles glade worde
-confirm.media.removed=entfärnt
+confirm.media.removed=entf\u00e4rnt
 confirm.correlateaudios.single=Audiofile isch korreliert worde
 confirm.correlateaudios.multi=Audiofiles sin korreliert worde
 
 # Buttons
 button.ok=OK
 button.back=Zrugg
-button.next=Nöchstä
+button.next=N\u00f6chst\u00e4
 button.finish=Fertig
-button.cancel=Abbrächä
-button.overwrite=Überschriibä
-button.moveup=Uufä schiebä
-button.movedown=Aba schiebä
-button.showlines=Linie aazeigä
-button.edit=Editierä
-button.exit=Beändä
-button.close=Schliessä
-button.continue=Fortsetzä
+button.cancel=Abbr\u00e4ch\u00e4
+button.overwrite=Ãœberschriib\u00e4
+button.moveup=Uuf\u00e4 schieb\u00e4
+button.movedown=Aba schieb\u00e4
+button.showlines=Linie aazeig\u00e4
+button.edit=Editier\u00e4
+button.exit=Be\u00e4nd\u00e4
+button.close=Schliess\u00e4
+button.continue=Fortsetz\u00e4
 button.yes=Ja
 button.no=Nei
-button.yestoall=Ja für alli
-button.notoall=Nei für alli
-button.select=Uuswähle
-button.selectall=Alli uuswähle
-button.selectnone=Nüüt uuswähle
-button.preview=Vorschauä
-button.load=Ladä
-button.upload=Uufaladä
-button.guessfields=Fälde erratä
-button.showwebpage=Websiite aazeigä
-button.check=Prüefa
-button.resettodefaults=Zurücksetzä
-button.browse=Durasuechä...
-button.addnew=Hinzuefügä
-button.delete=Entfärnä
-button.manage=Verwoltä
+button.yestoall=Ja f\u00fcr alli
+button.notoall=Nei f\u00fcr alli
+button.select=Uusw\u00e4hle
+button.selectall=Alli uusw\u00e4hle
+button.selectnone=N\u00fc\u00fct uusw\u00e4hle
+button.preview=Vorschau\u00e4
+button.load=Lad\u00e4
+button.upload=Uufalad\u00e4
+button.guessfields=F\u00e4lde errat\u00e4
+button.showwebpage=Websiite aazeig\u00e4
+button.check=Pr\u00fcefa
+button.resettodefaults=Zur\u00fccksetz\u00e4
+button.browse=Durasuech\u00e4...
+button.addnew=Hinzuef\u00fcg\u00e4
+button.delete=Entf\u00e4rn\u00e4
+button.manage=Verwolt\u00e4
 
 # File types
 filetype.txt=TXT Dateie
@@ -549,19 +562,19 @@ filetype.audio=MP3, OGG, WAV Dateie
 
 # Display components
 display.nodata=Kei Date glade worde
-display.noaltitudes=Track hät kei Höhi Date
-display.notimestamps=Track hät kei Ziitstämple
+display.noaltitudes=Track h\u00e4t kei H\u00f6hi Date
+display.notimestamps=Track h\u00e4t kei Ziitst\u00e4mple
 details.trackdetails=Details vom Track
 details.notrack=Kei Track glade worde
-details.track.points=Punkte
+details.track.points=P\u00fcnkte
 details.track.file=Datei
 details.track.numfiles=Anzahl Dateie
 details.pointdetails=Details vonem Punkt
 details.index.selected=Index
 details.index.of=vo
-details.nopointselection=Nüüt selektiert
-details.photofile=Föteli Datei
-details.norangeselection=Nüüt selektiert
+details.nopointselection=N\u00fc\u00fct selektiert
+details.photofile=F\u00f6teli Datei
+details.norangeselection=N\u00fc\u00fct selektiert
 details.rangedetails=Details vonem Beriich
 details.range.selected=Selektiert
 details.range.to=bis
@@ -575,39 +588,38 @@ display.range.time.mins=Min
 display.range.time.hours=Std
 display.range.time.days=T
 details.range.avespeed=Gschwindikeit
-details.range.avemovingspeed=Gschwindikeit ufem Wäg
-details.range.maxspeed=Höchstgschwindikeit
-details.range.numsegments=Aazahl Segmänte
+details.range.maxspeed=H\u00f6chstgschwindikeit
+details.range.numsegments=Aazahl Segm\u00e4nte
 details.range.pace=Tempo
-details.range.gradient=Gefälle
+details.range.gradient=Gef\u00e4lle
 details.lists.waypoints=Waypoints
-details.lists.photos=Fötelis
-details.photodetails=Details vonem Föteli
-details.nophoto=Kei föteli selektiert
-details.photo.loading=Ladä
+details.lists.photos=F\u00f6telis
+details.photodetails=Details vonem F\u00f6teli
+details.nophoto=Kei f\u00f6teli selektiert
+details.photo.loading=Lad\u00e4
 details.photo.bearing=Richtig
-details.media.connected=Verbundä
+details.media.connected=Verbund\u00e4
 details.lists.audio=Audio
 details.audiodetails=Audiodetails
 details.noaudio=Kei Audiofile selektiert
 details.audio.file=Audiofile
-details.audio.playing=am abschpielä...
+details.audio.playing=am abschpiel\u00e4...
 map.overzoom=Kei Karte mit diesem Zoom
 
 # Field names
 fieldname.latitude=Breitegrad
-fieldname.longitude=Längegrad
-fieldname.altitude=Höchi
-fieldname.timestamp=Ziitstämpel
+fieldname.longitude=L\u00e4ngegrad
+fieldname.altitude=H\u00f6chi
+fieldname.timestamp=Ziitst\u00e4mpel
 fieldname.time=Ziit
 fieldname.waypointname=Name
 fieldname.waypointtype=Typ
-fieldname.newsegment=Segmänt
+fieldname.newsegment=Segm\u00e4nt
 fieldname.custom=Custom
-fieldname.prefix=Fäld
-fieldname.distance=Längi
-fieldname.movingdistance=Weglängi
-fieldname.duration=Ziitlängi
+fieldname.prefix=F\u00e4ld
+fieldname.distance=L\u00e4ngi
+fieldname.movingdistance=Wegl\u00e4ngi
+fieldname.duration=Ziitl\u00e4ngi
 fieldname.speed=Gschwindikeit
 fieldname.verticalspeed=Uf/Ab Gschwindikeit
 fieldname.description=Bschriibig
@@ -616,11 +628,15 @@ fieldname.description=Bschriibig
 units.original=Original
 units.default=Default
 units.metres=Meter
+units.metres.short=m
 units.kilometres=Kilometer
 units.kilometres.short=km
-units.kmh=km/h
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.kilometresperhour.short=kmh
+units.nauticalmiles=Seemeile
+units.nauticalmiles.short=sm
+units.nauticalmilesperhour.short=kn
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=Std
 units.degminsec=Grad-Min-Sek
 units.degmin=Grad-Min
@@ -638,74 +654,77 @@ cardinal.e=O
 cardinal.w=W
 
 # Undo operations
-undo.load=Date ladä
-undo.loadphotos=Fötelis ladä
-undo.loadaudios=Audiofiles ladä
-undo.editpoint=Punkt editierä
-undo.deletepoint=Punkt löschä
-undo.removephoto=Föteli entfärnä
-undo.removeaudio=Audiofile entfärnä
-undo.deleterange=Beriich löschä
-undo.compress=Track komprimierä
-undo.insert=Punkte innätuä
-undo.reverse=Beriich umdrähie
-undo.mergetracksegments=track segmänte merge
+undo.load=Date lad\u00e4
+undo.loadphotos=F\u00f6telis lad\u00e4
+undo.loadaudios=Audiofiles lad\u00e4
+undo.editpoint=Punkt editier\u00e4
+undo.deletepoint=Punkt l\u00f6sch\u00e4
+undo.removephoto=F\u00f6teli entf\u00e4rn\u00e4
+undo.removeaudio=Audiofile entf\u00e4rn\u00e4
+undo.deleterange=Beriich l\u00f6sch\u00e4
+undo.croptrack=Track zuschniid\u00e4
+undo.deletemarked=P\u00fcnkte l\u00f6sch\u00e4
+undo.insert=P\u00fcnkte inn\u00e4tu\u00e4
+undo.reverse=Beriich umdr\u00e4hie
+undo.mergetracksegments=track segm\u00e4nte merge
 undo.addtimeoffset=ziitverschiebig zutue
-undo.addaltitudeoffset=höchiverschiebig zutue
-undo.rearrangewaypoints=Waypoints reorganisierä
-undo.cutandmove=Selektion movä
-undo.connect=verbindä
-undo.disconnect=trännä
-undo.correlatephotos=Fötelis korrelierä
-undo.rearrangephotos=Fötelis reorganisierä
-undo.createpoint=Punkt kreierä
-undo.rotatephoto=Föteli umadräya
-undo.convertnamestotimes=Name ins Ziitstämple verwondlä
-undo.lookupsrtm=Höhendate vonem SRTM holä
-undo.deletefieldvalues=Feldwärte löschä
-undo.correlateaudios=Audios korrelierä
+undo.addaltitudeoffset=h\u00f6chiverschiebig zutue
+undo.rearrangewaypoints=Waypoints reorganisier\u00e4
+undo.cutandmove=Selektion mov\u00e4
+undo.connect=verbind\u00e4
+undo.disconnect=tr\u00e4nn\u00e4
+undo.correlatephotos=F\u00f6telis korrelier\u00e4
+undo.rearrangephotos=F\u00f6telis reorganisier\u00e4
+undo.createpoint=Punkt kreier\u00e4
+undo.rotatephoto=F\u00f6teli umadr\u00e4ya
+undo.convertnamestotimes=Name ins Ziitst\u00e4mple verwondl\u00e4
+undo.lookupsrtm=H\u00f6hendate vonem SRTM hol\u00e4
+undo.deletefieldvalues=Feldw\u00e4rte l\u00f6sch\u00e4
+undo.correlateaudios=Audios korrelier\u00e4
 
 # Error messages
-error.save.dialogtitle=Fähle bim Speichere
+error.save.dialogtitle=F\u00e4hle bim Speichere
 error.save.nodata=Kei Date zum speichere
 error.save.failed=Speichere vom File fehlgschlage
-error.saveexif.filenotfound=Föteli File nöd gfunde
-error.saveexif.cannotoverwrite1=Föteli File
-error.saveexif.cannotoverwrite2=isch nöd schriibbar. Speichere na einer Kopie?
+error.saveexif.filenotfound=F\u00f6teli File n\u00f6d gfunde
+error.saveexif.cannotoverwrite1=F\u00f6teli File
+error.saveexif.cannotoverwrite2=isch n\u00f6d schriibbar. Speichere na einer Kopie?
 error.saveexif.failed1=
-error.saveexif.failed2=von d Bilder han i nöd speichere könne
+error.saveexif.failed2=von d Bilder han i n\u00f6d k\u00f6nne speichere
 error.saveexif.forced1=
-error.saveexif.forced2=von d Bilder han i müsse forziere
-error.load.dialogtitle=Fähle bim Lade
-error.load.noread=File cha nöd glase werde
-error.load.nopoints=Kei gültigi Information inem File gfunde
+error.saveexif.forced2=von d Bilder han i m\u00fcsse forziere
+error.load.dialogtitle=F\u00e4hle bim Lade
+error.load.noread=File cha n\u00f6d glase werde
+error.load.nopoints=Kei g\u00fcltigi Information inem File gfunde
 error.load.unknownxml=Unbekanntes xml Format:
 error.load.noxmlinzip=Kei xml im Zip File gfunde
-error.load.othererror=Fähle bim Läse:
-error.jpegload.dialogtitle=Fähle bim Lade von Fötelis
+error.load.othererror=F\u00e4hle bim L\u00e4se:
+error.jpegload.dialogtitle=F\u00e4hle bim Lade von F\u00f6telis
 error.jpegload.nofilesfound=Kei Files gfunde
 error.jpegload.nojpegsfound=Kei Jpegs gfunde
 error.jpegload.nogpsfound=Kei GPS Information gfunde
-error.jpegload.exifreadfailed=EXIF Uufruef isch fehlgschlage. Kei EXIF Infos könnet gläse werde\nohni nen interni oder extärni Bibliothek.
+error.jpegload.exifreadfailed=EXIF Uufruef isch fehlgschlage. Kei EXIF Infos k\u00f6nnet gl\u00e4se werde\nohni nen interni oder ext\u00e4rni Bibliothek.
 error.audioload.nofilesfound=Kei Audiofiles gfunde
-error.gpsload.unknown=Unbekannts Fähler
+error.gpsload.unknown=Unbekannts F\u00e4hler
 error.undofailed.title=Undo isch fehlgschlage worde
-error.undofailed.text=Operation kann nöd rückgängig gmacht werde
-error.function.noop.title=Funktion hät gar nüüt gmacht
-error.rearrange.noop=Punkte Reorganisierig hät kei Effäkt gha
-error.function.notavailable.title=Funktion nöd verfüegbar
-error.function.nojava3d=Sorry, d'Funktion brucht d Java3d Library,\nvo Sun.com erhältlech.
-error.3d=N Fähler isch mitere 3d Darstellig ufgträte
-error.readme.notfound=Läs mi File nöd gfunde
-error.osmimage.dialogtitle=Fähle bim Bildli-Lade
-error.osmimage.failed=Map Bildli könne nöd glade werde.  Gits ne Internet Verbindig?
-error.language.wrongfile=Die uusgewählti Datei scheint kei Sproch-Datei für GpsPrune z'sii
-error.convertnamestotimes.nonames=Kei Namen han könnet verwondlet werde
-error.lookupsrtm.nonefound=Kei Höhendate verfüegbar für d'Punkte
-error.lookupsrtm.nonerequired=Alle Punkte han die Höhendate scho.  Nüüt z'tue.
-error.gpsies.uploadnotok=Der Gpsies Server hät gseit gha
+error.undofailed.text=Operation kann n\u00f6d r\u00fcckg\u00e4ngig gmacht werde
+error.function.noop.title=Funktion h\u00e4t gar n\u00fc\u00fct gmacht
+error.rearrange.noop=P\u00fcnkte Reorganisierig h\u00e4t kei Eff\u00e4kt gha
+error.function.notavailable.title=Funktion n\u00f6d verf\u00fcegbar
+error.function.nojava3d=Sorry, d'Funktion brucht d Java3d Library,\nvo Sun.com erh\u00e4ltlech.
+error.3d=N F\u00e4hler isch mitere 3d Darstellig ufgtr\u00e4te
+error.readme.notfound=L\u00e4s mi File n\u00f6d gfunde
+error.osmimage.dialogtitle=F\u00e4hle bim Bildli-Lade
+error.osmimage.failed=Map Bildli k\u00f6nne n\u00f6d glade werde.  Gits ne Internet Verbindig?
+error.language.wrongfile=Die uusgew\u00e4hlti Datei scheint kei Sproch-Datei f\u00fcr GpsPrune z'sii
+error.convertnamestotimes.nonames=Kei Namen han k\u00f6nnet verwondlet werde
+error.lookupsrtm.nonefound=Kei H\u00f6hendate verf\u00fcegbar f\u00fcr d'P\u00fcnkte
+error.lookupsrtm.nonerequired=Alle P\u00fcnkte han die H\u00f6hendate scho.  N\u00fc\u00fct z'tue.
+error.gpsies.uploadnotok=Der Gpsies Server h\u00e4t gseit gha
 error.gpsies.uploadfailed=S Uufalade isch fehlgschlage
+error.showphoto.failed=S F\u00f6teli han i n\u00f6d k\u00f6nne lade
 error.playaudiofailed=S Abschpiele vonem File isch fehlgschlage
-error.cache.notthere=D Ordner isch nöd gfunde worde
-error.cache.empty=D Ordner hät nüüt drinne
+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
index 003593b58a1700e52024f28c72651110294d095f..f2e70e1d9a6ba94f8a8c7d92dafa36985d9e3426 100644 (file)
@@ -1,5 +1,5 @@
 # Text entries for the GpsPrune application
-# English entries as default - others can be added
+# English entries as default
 
 # Menu entries
 menu.file=File
@@ -10,6 +10,7 @@ menu.file.exit=Exit
 menu.track=Track
 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
@@ -20,8 +21,6 @@ menu.range.all=Select all
 menu.range.none=Select none
 menu.range.start=Set range start
 menu.range.end=Set range end
-menu.range.deleterange=Delete range
-menu.range.interpolate=Interpolate
 menu.range.average=Average selection
 menu.range.reverse=Reverse range
 menu.range.mergetracksegments=Merge track segments
@@ -54,6 +53,7 @@ menu.map.connect=Connect track points
 menu.map.autopan=Autopan
 menu.map.showmap=Show map
 menu.map.showscalebar=Show scalebar
+menu.map.editmode=Edit mode
 
 # Alt keys for menus
 altkey.menu.file=F
@@ -86,6 +86,9 @@ function.exportpov=Export POV
 function.exportsvg=Export SVG
 function.editwaypointname=Edit waypoint name
 function.compress=Compress track
+function.deleterange=Delete range
+function.croptrack=Crop track
+function.interpolate=Interpolate points
 function.addtimeoffset=Add time offset
 function.addaltitudeoffset=Add altitude offset
 function.findwaypoint=Find waypoint
@@ -140,6 +143,7 @@ dialog.deletepoint.title=Delete Point
 dialog.deletepoint.deletephoto=Delete photo attached to this point?
 dialog.deletephoto.title=Delete Photo
 dialog.deletephoto.deletepoint=Delete point attached to this photo?
+dialog.deleteaudio.deletepoint=Delete point attached to this audio clip?
 dialog.openoptions.title=Open options
 dialog.openoptions.filesnippet=Extract of file
 dialog.load.table.field=Field
@@ -219,8 +223,8 @@ dialog.confirmreversetrack.title=Confirm reversal
 dialog.confirmreversetrack.text=This track contains timestamp information, which will be out of sequence after a reversal.\nAre you sure you want to reverse this section?
 dialog.confirmcutandmove.title=Confirm cut and move
 dialog.confirmcutandmove.text=This track contains timestamp information, which will be out of sequence after a move.\nAre you sure you want to move this section?
-dialog.interpolate.title=Interpolate points
-dialog.interpolate.parameter.text=Number of points to insert between selected points
+dialog.interpolate.parameter.text=Number of points to insert between each pair of points
+dialog.interpolate.betweenwaypoints=Interpolate between waypoints?
 dialog.undo.title=Undo action(s)
 dialog.undo.pretext=Please select the action(s) to undo
 dialog.undo.none.title=Cannot undo
@@ -237,7 +241,7 @@ dialog.pointedit.changevalue.title=Edit field
 dialog.pointnameedit.name=Waypoint name
 dialog.pointnameedit.uppercase=UPPER case
 dialog.pointnameedit.lowercase=lower case
-dialog.pointnameedit.sentencecase=Sentence Case
+dialog.pointnameedit.titlecase=Title Case
 dialog.addtimeoffset.add=Add time
 dialog.addtimeoffset.subtract=Subtract time
 dialog.addtimeoffset.days=Days
@@ -273,6 +277,8 @@ dialog.distances.column.to=To point
 dialog.distances.currentpoint=Current point
 dialog.distances.toofewpoints=This function needs waypoints in order to calculate the distances between them
 dialog.fullrangedetails.intro=Here are the details for the selected range
+dialog.fullrangedetails.coltotal=Including gaps
+dialog.fullrangedetails.colsegments=Without gaps
 dialog.setmapbg.intro=Select one of the map sources, or add a new one
 dialog.addmapsource.title=Add new map source
 dialog.addmapsource.sourcename=Name of source
@@ -303,6 +309,7 @@ dialog.wikipedia.column.name=Article name
 dialog.wikipedia.column.distance=Distance
 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?
 dialog.correlate.photoselect.intro=Select one of these correlated photos to use as the time offset
 dialog.correlate.select.photoname=Photo name
 dialog.correlate.select.timediff=Time difference
@@ -324,7 +331,7 @@ dialog.correlate.options.timelimit=Time limit
 dialog.correlate.options.nodistancelimit=No distance limit
 dialog.correlate.options.distancelimit=Distance limit
 dialog.correlate.options.correlate=Correlate
-dialog.correlate.alloutsiderange=All photos are outside the time range of the track, so none can be correlated.\nTry changing the offset or manually correlating at least one photo.
+dialog.correlate.alloutsiderange=All the items are outside the time range of the track, so none can be correlated.\nTry changing the offset or manually correlating at least one item.
 dialog.correlate.filetimes=File timestamps denote:
 dialog.correlate.filetimes2=of audio clip
 dialog.correlate.correltimes=For correlation, use:
@@ -340,7 +347,6 @@ 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.compress.nonefound=No data points could be removed
 dialog.compress.duplicates.title=Duplicate removal
 dialog.compress.closepoints.title=Nearby point removal
 dialog.compress.closepoints.paramdesc=Span factor
@@ -351,15 +357,19 @@ dialog.compress.singletons.paramdesc=Distance factor
 dialog.compress.douglaspeucker.title=Douglas-Peucker compression
 dialog.compress.douglaspeucker.paramdesc=Span factor
 dialog.compress.summarylabel=Points to delete
+dialog.compress.confirm1=
+dialog.compress.confirm2=points have been marked.\nUse Track->Delete marked points to delete them
+dialog.compress.confirmnone=no points have been marked
+dialog.deletemarked.nonefound=No data points could be removed
 dialog.pastecoordinates.desc=Enter or paste the coordinates here
 dialog.pastecoordinates.coords=Coordinates
 dialog.pastecoordinates.nothingfound=Please check the coordinates and try again
-dialog.help.help=Please see\n http://activityworkshop.net/software/gpsprune/\nfor more information and user guides.
+dialog.help.help=Please see\n http://gpsprune.activityworkshop.net/\nfor more information and tips,\nincluding a new PDF user guide you can buy.
 dialog.about.version=Version
 dialog.about.build=Build
 dialog.about.summarytext1=GpsPrune is a program for loading, displaying and editing data from GPS receivers.
 dialog.about.summarytext2=It is released under the Gnu GPL for free, open, worldwide use and enhancement.<br>Copying, redistribution and modification are permitted and encouraged<br>according to the conditions in the included <code>license.txt</code> file.
-dialog.about.summarytext3=Please see <code style="font-weight:bold">http://activityworkshop.net/</code> for more information and user guides.
+dialog.about.summarytext3=Please see <code style="font-weight:bold">http://activityworkshop.net/</code> for more information and tips, including<br>a new PDF user guide you can buy.
 dialog.about.languages=Available languages
 dialog.about.translatedby=English text by activityworkshop.
 dialog.about.systeminfo=System info
@@ -406,7 +416,6 @@ dialog.saveconfig.prune.languagefile=Language file
 dialog.saveconfig.prune.gpsdevice=GPS device
 dialog.saveconfig.prune.gpsformat=GPS format
 dialog.saveconfig.prune.povrayfont=Povray font
-dialog.saveconfig.prune.metricunits=Use metric units?
 dialog.saveconfig.prune.gnuplotpath=Path to gnuplot
 dialog.saveconfig.prune.gpsbabelpath=Path to gpsbabel
 dialog.saveconfig.prune.exiftoolpath=Path to exiftool
@@ -447,6 +456,7 @@ dialog.diskcache.save=Save map images to disk
 dialog.diskcache.dir=Cache directory
 dialog.diskcache.createdir=Create directory
 dialog.diskcache.nocreate=Cache directory not created
+dialog.diskcache.cannotwrite=Map tiles cannot be saved in the selected directory
 dialog.diskcache.table.path=Path
 dialog.diskcache.table.usedby=Used by
 dialog.diskcache.table.zoom=Zoom
@@ -460,6 +470,7 @@ dialog.diskcache.deleteall=Delete all tiles
 dialog.diskcache.deleted1=Deleted
 dialog.diskcache.deleted2=files from the cache
 dialog.deletefieldvalues.intro=Select the field to delete for the current range
+dialog.deletefieldvalues.nofields=There are no fields to delete for this range
 dialog.setlinewidth.text=Enter the thickness of lines to draw for the tracks (1-4)
 dialog.downloadosm.desc=Confirm to download the raw OSM data for the specified area:
 dialog.searchwikipedianames.search=Search for:
@@ -485,6 +496,7 @@ confirm.addaltitudeoffset=Altitude offset added
 confirm.rearrangewaypoints=Waypoints rearranged
 confirm.rearrangephotos=Photos rearranged
 confirm.cutandmove=Selection moved
+confirm.interpolate=Points added
 confirm.convertnamestotimes=Waypoint names converted
 confirm.saveexif.ok1=Saved
 confirm.saveexif.ok2=photo files
@@ -580,7 +592,6 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=d
 details.range.avespeed=Ave speed
-details.range.avemovingspeed=Moving average
 details.range.maxspeed=Maximum speed
 details.range.numsegments=Number of segments
 details.range.pace=Pace
@@ -593,6 +604,7 @@ details.nophoto=No photo selected
 details.photo.loading=Loading
 details.photo.bearing=Bearing
 details.media.connected=Connected
+details.media.fullpath=Full path
 details.audiodetails=Audio details
 details.noaudio=No audio clip selected
 details.audio.file=Audio file
@@ -626,12 +638,15 @@ units.feet=Feet
 units.feet.short=ft
 units.kilometres=Kilometres
 units.kilometres.short=km
-units.kmh=km/h
+units.kilometresperhour.short=km/h
 units.miles=Miles
 units.miles.short=mi
-units.mph=mph
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mph
+units.nauticalmiles=Nautical miles
+units.nauticalmiles.short=N.m.
+units.nauticalmilesperhour.short=kts
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=hours
 units.degminsec=Deg-min-sec
 units.degmin=Deg-min
@@ -657,7 +672,8 @@ undo.deletepoint=delete point
 undo.removephoto=remove photo
 undo.removeaudio=remove audio clip
 undo.deleterange=delete range
-undo.compress=compress track
+undo.croptrack=crop track
+undo.deletemarked=delete points
 undo.insert=insert points
 undo.reverse=reverse range
 undo.mergetracksegments=merge track segments
@@ -716,7 +732,9 @@ error.lookupsrtm.nonefound=No altitude values available for these points
 error.lookupsrtm.nonerequired=All points already have altitudes, so there's nothing to lookup
 error.gpsies.uploadnotok=The gpsies server returned the message
 error.gpsies.uploadfailed=The upload failed with the error
+error.showphoto.failed=Failed to load photo
 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
index 49bc392e06a2fae6168b18d9f8b4d0b4e38441f9..77f889328b5b00cbab166c52346afb72f1873181 100644 (file)
@@ -20,8 +20,8 @@ menu.range.all=Seleccionar todo
 menu.range.none=No seleccionar nada
 menu.range.start=Fijar comienzo
 menu.range.end=Fijar final
-menu.range.deleterange=Eliminar rango
-menu.range.interpolate=Interpolar
+function.deleterange=Eliminar rango
+function.interpolate=Interpolar puntos
 menu.range.average=Crear punto a la media del rango
 menu.range.reverse=Invertir rango
 menu.range.mergetracksegments=Unir los segmentos de track
@@ -219,7 +219,6 @@ dialog.confirmreversetrack.title=Confirmar inversi\u00f3n
 dialog.confirmreversetrack.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de la inversi\u00f3n. \u00bfEst\u00e1 seguro que desea invertir esta secci\u00f3n?
 dialog.confirmcutandmove.title=Confirmar accion cortar/pegar
 dialog.confirmcutandmove.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de mover.\n\u00bfEsta seguro que desea mover esta secci\u00f3n?
-dialog.interpolate.title=Interpolar puntos
 dialog.interpolate.parameter.text=N\u00famero de los puntos a insertar entre los puntos elegidos
 dialog.undo.title=Deshacer
 dialog.undo.pretext=Por favor, seleccione la operaci\u00f3n(es) a deshacer
@@ -237,7 +236,7 @@ dialog.pointedit.changevalue.title=Editar campo
 dialog.pointnameedit.name=Nombre de waypoint
 dialog.pointnameedit.uppercase=May\u00fasculas
 dialog.pointnameedit.lowercase=min\u00fasculas
-dialog.pointnameedit.sentencecase=Mezcla
+dialog.pointnameedit.titlecase=Mezcla
 dialog.addtimeoffset.add=A\u00f1adir tiempo
 dialog.addtimeoffset.subtract=Sustraer tiempo
 dialog.addtimeoffset.days=Dias
@@ -340,7 +339,7 @@ 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.compress.nonefound=Ning\u00fan punto eliminado
+dialog.deletemarked.nonefound=Ning\u00fan punto eliminado
 dialog.compress.closepoints.title=remover puntos cercanos
 dialog.compress.closepoints.paramdesc=Factor de extensi\u00f3n
 dialog.compress.wackypoints.title=Eliminar puntos an\u00f3malos
@@ -406,7 +405,6 @@ dialog.saveconfig.prune.languagefile=Archivo de lenguaje
 dialog.saveconfig.prune.gpsdevice=Dispositivo GPS
 dialog.saveconfig.prune.gpsformat=Formato GPS
 dialog.saveconfig.prune.povrayfont=Fuente povray
-dialog.saveconfig.prune.metricunits=\u00bfUsar unidades m\u00e9tricas?
 dialog.saveconfig.prune.gnuplotpath=Ruta a gnuplot
 dialog.saveconfig.prune.gpsbabelpath=Ruta a gpsbabel
 dialog.saveconfig.prune.exiftoolpath=Ruta a exiftool
@@ -580,7 +578,6 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=d
 details.range.avespeed=Velocidad media
-details.range.avemovingspeed=Moviendo promedio
 details.range.maxspeed=Velocidad m\u00e1xima
 details.range.numsegments=N\u00famero de segmentos
 details.range.pace=Ritmo
@@ -626,12 +623,11 @@ units.feet=Pies
 units.feet.short=ft
 units.kilometres=Kil\u00f3metros
 units.kilometres.short=km
-units.kmh=km/h
 units.miles=Millas
 units.miles.short=mi
-units.mph=mi/h
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mi/h
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=horas
 units.degminsec=Gra-min-seg
 units.degmin=Gra-min
@@ -651,13 +647,13 @@ cardinal.w=O
 # Undo operations
 undo.load=cargar datos
 undo.loadphotos=cargar fotos
-undo.loadaudios=Cargar archivos de audio
+undo.loadaudios=cargar archivos de audio
 undo.editpoint=editar punto
 undo.deletepoint=eliminar punto
 undo.removephoto=eliminar foto
-undo.removeaudio=Eliminar archivos de audio
+undo.removeaudio=eliminar archivos de audio
 undo.deleterange=eliminar rango
-undo.compress=comprimir track
+undo.deletemarked=eliminar puntos
 undo.insert=insertar puntos
 undo.reverse=invertir rango
 undo.mergetracksegments=unir los segmentos de track
@@ -665,16 +661,16 @@ undo.addtimeoffset=a\u00f1adir margen de tiempo
 undo.addaltitudeoffset=a\u00f1adir margen de altitud
 undo.rearrangewaypoints=reordenar waypoints
 undo.cutandmove=mover secci\u00f3n
-undo.connect=Conectar
-undo.disconnect=Desconectar
+undo.connect=conectar
+undo.disconnect=desconectar
 undo.correlatephotos=correlacionar fotos
 undo.rearrangephotos=reordenar fotos
 undo.createpoint=crear punto
 undo.rotatephoto=girar foto
 undo.convertnamestotimes=convertir nombres a tiempo
 undo.lookupsrtm=obtener altitudes de SRTM
-undo.deletefieldvalues=Eliminar valores de campo
-undo.correlateaudios=Correlacionar audios
+undo.deletefieldvalues=eliminar valores de campo
+undo.correlateaudios=correlacionar audios
 
 # Error messages
 error.save.dialogtitle=Fallo al guardar datos
index 2e9d558f0e63fca59936e87a219b8884a75d745f..960330d4f8a44c25dbca8c9fb7df2e56799cb868 100644 (file)
@@ -19,8 +19,8 @@ menu.range.all=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u0645\u0647 \u0646\u0
 menu.range.none=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u064a\u0686 \u064a\u06a9 \u0627\u0632 \u0646\u0642\u0627\u0637
 menu.range.start=\u062a\u0646\u0638\u064a\u0645 \u0634\u0631\u0648\u0639 \u0686\u064a\u0646\u0634
 menu.range.end=\u062a\u0646\u0638\u064a\u0645 \u0627\u0646\u062a\u0647\u0627\u06cc \u0686\u064a\u0646\u0634
-menu.range.deleterange=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0686\u064a\u0646\u0634
-menu.range.interpolate=\u062f\u0631\u0648\u0646\u064a\u0627\u0628\u06cc
+function.deleterange=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0686\u064a\u0646\u0634
+function.interpolate=\u062f\u0631\u0648\u0646\u064a\u0627\u0628\u06cc
 menu.range.average=\u0645\u06cc\u0627\u0646\u06af\u064a\u0646 \u0627\u0646\u062a\u062e\u0627\u0628 \u0634\u062f\u0647 \u0647\u0627
 menu.range.reverse=\u0686\u064a\u0646\u0634 \u0645\u0639\u06a9\u0648\u0633
 menu.range.mergetracksegments=\u0627\u062a\u0635\u0627\u0644 \u0642\u0633\u0645\u062a \u0647\u0627\u06cc \u0645\u0633\u064a\u0631
index 1e0164b3a87def438f01c537164dbee4792e64cb..1b0364ce8e600bd0a2c263fc43f9dbe0be6e56fd 100644 (file)
@@ -1,10 +1,10 @@
 # Text entries for the GpsPrune application
-# French entries as extra
+# French entries
 
 # Menu entries
 menu.file=Fichier
 menu.file.addphotos=Ajouter photos
-menu.file.recentfiles=Derniers Fichiers utilis\u00e9s
+menu.file.recentfiles=Fichiers r\u00e9cents
 menu.file.save=Enregistrer
 menu.file.exit=Quitter
 menu.track=Trace
@@ -20,8 +20,6 @@ menu.range.all=Tout s\u00e9lectionner
 menu.range.none=Rien s\u00e9lectionner
 menu.range.start=D\u00e9finir le d\u00e9but de l'\u00e9tendue
 menu.range.end=D\u00e9finir la fin de l'\u00e9tendue
-menu.range.deleterange=Supprimer l'\u00e9tendue
-menu.range.interpolate=Interpoler
 menu.range.average=Cr\u00e9er un point pour la s\u00e9lection
 menu.range.reverse=Inverser l'\u00e9tendue
 menu.range.mergetracksegments=Fusionner les segments de trace
@@ -77,6 +75,7 @@ shortcut.menu.help.help=A
 
 # Functions
 function.open=Ouvrir fichier
+function.importwithgpsbabel=Importer fichier avec GPSBabel
 function.loadfromgps=T\u00e9l\u00e9charger donn\u00e9es du GPS
 function.sendtogps=Envoyer donn\u00e9es au GPS
 function.exportkml=Exporter en KML
@@ -85,6 +84,8 @@ function.exportpov=Exporter en POV
 function.exportsvg=Exporter en SVG
 function.editwaypointname=\u00c9diter le nom du waypoint
 function.compress=Compresser la trace
+function.deleterange=Supprimer l'\u00e9tendue
+function.interpolate=Interpoler les points
 function.addtimeoffset=Ajouter un d\u00e9calage d'horaire
 function.addaltitudeoffset=Ajouter un d\u00e9calage d'altitude
 function.convertnamestotimes=Convertir les noms de waypoints en horodatages
@@ -128,6 +129,7 @@ function.about=\u00c0 propos de GpsPrune
 function.checkversion=Chercher une mise \u00e0 jour
 function.saveconfig=Enregistrer les pr\u00e9f\u00e9rences
 function.diskcache=Enregistrer les cartes sur le disque
+function.managetilecache=Gestion du cache des tuiles de cartes
 
 # Dialogs
 dialog.exit.confirm.title=Quitter GpsPrune
@@ -191,7 +193,8 @@ dialog.exportgpx.name=Nom
 dialog.exportgpx.desc=L\u00e9gende
 dialog.exportgpx.includetimestamps=Inclure l'heure pour chaque point
 dialog.exportgpx.copysource=Copier la source xml
-dialog.exportgpx.encoding.system=Syst\u00e8me
+dialog.exportgpx.encoding=Encodage
+dialog.exportgpx.encoding.system=Encodage syst\u00e8me
 dialog.exportgpx.encoding.utf8=UTF-8
 dialog.exportpov.text=Entrez les param\u00e8tres pour l'export POV
 dialog.exportpov.font=Police
@@ -216,7 +219,6 @@ dialog.confirmreversetrack.title=Confirmer l'inversion
 dialog.confirmreversetrack.text=Cette trace contient des informations temporelles qui seront d\u00e9sordonn\u00e9es apr\u00e8s une inversion.\n\u00cates-vous s\u00fbr de vouloir inverser cette section ?
 dialog.confirmcutandmove.title=Confirmer le d\u00e9placement
 dialog.confirmcutandmove.text=Cette trace contient des informations temporelles qui seront d\u00e9sordonn\u00e9es apr\u00e8s un d\u00e9placement.\n\u00cates-vous s\u00fbr de vouloir d\u00e9placer cette section ?
-dialog.interpolate.title=Interpoler les points
 dialog.interpolate.parameter.text=Nombre de points \u00e0 ins\u00e9rer entre les points s\u00e9lectionn\u00e9s
 dialog.undo.title=Annuler les actions
 dialog.undo.pretext=S\u00e9lectionnez les actions \u00e0 annuler
@@ -234,7 +236,7 @@ dialog.pointedit.changevalue.title=\u00c9diter le champ
 dialog.pointnameedit.name=Nom de waypoint
 dialog.pointnameedit.uppercase=CASSE MAJUSCULES
 dialog.pointnameedit.lowercase=casse minuscules
-dialog.pointnameedit.sentencecase=Casse Phrase
+dialog.pointnameedit.titlecase=Casse Phrase
 dialog.addtimeoffset.add=Retarder l'heure
 dialog.addtimeoffset.subtract=Avancer l'heure
 dialog.addtimeoffset.days=Jours
@@ -337,7 +339,6 @@ 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.compress.nonefound=Pas de donn\u00e9es \u00e0 effacer
 dialog.compress.closepoints.title=Suppression des points voisins
 dialog.compress.closepoints.paramdesc=Taille du voisinage
 dialog.compress.wackypoints.title=Suppression des points anormaux
@@ -348,6 +349,7 @@ dialog.compress.duplicates.title=Suppression des doublons
 dialog.compress.douglaspeucker.title=Compression Douglas-Peucker
 dialog.compress.douglaspeucker.paramdesc=Taille du voisinage
 dialog.compress.summarylabel=Points \u00e0 supprimer
+dialog.deletemarked.nonefound=Pas de donn\u00e9es \u00e0 effacer
 dialog.pastecoordinates.desc=Entrez ou collez les coordonn\u00e9es ici
 dialog.pastecoordinates.coords=Coordonn\u00e9es
 dialog.pastecoordinates.nothingfound=V\u00e9rifier les coordonn\u00e9es et essayez \u00e0 nouveau
@@ -403,7 +405,6 @@ dialog.saveconfig.prune.languagefile=Fichier de langue
 dialog.saveconfig.prune.gpsdevice=Chemin du p\u00e9riph\u00e9rique GPS
 dialog.saveconfig.prune.gpsformat=Format GPS
 dialog.saveconfig.prune.povrayfont=Police povray
-dialog.saveconfig.prune.metricunits=Utiliser le syst\u00e8me m\u00e9trique ?
 dialog.saveconfig.prune.gnuplotpath=Chemin gnuplot
 dialog.saveconfig.prune.gpsbabelpath=Chemin gpsbabel
 dialog.saveconfig.prune.exiftoolpath=Chemin exiftool
@@ -444,6 +445,16 @@ dialog.diskcache.save=Enregistrer les images de carte sur le disque
 dialog.diskcache.dir=R\u00e9pertoire cache
 dialog.diskcache.createdir=Cr\u00e9er r\u00e9pertoire
 dialog.diskcache.nocreate=Le r\u00e9pertoire cache n'est pas cr\u00e9\u00e9
+dialog.diskcache.table.path=Chemin
+dialog.diskcache.table.usedby=Utilis\u00e9 par
+dialog.diskcache.table.zoom=Amplitude du zoom
+dialog.diskcache.table.tiles=Tuiles
+dialog.diskcache.table.megabytes=Megabytes
+dialog.diskcache.tileset.multiple=multiple
+dialog.diskcache.deleteold=Efface vieilles tuiles
+dialog.diskcache.deleteall=Efface toute les tuiles
+dialog.diskcache.deleted1=Effac\u00e9
+dialog.diskcache.deleted2=tuiles du cache
 dialog.deletefieldvalues.intro=Choisir le champ \u00e0 effacer pour l'\u00e9tendue actuelle
 dialog.setlinewidth.text=Entrer l'\u00e9paisseur des lignes des traces (1-4)
 dialog.downloadosm.desc=Confirmer le t\u00e9l\u00e9chargement des donn\u00e9es OSM brutes pour la zone indiqu\u00e9e :
@@ -524,6 +535,7 @@ button.resettodefaults=Revenir aux valeurs par d\u00e9faut
 button.browse=Naviguer...
 button.addnew=Ajouter nouveau...
 button.delete=Supprimer
+button.manage=G\u00e9rer
 
 # File types
 filetype.txt=Fichiers TXT
@@ -564,7 +576,6 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=j
 details.range.avespeed=Vitesse moyenne
-details.range.avemovingspeed=Moyenne continue
 details.range.maxspeed=Vitesse maximum
 details.range.numsegments=Nombre de segments
 details.range.pace=Allure
@@ -575,6 +586,7 @@ details.lists.audio=Audio
 details.photodetails=D\u00e9tails de la photo
 details.nophoto=Pas de photo
 details.photo.loading=Chargement
+details.photo.bearing=Direction
 details.media.connected=Reli\u00e9e
 details.audiodetails=D\u00e9tails de l'audio
 details.noaudio=Pas de fichier audio s\u00e9lectionner
@@ -609,12 +621,12 @@ units.feet=pieds
 units.feet.short=p
 units.kilometres=Kilom\u00e8tres
 units.kilometres.short=km
-units.kmh=km/h
+units.kilometresperhour.short=km/h
 units.miles=Miles
 units.miles.short=mi
-units.mph=mi/h
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mi/h
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=heures
 units.degminsec=Deg-min-sec
 units.degmin=Deg-min
@@ -640,7 +652,7 @@ undo.deletepoint=effacer le point
 undo.removephoto=retirer la photo
 undo.removeaudio=retirer le fichier audio
 undo.deleterange=effacer l'\u00e9tendue
-undo.compress=compresser la trace
+undo.deletemarked=effacer les points
 undo.insert=ins\u00e9rer les points
 undo.reverse=inverser l'\u00e9tendue
 undo.mergetracksegments=fusionner les segments de trace
@@ -700,3 +712,6 @@ error.lookupsrtm.nonerequired=Tous les points ont d\u00e9j\u00e0 une altitude, i
 error.gpsies.uploadnotok=Le serveur de Gpsies \u00e0 renvoy\u00e9 le message
 error.gpsies.uploadfailed=L'envoi a \u00e9chou\u00e9 avec l'erreur
 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=Ne peux pas effac\u00e9 les tuiles
index da5b1b6e5a554af35f1fba17e6911ea691f862cd..837f9d9decf02318d9d028089261b6e4612f684b 100644 (file)
@@ -20,8 +20,8 @@ menu.range.all=Mindet kijel\u00f6l
 menu.range.none=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
 menu.range.start=Tartom\u00e1ny kezdet\u00e9nek be\u00e1ll\u00edt\u00e1sa
 menu.range.end=Tartom\u00e1ny v\u00e9g\u00e9nek be\u00e1ll\u00edt\u00e1sa
-menu.range.deleterange=Tartom\u00e1ny t\u00f6rl\u00e9se
-menu.range.interpolate=Interpol\u00e1ci\u00f3
+function.deleterange=Tartom\u00e1ny t\u00f6rl\u00e9se
+function.interpolate=Pontok interpol\u00e1l\u00e1sa
 menu.range.average=Kijel\u00f6l\u00e9s \u00e1tlaga
 menu.range.reverse=Tartom\u00e1ny megford\u00edt\u00e1sa
 menu.range.mergetracksegments=Nyomvonalszakaszok egyes\u00edt\u00e9se
@@ -219,7 +219,6 @@ dialog.confirmreversetrack.title=Megford\u00edt\u00e1s meger\u0151s\u00edt\u00e9
 dialog.confirmreversetrack.text=Ez a nyomvonal id\u0151b\u00e9lyeg-inform\u00e1ci\u00f3t tartalmaz, amely sorrendje megford\u00edt\u00e1s ut\u00e1n megv\u00e1ltozik.\n Biztos benne, hogy megford\u00edtja a kijel\u00f6l\u00e9st?
 dialog.confirmcutandmove.title=Kiv\u00e1g\u00e1s \u00e9s mozgat\u00e1s meger\u0151s\u00edt\u00e9se
 dialog.confirmcutandmove.text=Ez a nyomvonal id\u0151b\u00e9lyeg-inform\u00e1ci\u00f3t tartalmaz, amely sorrendje mozgat\u00e1s ut\u00e1n megv\u00e1ltozik.\n Biztos benne, hogy mozgatja a kijel\u00f6l\u00e9st?
-dialog.interpolate.title=Pontok interpol\u00e1l\u00e1sa
 dialog.interpolate.parameter.text=Pontok sz\u00e1ma, amely a k\u00e9t kiv\u00e1lasztott pont k\u00f6z\u00e9 besz\u00farand\u00f3
 dialog.undo.title=M\u0171velet(ek) visszavon\u00e1sa
 dialog.undo.pretext=V\u00e1lassza ki a visszavonand\u00f3 m\u0171velet(ek)et
@@ -237,7 +236,7 @@ dialog.pointedit.changevalue.title=Mez\u0151 szerkeszt\u00e9se
 dialog.pointnameedit.name=\u00datpont neve
 dialog.pointnameedit.uppercase=NAGYBET\u0170S
 dialog.pointnameedit.lowercase=kisbet\u0171s
-dialog.pointnameedit.sentencecase=Nagy Kezd\u0151bet\u0171s
+dialog.pointnameedit.titlecase=Nagy Kezd\u0151bet\u0171s
 dialog.addtimeoffset.add=Id\u0151 hozz\u00e1ad\u00e1sa
 dialog.addtimeoffset.subtract=Id\u0151 kivon\u00e1sa
 dialog.addtimeoffset.days=Nap
@@ -340,7 +339,7 @@ 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.compress.nonefound=Nem t\u00e1vol\u00edthat\u00f3 el adatpont
+dialog.deletemarked.nonefound=Nem t\u00e1vol\u00edthat\u00f3 el adatpont
 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
@@ -406,7 +405,6 @@ dialog.saveconfig.prune.languagefile=Nyelvi f\u00e1jl
 dialog.saveconfig.prune.gpsdevice=GPS eszk\u00f6z
 dialog.saveconfig.prune.gpsformat=GPS form\u00e1tum
 dialog.saveconfig.prune.povrayfont=Povray bet\u0171t\u00edpus
-dialog.saveconfig.prune.metricunits=Metrikus m\u00e9rt\u00e9krendszer haszn\u00e1lata?
 dialog.saveconfig.prune.gnuplotpath=\u00datvonal a gnuplothoz
 dialog.saveconfig.prune.gpsbabelpath=\u00datvonal a gpsbabelhez
 dialog.saveconfig.prune.exiftoolpath=\u00datvonal az exiftoolhoz
@@ -580,7 +578,6 @@ display.range.time.mins=p
 display.range.time.hours=\u00f3
 display.range.time.days=n
 details.range.avespeed=\u00c1tlagsebess\u00e9g
-details.range.avemovingspeed=Mozg\u00e1si \u00e1tlag
 details.range.maxspeed=Maxim\u00e1lis sebess\u00e9g
 details.range.numsegments=Szakaszok sz\u00e1ma
 details.range.pace=Iram
@@ -626,12 +623,11 @@ units.feet=l\u00e1b
 units.feet.short=ft
 units.kilometres=kilom\u00e9ter
 units.kilometres.short=km
-units.kmh=km/h
 units.miles=m\u00e9rf\u00f6ld
 units.miles.short=mi
-units.mph=mph
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mph
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=\u00f3ra
 units.degminsec=Sz\u00f6g-sz\u00f6gperc-sz\u00f6gm\u00e1sodperc
 units.degmin=Sz\u00f6g-sz\u00f6gperc
@@ -657,7 +653,7 @@ undo.deletepoint=pont t\u00f6rl\u00e9se
 undo.removephoto=f\u00e9nyk\u00e9p elt\u00e1vol\u00edt\u00e1sa
 undo.removeaudio=hangf\u00e1jl elt\u00e1vol\u00edt\u00e1sa
 undo.deleterange=tartom\u00e1ny t\u00f6rl\u00e9se
-undo.compress=nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se
+undo.deletemarked=nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se
 undo.insert=pontok besz\u00far\u00e1sa
 undo.reverse=tartom\u00e1ny megford\u00edt\u00e1sa
 undo.mergetracksegments=nyomvonalszakaszok egyes\u00edt\u00e9se
index ec4bc255e678a54492e37ce5fb00999801161e98..c0b42460bfa8c0918b824be473990e4c53807c22 100644 (file)
@@ -10,7 +10,7 @@ menu.track=Track
 menu.track.undo=Batal
 menu.point.editpoint=Perbaiki titik
 menu.point.deletepoint=Hapus titik
-menu.range.deleterange=Hapus jarak
+function.deleterange=Hapus jarak
 menu.range=Jangkauan
 menu.point=Titik
 menu.range.all=Pilih semua
index a15ceb19fb7289cb5390fa8eb13c94a4d39d4497..989110a6b4820a0fce93565e81bb02004b86aa96 100644 (file)
@@ -1,5 +1,5 @@
 # Text entries for the GpsPrune application
-# Italian entries thanks to josatoc and others
+# Italian entries thanks to josatoc, denisov
 
 # Menu entries
 menu.file=File
@@ -10,6 +10,7 @@ menu.file.exit=Esci
 menu.track=Traccia
 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
@@ -20,8 +21,6 @@ menu.range.all=Seleziona tutto
 menu.range.none=Deseleziona tutto
 menu.range.start=Imposta inizio serie
 menu.range.end=Imposta fine serie
-menu.range.deleterange=Cancella la serie
-menu.range.interpolate=Interpola
 menu.range.average=Crea punto medio della selezione
 menu.range.reverse=Inverti la serie
 menu.range.mergetracksegments=Unisci segmenti traccia
@@ -54,6 +53,7 @@ menu.map.connect=Aggancia ai punti
 menu.map.autopan=Autopan
 menu.map.showmap=Mostra sulla mappa
 menu.map.showscalebar=Mostra scala
+menu.map.editmode=Modifica
 
 # Alt keys for menus
 altkey.menu.file=F
@@ -86,6 +86,9 @@ function.exportpov=Esporta in POV
 function.exportsvg=Esporta in SVG
 function.editwaypointname=Modifica nome waypoint
 function.compress=Comprimi la traccia
+function.deleterange=Cancella la serie
+function.croptrack=Cima la traccia
+function.interpolate=Interpola i punti
 function.addtimeoffset=Aggiungi uno scarto temporale
 function.addaltitudeoffset=Aggiungi uno scarto di altitudine
 function.convertnamestotimes=Converti nomi dei waypoint in orari
@@ -140,6 +143,7 @@ dialog.deletepoint.title=Cancella Punto
 dialog.deletepoint.deletephoto=Cancella la foto collegata a questo punto?
 dialog.deletephoto.title=Cancella Foto
 dialog.deletephoto.deletepoint=Cancella il punto collegato a questa foto?
+dialog.deleteaudio.deletepoint=Elimina il punto collegato a questo file audio?
 dialog.openoptions.title=Apri opzioni
 dialog.openoptions.filesnippet=Estrai dal file
 dialog.load.table.field=Campo
@@ -219,8 +223,8 @@ dialog.confirmreversetrack.title=Conferma l'inversione
 dialog.confirmreversetrack.text=Questa traccia contiene informazioni sull'orario di scatto che possono essere messe fuori sequenza dopo l'inversione.\nSei sicuro di voler invertire questa sezione?
 dialog.confirmcutandmove.title=Conferma il taglio e lo spostamento
 dialog.confirmcutandmove.text=Questa traccia contiene informazioni sull'orario di scatto che possono essere messe fuori sequenza dopo lo spostamento\nSei sicuro di voler spostare questa sezione?
-dialog.interpolate.title=Interpola i punti
 dialog.interpolate.parameter.text=Numero di punti da inserire tra i punti selezionati
+dialog.interpolate.betweenwaypoints=Interpolazione tra waypoint?
 dialog.undo.title=Annulla l'azione(i)
 dialog.undo.pretext=Per favore seleziona l'azione(i) da annullare
 dialog.undo.none.title=Non \u00e8 possibile annullare
@@ -237,7 +241,7 @@ dialog.pointedit.changevalue.title=Modifica il campo
 dialog.pointnameedit.name=Nome del waypoint
 dialog.pointnameedit.uppercase=MAIUSCOLE
 dialog.pointnameedit.lowercase=minuscole
-dialog.pointnameedit.sentencecase=Iniziali Maiuscole
+dialog.pointnameedit.titlecase=Iniziali Maiuscole
 dialog.addtimeoffset.add=Scarto in aggiunta
 dialog.addtimeoffset.subtract=Scarto in sottrazione
 dialog.addtimeoffset.days=Giorni
@@ -273,6 +277,8 @@ dialog.distances.column.to=Al punto
 dialog.distances.currentpoint=Punto attuale
 dialog.distances.toofewpoints=Questa funzione necessita di waypoints per calcolare le distanze tra loro
 dialog.fullrangedetails.intro=Qui i dettagli della selezione
+dialog.fullrangedetails.coltotal=Includere le lacune
+dialog.fullrangedetails.colsegments=Senza lacune
 dialog.setmapbg.intro=Selezione una fonte delle mappe o aggiungine una nuova
 dialog.addmapsource.title=Aggiungi nuova fonte delle mappa
 dialog.addmapsource.sourcename=Nome della fonte
@@ -303,6 +309,7 @@ dialog.wikipedia.column.name=Titolo articolo
 dialog.wikipedia.column.distance=Distanza
 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?
 dialog.correlate.photoselect.intro=Selezione una delle foto correlate da usare come scarto dell'orario
 dialog.correlate.select.photoname=Nome della foto
 dialog.correlate.select.timediff=Differenza di orario
@@ -340,7 +347,6 @@ 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.compress.nonefound=Nessun punto rimosso
 dialog.compress.closepoints.title=Cancella punti vicini
 dialog.compress.closepoints.paramdesc=Fattore vicinanza
 dialog.compress.wackypoints.title=Cancella punti strani
@@ -349,7 +355,12 @@ dialog.compress.singletons.title=Cancella solitari
 dialog.compress.singletons.paramdesc=Fattore distanza
 dialog.compress.duplicates.title=Cancella duplicati
 dialog.compress.douglaspeucker.title=Compressione con algoritmo Douglas-Peucker
+dialog.compress.douglaspeucker.paramdesc=Fattore di apertura
 dialog.compress.summarylabel=Punti da cancellare
+dialog.compress.confirm1=
+dialog.compress.confirm2=punti sono stati contrassegnati.\nUsa traccia-> Elimina i punti segnati per eliminarle
+dialog.compress.confirmnone=I punti non sono stati marcati
+dialog.deletemarked.nonefound=Nessun punto rimosso
 dialog.pastecoordinates.desc=Inserisci o incolla qui le coordinate
 dialog.pastecoordinates.coords=Coordinate
 dialog.pastecoordinates.nothingfound=Per favore, controlla le coordinate e riprova
@@ -405,7 +416,6 @@ dialog.saveconfig.prune.languagefile=File lingua
 dialog.saveconfig.prune.gpsdevice=Nome del Dispositivo GPS
 dialog.saveconfig.prune.gpsformat=Formato GPS
 dialog.saveconfig.prune.povrayfont=Font povray
-dialog.saveconfig.prune.metricunits=Utilizza unita' metrica?
 dialog.saveconfig.prune.gnuplotpath=Path gnuplot
 dialog.saveconfig.prune.gpsbabelpath=Path gpsbabel
 dialog.saveconfig.prune.exiftoolpath=Path exiftool
@@ -446,6 +456,7 @@ dialog.diskcache.save=Salva la mappa sul disco
 dialog.diskcache.dir=Cartella della cache
 dialog.diskcache.createdir=Crea cartella
 dialog.diskcache.nocreate=Cartella della cache non creata
+dialog.diskcache.cannotwrite=Il titolo della mappa non pu\u00f2 essere salvato nella cartella selezionata
 dialog.diskcache.table.path=Percorso (Path)
 dialog.diskcache.table.usedby=Utilizzato da
 dialog.diskcache.table.zoom=Zoom
@@ -484,6 +495,7 @@ confirm.addaltitudeoffset=Scarto altitudine aggiunto
 confirm.rearrangewaypoints=Waypoint riorganizzati
 confirm.rearrangephotos=Foto riorganizzate
 confirm.cutandmove=Selezione spostata
+confirm.interpolate=Aggiungi punto
 confirm.convertnamestotimes=Nome del waypoint convertito
 confirm.saveexif.ok1=Salvato
 confirm.saveexif.ok2=foto
@@ -579,7 +591,6 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=g
 details.range.avespeed=Velocit\u00e0 media
-details.range.avemovingspeed=Velocit\u00e0 media in movimento
 details.range.maxspeed=Velocit\u00e0 massima
 details.range.numsegments=Numero di segmenti
 details.range.pace=Passo
@@ -592,6 +603,7 @@ details.nophoto=Nessuna foto selezionata
 details.photo.loading=Caricamento
 details.photo.bearing=Direzione
 details.media.connected=Collegata
+details.media.fullpath=Percorso completo
 details.audiodetails=Dettagli ripresa audio
 details.noaudio=Nessuna ripresa audio selezionata
 details.audio.file=Ripresa audio
@@ -625,12 +637,15 @@ units.feet=Feet
 units.feet.short=ft
 units.kilometres=Kilometri
 units.kilometres.short=km
-units.kmh=km/h
+units.kilometresperhour.short=km/h
 units.miles=Miglia
 units.miles.short=mi
-units.mph=mph
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mph
+units.nauticalmiles=Miglia nautiche
+units.nauticalmiles.short=N.m
+units.nauticalmilesperhour.short=Miglia nautiche all'ora
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=Ore
 units.degminsec=Deg-min-sec
 units.degmin=Deg-min
@@ -656,7 +671,8 @@ undo.deletepoint=cancella punto
 undo.removephoto=rimuovi foto
 undo.removeaudio=rimuovi riprese audio
 undo.deleterange=cancella l'intervallo
-undo.compress=comprimi traccia
+undo.croptrack=taglia la traccia
+undo.deletemarked=
 undo.insert=inserisci punti
 undo.reverse=inverti l'intervallo
 undo.mergetracksegments=unisci segmenti traccia
@@ -715,7 +731,9 @@ error.lookupsrtm.nonefound=Valori di quota non trovati
 error.lookupsrtm.nonerequired=Tutti i punti hanno gi\u00e0 una quota, non c'\u00e8 niente da cercare
 error.gpsies.uploadnotok=Il server Gpsies ha riportato il messaggio
 error.gpsies.uploadfailed=Il caricamento \u00e8 fallito con l'errore
+error.showphoto.failed=Caricamento foto fallito
 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
index 30c3556bcb3a9be9f014e4605807b080a81dcfc9..ada2387cb5afb88bc81b4dee346ca4213d104720 100644 (file)
@@ -1,52 +1,51 @@
 # Text entries for the GpsPrune application
-# Japanese entries as extra
+# Japanese entries
 
 # Menu entries
-menu.file=\u30d5\u30a1\u30a4\u30eb
+menu.file=\u30d5\u30a1\u30a4\u30eb(F)
 menu.file.addphotos=\u5199\u771f\u3092\u8ffd\u52a0
 menu.file.save=\u4fdd\u5b58
 menu.file.exit=\u7d42\u4e86
-menu.track=\u30c8\u30e9\u30c3\u30af
+menu.track=\u30c8\u30e9\u30c3\u30af(T)
 menu.track.undo=\u30a2\u30f3\u30c9\u30a5
 menu.track.clearundo=\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3059\u308b
-menu.point.editpoint=\u70b9\u3092\u7de8\u96c6
-menu.point.deletepoint=\u70b9\u3092\u524a\u9664
-menu.range.deleterange=\u7bc4\u56f2\u3092\u524a\u9664
 menu.track.deletemarked=\u5370\u306e\u4ed8\u3044\u305f\u70b9\u3092\u524a\u9664
-menu.range.interpolate=\u88dc\u5b8c
-menu.range.average=\u9078\u629e\u7bc4\u56f2\u3092\u5e73\u5747\u5316
-menu.range.reverse=\u7bc4\u56f2\u3092\u53cd\u8ee2
-menu.range.mergetracksegments=\u30c8\u30e9\u30c3\u30af\u30bb\u30b0\u30e1\u30f3\u30c8\u3092\u7d71\u5408
 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
-menu.range.cutandmove=\u9078\u629e\u7bc4\u56f2\u3092\u79fb\u52d5
-menu.range=\u7bc4\u56f2
-menu.point=\u70b9
+menu.range=\u7bc4\u56f2(R)
 menu.range.all=\u5168\u3066\u9078\u629e
 menu.range.none=\u9078\u629e\u89e3\u9664
-menu.range.start=\u958b\u59cb\u70b9\u3092\u7f6e\u304f
-menu.range.end=\u7d42\u4e86\u70b9\u3092\u7f6e\u304f
+menu.range.start=\u958b\u59cb\u70b9\u3092\u8a2d\u5b9a
+menu.range.end=\u7d42\u4e86\u70b9\u3092\u8a2d\u5b9a
+menu.range.average=\u9078\u629e\u7bc4\u56f2\u3092\u5e73\u5747\u5316
+menu.range.reverse=\u7bc4\u56f2\u3092\u53cd\u8ee2
+menu.range.mergetracksegments=\u30c8\u30e9\u30c3\u30af\u30bb\u30b0\u30e1\u30f3\u30c8\u3092\u7d71\u5408
+menu.range.cutandmove=\u9078\u629e\u7bc4\u56f2\u3092\u79fb\u52d5
+menu.point=\u70b9(P)
+menu.point.editpoint=\u70b9\u3092\u7de8\u96c6
+menu.point.deletepoint=\u70b9\u3092\u524a\u9664
 menu.photo=\u5199\u771f
 menu.photo.saveexif=Exif\u306b\u4fdd\u5b58
-function.connecttopoint=\u70b9\u306b\u63a5\u7d9a
-function.disconnectfrompoint=\u70b9\u304b\u3089\u63a5\u7d9a\u89e3\u9664
-function.removephoto=\u5199\u771f\u3092\u53d6\u308a\u9664\u304f
-menu.view=\u30d3\u30e5\u30fc
+menu.audio=\u30aa\u30fc\u30c7\u30a3\u30aa(A)
+menu.view=\u30d3\u30e5\u30fc(V)
+menu.view.showsidebars=\u30b5\u30a4\u30c9\u30d0\u30fc\u3092\u8868\u793a
 menu.view.browser=\u5730\u56f3\u3092\u30d6\u30e9\u30a6\u30b6\u30fc\u3067\u898b\u308b
 menu.view.browser.google=Google \u30de\u30c3\u30d7
 menu.view.browser.openstreetmap=OpenStreetMap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Yahoo \u5730\u56f3
 menu.view.browser.bing=Bing \u5730\u56f3
-menu.settings=\u8a2d\u5b9a
-menu.help=\u30d8\u30eb\u30d7
+menu.settings=\u8a2d\u5b9a(S)
+menu.settings.onlinemode=\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u304b\u3089\u5730\u56f3\u3092\u30ed\u30fc\u30c9
+menu.help=\u30d8\u30eb\u30d7(H)
 # Popup menu for map
 menu.map.zoomin=\u62e1\u5927
 menu.map.zoomout=\u7e2e\u5c0f
 menu.map.zoomfull=\u6700\u5927\u62e1\u5927
 menu.map.newpoint=\u70b9\u3092\u4f5c\u308b
+menu.map.drawpoints=\u9023\u7d9a\u3057\u305f\u70b9\u3092\u4f5c\u308b
 menu.map.connect=\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306b\u63a5\u7d9a
 menu.map.autopan=\u81ea\u52d5\u79fb\u52d5
 menu.map.showmap=\u5730\u56f3\u3092\u8868\u793a
@@ -59,11 +58,15 @@ function.sendtogps=GPS\u3078\u4fdd\u5b58
 function.exportkml=KML\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
 function.exportgpx=GPX\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
 function.exportpov=POV\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
+function.exportsvg=SVG\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
 function.editwaypointname=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u306e\u540d\u524d\u3092\u7de8\u96c6
+function.deleterange=\u7bc4\u56f2\u3092\u524a\u9664
+function.interpolate=\u70b9\u3092\u88dc\u5b8c\u3059\u308b
 function.compress=\u30c8\u30e9\u30c3\u30af\u3092\u5727\u7e2e
 function.addtimeoffset=\u6642\u9593\u306e\u504f\u4f4d\u3092\u52a0\u3048\u308b
 function.addaltitudeoffset=\u9ad8\u5ea6\u306b\u504f\u4f4d\u3092\u52a0\u3048\u308b
 function.convertnamestotimes=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u540d\u3092\u6642\u9593\u306b\u5909\u63db
+function.deletefieldvalues=\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u5024\u3092\u524a\u9664\u3059\u308b
 function.findwaypoint=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u63a2\u3059
 function.pastecoordinates=\u65b0\u3057\u3044\u5ea7\u6a19\u3092\u5165\u529b
 function.charts=\u9ad8\u5ea6\u901f\u5ea6\u30c1\u30e3\u30fc\u30c8
@@ -74,19 +77,35 @@ function.setmapbg=\u80cc\u666f\u5730\u56f3
 function.setkmzimagesize=KML \u30a4\u30e1\u30fc\u30b8\u30b5\u30a4\u30ba
 function.setpaths=\u5916\u90e8\u30d7\u30ed\u30b0\u30e9\u30e0\u30d1\u30b9\u3092\u8a2d\u5b9a
 function.getgpsies=Gpsies\u30c8\u30e9\u30c3\u30af\u3092\u5f97\u308b
+function.uploadgpsies=Gpsies\u306b\u30c8\u30e9\u30c3\u30af\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9
+function.lookupsrtm=SRTM\u304b\u3089\u6a19\u9ad8\u3092\u53d6\u5f97\u3059\u308b
+function.getwikipedia=Wikipedia\u304b\u3089\u5468\u56f2\u306e\u8a18\u4e8b\u3092\u53d6\u5f97\u3059\u308b
+function.searchwikipedianames=\u540d\u524d\u3067Wikipedia\u3092\u691c\u7d22
+function.downloadosm=\u30a8\u30ea\u30a2\u306eOSM\u30c7\u30fc\u30bf\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9
 function.duplicatepoint=\u91cd\u8907\u70b9
 function.setcolours=\u8272\u3092\u8a2d\u5b9a
+function.setlinewidth=\u884c\u306e\u5e45\u3092\u8a2d\u5b9a
 function.setlanguage=\u8a00\u8a9e\u8a2d\u5b9a
+function.connecttopoint=\u70b9\u306b\u63a5\u7d9a
+function.disconnectfrompoint=\u70b9\u304b\u3089\u63a5\u7d9a\u89e3\u9664
+function.removephoto=\u5199\u771f\u3092\u53d6\u308a\u9664\u304f
 function.correlatephotos=\u5199\u771f\u3068\u95a2\u9023\u3055\u305b\u308b
 function.rearrangephotos=\u5199\u771f\u306e\u4e26\u3079\u76f4\u3057
 function.rotatephotoleft=\u5199\u771f\u3092\u5de6\u306b\u56de\u3059
 function.rotatephotoright=\u5199\u771f\u3092\u53f3\u306b\u56de\u3059
+function.photopopup=\u5199\u771f\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3092\u8868\u793a
 function.ignoreexifthumb=EXIF\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u7121\u8996
+function.loadaudio=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0
+function.removeaudio=\u4e00\u89a7\u304b\u3089\u73fe\u5728\u306e\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u3092\u524a\u9664
+function.correlateaudios=\u30aa\u30fc\u30c7\u30a3\u30aa\u3092\u95a2\u9023\u4ed8\u3051\u308b
+function.playaudio=\u518d\u751f
+function.stopaudio=\u505c\u6b62
 function.help=\u30d8\u30eb\u30d7
 function.showkeys=\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\u30ad\u30fc\u3092\u8868\u793a
 function.about=GpsPrune \u306b\u3064\u3044\u3066
 function.checkversion=\u65b0\u3057\u3044\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u8868\u793a
 function.saveconfig=\u8a2d\u5b9a\u3092\u4fdd\u5b58
+function.diskcache=\u30c7\u30a3\u30b9\u30af\u306b\u30de\u30c3\u30d7\u3092\u4fdd\u5b58
 
 # Dialogs
 dialog.exit.confirm.title=GpsPrune \u3092\u7d42\u4e86
@@ -112,6 +131,7 @@ dialog.openoptions.deliminfo.records=\u8a18\u9332\uff0c
 dialog.openoptions.deliminfo.fields=\u30d5\u30a3\u30fc\u30eb\u30c9
 dialog.openoptions.deliminfo.norecords=\u8a18\u9332\u306a\u3057
 dialog.openoptions.altitudeunits=\u9ad8\u5ea6\u5358\u4f4d
+dialog.selecttracks.noname=\u540d\u524d\u306a\u3057
 dialog.jpegload.subdirectories=\u30b5\u30d6\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u542b\u3081\u308b
 dialog.jpegload.loadjpegswithoutcoords=\u5ea7\u6a19\u3092\u542b\u307e\u306a\u3044\u5199\u771f\u3092\u542b\u3081\u308b
 dialog.jpegload.loadjpegsoutsidearea=\u73fe\u5728\u898b\u3066\u3044\u308b\u5730\u57df\u5916\u306e\u5199\u771f\u3092\u542b\u3081\u308b
@@ -160,12 +180,12 @@ dialog.pointtype.desc=\u6b21\u306e\u70b9\u3092\u4fdd\u5b58\u3057\u307e\u3059\uff
 dialog.pointtype.track=\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8
 dialog.pointtype.waypoint=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8
 dialog.pointtype.photo=\u5199\u771f\u70b9
+dialog.pointtype.audio=\u30aa\u30fc\u30c7\u30a3\u30aa\u30dd\u30a4\u30f3\u30c8
 dialog.pointtype.selection=\u9078\u629e\u7bc4\u56f2\u306e\u307f
 dialog.confirmreversetrack.title=\u53cd\u8ee2\u306e\u78ba\u8a8d
 dialog.confirmreversetrack.text=\u3053\u306e\u30c8\u30e9\u30c3\u30af\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u542b\u3093\u3067\u3044\u3066\u3001\u53cd\u8ee2\u3059\u308b\u3068\u306a\u304f\u306a\u308a\u307e\u3059\u3002\n\u672c\u5f53\u306b\u53cd\u8ee2\u3055\u305b\u307e\u3059\u304b\uff1f
 dialog.confirmcutandmove.title=\u5207\u308a\u53d6\u308a\u3068\u79fb\u52d5\u306e\u78ba\u8a8d
 dialog.confirmcutandmove.text=\u3053\u306e\u30c8\u30e9\u30c3\u30af\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u542b\u3093\u3067\u3044\u3066\u3001\u79fb\u52d5\u3059\u308b\u3068\u306a\u304f\u306a\u308a\u307e\u3059\u3002\n\u672c\u5f53\u306b\u53cd\u8ee2\u3055\u305b\u307e\u3059\u304b\uff1f
-dialog.interpolate.title=\u70b9\u3092\u88dc\u5b8c\u3059\u308b
 dialog.interpolate.parameter.text=\u9078\u629e\u3057\u305f\u70b9\u306e\u9593\u306b\u633f\u5165\u3055\u308c\u308b\u70b9\u306e\u6570
 dialog.undo.title=\u4f5c\u696d\u3092\u30a2\u30f3\u30c9\u30a5
 dialog.undo.pretext=\u30a2\u30f3\u30c9\u30a5\u3059\u308b\u4f5c\u696d\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002
@@ -183,7 +203,7 @@ dialog.pointedit.changevalue.title=\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u7de8\u9
 dialog.pointnameedit.name=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u540d
 dialog.pointnameedit.uppercase=\u5927\u6587\u5b57
 dialog.pointnameedit.lowercase=\u5c0f\u6587\u5b57
-dialog.pointnameedit.sentencecase=\u982d\u6587\u5b57\u306e\u307f\u5927\u6587\u5b57
+dialog.pointnameedit.titlecase=\u982d\u6587\u5b57\u306e\u307f\u5927\u6587\u5b57
 dialog.addtimeoffset.add=\u6642\u9593\u3092\u8db3\u3059
 dialog.addtimeoffset.subtract=\u6642\u9593\u3092\u5f15\u304f
 dialog.addtimeoffset.days=\u65e5
@@ -219,24 +239,29 @@ dialog.distances.column.to=\u7d42\u70b9
 dialog.distances.currentpoint=\u73fe\u5728\u306e\u70b9
 dialog.distances.toofewpoints=\u3053\u306e\u6a5f\u80fd\u306f\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u9593\u306e\u8ddd\u96e2\u3092\u8a08\u7b97\u3059\u308b\u305f\u3081\u306b\u3001\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u304c\u5fc5\u8981\u3067\u3059\u3002
 dialog.fullrangedetails.intro=\u9078\u629e\u7bc4\u56f2\u306b\u306f\u8a73\u7d30\u304c\u3042\u308a\u307e\u3059\u3002
-dialog.setmapbg.mapnik=Mapnik(\u521d\u671f\u5024)
-dialog.setmapbg.osma=Osma
-dialog.setmapbg.cyclemap=Cyclemap
-dialog.setmapbg.other=\u305d\u306e\u4ed6(URL\u306b\u5165\u529b)
-dialog.setmapbg.server=\u5730\u56f3\u30b5\u30fc\u30d0\u30fcURL
+dialog.addmapsource.title=\u65b0\u3057\u3044\u30de\u30c3\u30d7\u30fb\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0
+dialog.addmapsource.sourcename=\u30bd\u30fc\u30b9\u306e\u540d\u524d
+dialog.addmapsource.maxzoom=\u6700\u5927\u30ba\u30fc\u30e0\u30ec\u30d9\u30eb
+dialog.addmapsource.cloudstyle=\u30b9\u30bf\u30a4\u30eb\u756a\u53f7
+dialog.addmapsource.noname=\u540d\u524d\u306a\u3057
 dialog.gpsies.column.name=\u30c8\u30e9\u30c3\u30af\u540d
 dialog.gpsies.column.length=\u9577\u3055
 dialog.gpsies.description=\u8a18\u8ff0
 dialog.gpsies.nodescription=\u8a18\u8ff0\u304c\u3042\u308a\u307e\u305b\u3093
 dialog.gpsies.nonefound=\u30c8\u30e9\u30c3\u30af\u304c\u3042\u308a\u307e\u305b\u3093
+dialog.gpsies.username=Gpsies\u306e\u30e6\u30fc\u30b6\u30fc\u540d
+dialog.gpsies.password=Gpsies\u306e\u30d1\u30b9\u30ef\u30fc\u30c9
 dialog.gpsies.activities=\u6d3b\u52d5\u306b\u9069\u3057\u3066
 dialog.gpsies.activity.trekking=\u30cf\u30a4\u30ad\u30f3\u30b0
 dialog.gpsies.activity.walking=\u30a6\u30a9\u30fc\u30ad\u30f3\u30b0
 dialog.gpsies.activity.jogging=\u5b9f\u884c\u4e2d
 dialog.gpsies.activity.biking=\u30b5\u30a4\u30af\u30ea\u30f3\u30b0
+dialog.gpsies.activity.motorbiking=\u30e2\u30fc\u30bf\u30fc\u30d0\u30a4\u30af
 dialog.gpsies.activity.snowshoe=\u30b9\u30ce\u30fc\u30b7\u30e5\u30fc\u30a4\u30f3\u30b0
 dialog.gpsies.activity.sailing=\u30bb\u30fc\u30ea\u30f3\u30b0
 dialog.gpsies.activity.skating=\u30d5\u30a3\u30ae\u30e5\u30a2\u30b9\u30b1\u30fc\u30c8
+dialog.wikipedia.column.name=Wikipedia\u8a18\u4e8b\u540d
+dialog.wikipedia.column.distance=\u8ddd\u96e2
 dialog.correlate.notimestamps=\u30c7\u30fc\u30bf\u30dd\u30a4\u30f3\u30c8\u306b\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u304c\u306a\u3044\u306e\u3067\u3001\u5199\u771f\u3092\u95a2\u9023\u4ed8\u3051\u3089\u308c\u308b\u7269\u304c\u3042\u308a\u307e\u305b\u3093\u3002
 dialog.correlate.nouncorrelatedphotos=\u95a2\u9023\u4ed8\u3051\u3089\u308c\u306a\u304b\u3063\u305f\u5199\u771f\u306f\u3042\u308a\u307e\u305b\u3093\u3002\n\u7d9a\u3051\u307e\u3059\u304b\uff1f
 dialog.correlate.photoselect.intro=\u6642\u9593\u504f\u4f4d\u3092\u4f5c\u308b\u305f\u3081\u306e\u5199\u771f\u3092\u4e00\u3064\u9078\u3093\u3067\u304f\u3060\u3055\u3044\u3002
@@ -259,13 +284,19 @@ dialog.correlate.options.nodistancelimit=\u8ddd\u96e2\u5236\u9650\u306a\u3057
 dialog.correlate.options.distancelimit=\u8ddd\u96e2\u5236\u9650
 dialog.correlate.options.correlate=\u95a2\u9023\u4ed8\u3051
 dialog.correlate.alloutsiderange=\u5168\u3066\u306e\u5199\u771f\u304c\u30c8\u30e9\u30c3\u30af\u306e\u7bc4\u56f2\u5916\u3067\u3059\u3002\u3060\u304b\u3089\u3001\u4f55\u3082\u95a2\u9023\u4ed8\u3051\u3089\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n\u6642\u9593\u504f\u4f4d\u3092\u5909\u3048\u308b\u304b\u3001\u5c11\u306a\u304f\u3068\u3082\u4e00\u3064\u306e\u5199\u771f\u3092\u624b\u52d5\u95a2\u9023\u4ed8\u3051\u3092\u3059\u308b\u306a\u3069\u3057\u3066\u898b\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.correlate.filetimes=\u30d5\u30a1\u30a4\u30eb\u306e\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u8868\u793a\uff1a
+dialog.correlate.filetimes2=\u30aa\u30fc\u30c7\u30a3\u30aa\u30af\u30ea\u30c3\u30d7\u306e
+dialog.correlate.timestamp.beginning=\u958b\u59cb
+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.compress.nonefound=\u9664\u304f\u3053\u3068\u304c\u3067\u304d\u308b\u30c7\u30fc\u30bf\u30dd\u30a4\u30f3\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\u3002
+dialog.deletemarked.nonefound=\u9664\u304f\u3053\u3068\u304c\u3067\u304d\u308b\u30c7\u30fc\u30bf\u30dd\u30a4\u30f3\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\u3002
 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
@@ -293,6 +324,7 @@ dialog.about.systeminfo.povray=Povray \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08
 dialog.about.systeminfo.exiftool=Exiftool \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08
 dialog.about.systeminfo.gpsbabel=Gpsbabel \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08
 dialog.about.systeminfo.gnuplot=Gnuplot \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08
+dialog.about.systeminfo.exiflib=Exif\u30e9\u30a4\u30d6\u30e9\u30ea
 dialog.about.yes=\u306f\u3044
 dialog.about.no=\u3044\u3044\u3048
 dialog.about.credits=\u30af\u30ec\u30b8\u30c3\u30c8
@@ -314,6 +346,8 @@ dialog.checkversion.releasedate2=\u306b\u30ea\u30ea\u30fc\u30b9\u3057\u307e\u305
 dialog.checkversion.download=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f\u3001 http://activityworkshop.net/software/gpsprune/download.html \u3078\u884c\u3063\u3066\u304f\u3060\u3055\u3044\u3002
 dialog.keys.intro=\u30de\u30a6\u30b9\u306e\u4ee3\u308f\u308a\u306b\u6b21\u306e\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\u30ad\u30fc\u3092\u4f7f\u3046\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002
 dialog.keys.keylist=<table><tr><td>\u77e2\u5370\u30ad\u30fc</td><td>\u5730\u56f3\u3092\u4e0a\u4e0b\u5de6\u53f3\u306b\u79fb\u52d5</td></tr><tr><td>Ctrl + \u5de6\u30fb\u53f3\u77e2\u5370</td><td>\u524d\u30fb\u6b21\u306e\u70b9\u3092\u9078\u629e</td></tr><tr><td>Ctrl + \u4e0a\u30fb\u4e0b\u77e2\u5370</td><td>\u62e1\u5927\u30fb\u7e2e\u5c0f</td></tr><tr><td>Del</td><td>\u73fe\u5728\u306e\u70b9\u3092\u524a\u9664</td></tr></table>
+dialog.keys.normalmodifier=Ctrl\u30ad\u30fc
+dialog.keys.macmodifier=Command\u30ad\u30fc
 dialog.saveconfig.desc=\u4e0b\u8a18\u306e\u8a2d\u5b9a\u304c\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059
 dialog.saveconfig.prune.trackdirectory=\u30c8\u30e9\u30c3\u30af\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
 dialog.saveconfig.prune.photodirectory=\u5199\u771f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
@@ -322,15 +356,16 @@ dialog.saveconfig.prune.languagefile=\u8a00\u8a9e\u30d5\u30a1\u30a4\u30eb
 dialog.saveconfig.prune.gpsdevice=GPS\u30c7\u30d0\u30a4\u30b9
 dialog.saveconfig.prune.gpsformat=GPS\u30d5\u30a9\u30fc\u30de\u30c3\u30c8
 dialog.saveconfig.prune.povrayfont=Povray \u30d5\u30a9\u30f3\u30c8
-dialog.saveconfig.prune.metricunits=\u30e1\u30fc\u30c8\u30eb\u6cd5\u3092\u4f7f\u3046\uff1f
 dialog.saveconfig.prune.gnuplotpath=gnuplot\u3078\u306e\u30d1\u30b9
 dialog.saveconfig.prune.gpsbabelpath=gpsbabel\u3078\u306e\u30d1\u30b9
 dialog.saveconfig.prune.exiftoolpath=exiftool\u3078\u306e\u30d1\u30b9
-dialog.saveconfig.prune.mapserverindex=\u80cc\u666f\u5730\u56f3\u30b5\u30fc\u30d0\u30fc\u306e\u7d22\u5f15(1-4)
-dialog.saveconfig.prune.mapserverurl=\u5730\u56f3\u30b5\u30fc\u30d0\u30fc\u306eURL
+dialog.saveconfig.prune.mapsource=\u30de\u30c3\u30d7\u30fb\u30bd\u30fc\u30b9\u3092\u9078\u629e
+dialog.saveconfig.prune.mapsourcelist=\u30de\u30c3\u30d7\u30fb\u30bd\u30fc\u30b9
+dialog.saveconfig.prune.diskcache=\u30de\u30c3\u30d7\u306e\u30ad\u30e3\u30c3\u30b7\u30e5
 dialog.saveconfig.prune.kmzimagewidth=KML \u753b\u50cf\u5e45
 dialog.saveconfig.prune.kmzimageheight=KML \u753b\u50cf\u9ad8
 dialog.saveconfig.prune.colourscheme=\u8272\u306e\u30b9\u30ad\u30fc\u30e0
+dialog.saveconfig.prune.linewidth=\u7dda\u306e\u5e45
 dialog.saveconfig.prune.kmltrackcolour=KML \u30c8\u30e9\u30c3\u30af\u306e\u8272
 dialog.setpaths.intro=\u5fc5\u8981\u306a\u3089\u3001\u5916\u90e8\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30b9\u3092\u9078\u3076\u4e8b\u304c\u3067\u304d\u307e\u3059
 dialog.addaltitude.noaltitudes=\u9078\u629e\u7bc4\u56f2\u306f\u3001\u9ad8\u5ea6\u3092\u542b\u3093\u3067\u307e\u305b\u3093\u3002
@@ -354,13 +389,20 @@ dialog.setlanguage.language=\u8a00\u8a9e
 dialog.setlanguage.languagefile=\u8a00\u8a9e\u30d5\u30a1\u30a4\u30eb
 dialog.setlanguage.endmessage=\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u3001\u8a00\u8a9e\u306e\u5909\u66f4\u3092\u6709\u52b9\u306b\u3059\u308b\u305f\u3081\u306b\nGpsPrune \u3092\u518d\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002
 
+dialog.diskcache.save=\u30c7\u30a3\u30b9\u30af\u306b\u30a4\u30e1\u30fc\u30b8\u3092\u4fdd\u5b58
+dialog.diskcache.dir=\u30ad\u30e3\u30c3\u30b7\u30e5\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
+dialog.diskcache.createdir=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210
+dialog.diskcache.nocreate=\u30ad\u30e3\u30c3\u30b7\u30e5\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c\u4f5c\u6210\u3055\u308c\u306a\u304b\u3063\u305f\u3002
+dialog.setlinewidth.text=\u30c8\u30e9\u30c3\u30af\u63cf\u753b\u306e\u7dda\u5e45\u30921-4\u306e\u7bc4\u56f2\u3067\u5165\u529b
+dialog.searchwikipedianames.search=\u53f3\u8a18\u3092\u691c\u7d22:
+
 # 3d window
 dialog.3d.title=GpsPrune 3D \u8868\u793a
 dialog.3dlines.title=GpsPrune \u683c\u5b50\u7dda
 dialog.3dlines.empty=\u683c\u5b50\u7dda\u304c\u8868\u793a\u3055\u308c\u307e\u305b\u3093
 dialog.3dlines.intro=\u3053\u308c\u3089\u304c 3D \u8868\u793a\u7528\u306e\u683c\u5b50\u7dda\u3067\u3059\u3002
 
-# Confirm messages || These are displayed as confirmation in the status bar
+# Confirm messages
 confirm.loadfile=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u3080
 confirm.save.ok1=\u4fdd\u5b58\u6210\u529f
 confirm.save.ok2=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u70b9
@@ -381,14 +423,23 @@ confirm.undo.single=\u64cd\u4f5c\u306f\u30a2\u30f3\u30c9\u30a5\u3055\u308c\u305f
 confirm.undo.multi=\u64cd\u4f5c\u306f\u30a2\u30f3\u30c9\u30a5\u3055\u308c\u305f
 confirm.jpegload.single=\u5199\u771f\u304c\u52a0\u3048\u3089\u308c\u305f
 confirm.jpegload.multi=\u5199\u771f\u304c\u52a0\u3048\u3089\u308c\u305f
-confirm.photo.disconnect=\u5199\u771f\u304c\u63a5\u7d9a\u3055\u308c\u305f
+confirm.media.connect=\u30e1\u30c7\u30a3\u30a2\u304c\u63a5\u7d9a\u3055\u308c\u305f
+confirm.photo.disconnect=\u5199\u771f\u304c\u5207\u65ad\u3055\u308c\u305f
+confirm.audio.disconnect=\u30aa\u30fc\u30c7\u30a3\u30aa\u304c\u5207\u65ad\u3055\u308c\u305f
+confirm.media.removed=\u524a\u9664\u3055\u308c\u305f
 confirm.correlatephotos.single=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
 confirm.correlatephotos.multi=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
 confirm.createpoint=\u70b9\u304c\u4f5c\u3089\u308c\u305f
 confirm.rotatephoto=\u5199\u771f\u3092\u56de\u8ee2\u3057\u305f
 confirm.running=\u5b9f\u884c\u4e2d...
+confirm.lookupsrtm1=
+confirm.lookupsrtm2=\u6a19\u9ad8\u5024
+confirm.deletefieldvalues=\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u5024\u304c\u524a\u9664\u3055\u308c\u305f
+confirm.audioload=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u304c\u8ffd\u52a0\u3055\u308c\u305f
+confirm.correlateaudios.single=\u30aa\u30fc\u30c7\u30a3\u30aa\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
+confirm.correlateaudios.multi=\u8907\u6570\u306e\u30aa\u30fc\u30c7\u30a3\u30aa\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
 
-# Buttons || These are all the texts for buttons
+# Buttons
 button.ok=\u6c7a\u5b9a
 button.back=\u623b\u308b
 button.next=\u6b21
@@ -411,11 +462,14 @@ button.selectall=\u5168\u9078\u629e
 button.selectnone=\u9078\u629e\u89e3\u9664
 button.preview=\u30d7\u30ec\u30d3\u30e5\u30fc
 button.load=\u8aad\u307f\u8fbc\u307f
+button.upload=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9
 button.guessfields=\u30d5\u30a3\u30fc\u30eb\u30c9\u4e88\u6e2c
 button.showwebpage=\u30a6\u30a7\u30d6\u30da\u30fc\u30b8\u3092\u8868\u793a
 button.check=\u691c\u67fb
 button.resettodefaults=\u521d\u671f\u5024\u306b\u623b\u3059
 button.browse=\u95b2\u89a7...
+button.addnew=\u65b0\u3057\u304f\u8ffd\u52a0
+button.delete=\u524a\u9664
 
 # File types
 filetype.txt=TXT\u30d5\u30a1\u30a4\u30eb
@@ -426,10 +480,12 @@ filetype.kmz=KMZ\u30d5\u30a1\u30a4\u30eb
 filetype.gpx=GPX\u30d5\u30a1\u30a4\u30eb
 filetype.pov=POV\u30d5\u30a1\u30a4\u30eb
 filetype.svg=SVG\u30d5\u30a1\u30a4\u30eb
+filetype.audio=MP3,OGG,WAV\u30d5\u30a1\u30a4\u30eb
 
-# Display components || These are all for the side panels showing point/range details
+# Display components
 display.nodata=\u8aad\u307f\u8fbc\u307e\u308c\u305f\u30c7\u30fc\u30bf\u306a\u3057
 display.noaltitudes=\u30c8\u30e9\u30c3\u30af\u30c7\u30fc\u30bf\u306f\u9ad8\u5ea6\u3092\u542b\u307f\u307e\u305b\u3093
+display.notimestamps=\u30c8\u30e9\u30c3\u30af\u30c7\u30fc\u30bf\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u542b\u3093\u3067\u3044\u307e\u305b\u3093
 details.trackdetails=\u30c8\u30e9\u30c3\u30af\u8a73\u7d30
 details.notrack=\u8aad\u307f\u8fbc\u307e\u308c\u305f\u30c8\u30e9\u30c3\u30af\u306a\u3057
 details.track.points=\u70b9
@@ -454,16 +510,21 @@ display.range.time.mins=\u5206
 display.range.time.hours=\u6642
 display.range.time.days=\u65e5
 details.range.avespeed=\u5e73\u5747\u901f\u5ea6
-details.range.avemovingspeed=\u5e73\u5747\u79fb\u52d5
+details.range.maxspeed=\u6700\u9ad8\u901f\u5ea6
 details.range.numsegments=\u30bb\u30b0\u30e1\u30f3\u30c8\u6570
 details.range.pace=\u30da\u30fc\u30b9
 details.range.gradient=\u52fe\u914d
 details.lists.waypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8
 details.lists.photos=\u5199\u771f
+details.lists.audio=\u30aa\u30fc\u30c7\u30a3\u30aa
 details.photodetails=\u5199\u771f\u8a73\u7d30
 details.nophoto=\u5199\u771f\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093
 details.photo.loading=\u8aad\u307f\u8fbc\u307f\u4e2d
 details.media.connected=\u63a5\u7d9a\u6e08
+details.audiodetails=\u30aa\u30fc\u30c7\u30a3\u30aa\u306e\u8a73\u7d30
+details.noaudio=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u304c\u672a\u9078\u629e
+details.audio.file=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb
+details.audio.playing=\u518d\u751f...
 map.overzoom=\u3053\u306e\u30ba\u30fc\u30e0\u30ec\u30d9\u30eb\u3067\u306f\u5730\u56f3\u304c\u5165\u624b\u3067\u304d\u307e\u305b\u3093\u3002
 
 # Field names
@@ -492,12 +553,12 @@ units.feet=\u30d5\u30a3\u30fc\u30c8
 units.feet.short=ft
 units.kilometres=\u30ad\u30ed\u30e1\u30fc\u30c8\u30eb
 units.kilometres.short=km
-units.kmh=km/h
+units.kilometresperhour.short=km/h
 units.miles=\u30de\u30a4\u30eb
 units.miles.short=mi
-units.mph=mph
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mph
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=\u6642\u9593
 units.degminsec=\u5ea6-\u5206-\u79d2
 units.degmin=\u5ea6-\u5206
@@ -514,14 +575,16 @@ cardinal.s=S
 cardinal.e=E
 cardinal.w=W
 
-# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did
+# Undo operations
 undo.load=\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u307f
 undo.loadphotos=\u5199\u771f\u306e\u8aad\u307f\u8fbc\u307f
+undo.loadaudios=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u8fbc\u307f
 undo.editpoint=\u70b9\u306e\u7de8\u96c6
 undo.deletepoint=\u70b9\u306e\u524a\u9664
-undo.removephoto=\u5199\u771f\u306e\u53d6\u308a\u9664\u304d
+undo.removephoto=\u5199\u771f\u306e\u524a\u9664
+undo.removeaudio=\u30aa\u30fc\u30c7\u30a3\u30aa\u306e\u524a\u9664
 undo.deleterange=\u7bc4\u56f2\u306e\u524a\u9664
-undo.compress=\u30c8\u30e9\u30c3\u30af\u306e\u5727\u7e2e
+undo.deletemarked=\u30c8\u30e9\u30c3\u30af\u306e\u5727\u7e2e
 undo.insert=\u70b9\u306e\u633f\u5165
 undo.reverse=\u7bc4\u56f2\u306e\u53cd\u8ee2
 undo.mergetracksegments=\u30c8\u30e9\u30c3\u30af\u30bb\u30b0\u30e1\u30f3\u30c8\u306e\u7d71\u5408
@@ -529,13 +592,16 @@ undo.addtimeoffset=\u6642\u9593\u504f\u4f4d\u3092\u52a0\u3048\u308b
 undo.addaltitudeoffset=\u9ad8\u5ea6\u504f\u4f4d\u3092\u52a0\u3048\u308b
 undo.rearrangewaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u633f\u5165
 undo.cutandmove=\u30bb\u30af\u30b7\u30e7\u30f3\u306e\u79fb\u52d5
-undo.connect=\u5199\u771f\u306e\u63a5\u7d9a
-undo.disconnect=\u5199\u771f\u306e\u63a5\u7d9a\u89e3\u9664
+undo.connect=\u63a5\u7d9a
+undo.disconnect=\u5207\u65ad
 undo.correlatephotos=\u5199\u771f\u306e\u95a2\u9023\u4ed8\u3051
 undo.rearrangephotos=\u5199\u771f\u306e\u4e26\u3079\u66ff\u3048
 undo.createpoint=\u70b9\u306e\u4f5c\u6210
 undo.rotatephoto=\u5199\u771f\u306e\u56de\u8ee2
 undo.convertnamestotimes=\u540d\u524d\u3092\u6642\u9593\u306b\u5909\u63db\u3059\u308b
+undo.lookupsrtm=SRTM\u304b\u3089\u6a19\u9ad8\u3092\u691c\u7d22
+undo.deletefieldvalues=\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u5024\u3092\u524a\u9664
+undo.correlateaudios=\u30aa\u30fc\u30c7\u30a3\u30aa\u3092\u95a2\u9023\u4ed8\u3051
 
 # Error messages
 error.save.dialogtitle=\u30c7\u30fc\u30bf\u4fdd\u5b58\u306e\u30a8\u30e9\u30fc
@@ -558,6 +624,7 @@ error.jpegload.dialogtitle=\u5199\u771f\u306e\u8aad\u307f\u8fbc\u307f\u30a8\u30e
 error.jpegload.nofilesfound=\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
 error.jpegload.nojpegsfound=Jpeg\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
 error.jpegload.nogpsfound=GPS\u60c5\u5831\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+error.audioload.nofilesfound=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u304c\u672a\u9078\u629e
 error.gpsload.unknown=\u4e0d\u660e\u306a\u30a8\u30e9\u30fc
 error.undofailed.title=\u30a2\u30f3\u30c9\u30a5\u5931\u6557
 error.undofailed.text=\u30a2\u30f3\u30c9\u30a5\u64cd\u4f5c\u306e\u5931\u6557
@@ -571,3 +638,8 @@ error.osmimage.dialogtitle=\u5730\u56f3\u753b\u50cf\u3092\u8aad\u307f\u8fbc\u307
 error.osmimage.failed=\u5730\u56f3\u753b\u50cf\u3092\u8aad\u307f\u8fbc\u307f\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u63a5\u7d9a\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002
 error.language.wrongfile=\u9078\u629e\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306fGpsPrune \u7528\u306e\u8a00\u8a9e\u30d5\u30a1\u30a4\u30eb\u306b\u898b\u3048\u307e\u305b\u3093\u3002
 error.convertnamestotimes.nonames=\u3069\u306e\u540d\u524d\u3082\u6642\u9593\u306b\u5909\u63db\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
+error.lookupsrtm.nonefound=\u3069\u306e\u6a19\u9ad8\u5024\u3082\u3053\u308c\u3089\u306e\u30dd\u30a4\u30f3\u30c8\u304b\u3089\u53d6\u5f97\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002
+error.lookupsrtm.nonerequired=\u5168\u3066\u306e\u30dd\u30a4\u30f3\u30c8\u306f\u6a19\u9ad8\u5024\u3092\u6301\u3063\u3066\u3044\u308b\u305f\u3081\u3001\u691c\u7d22\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002
+error.gpsies.uploadnotok=Gpsies\u30b5\u30fc\u30d0\u30fc\u304c\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u8fd4\u3057\u307e\u3057\u305f\u3002
+error.gpsies.uploadfailed=\u30a8\u30e9\u30fc\u306e\u305f\u3081\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+error.playaudiofailed=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u306e\u518d\u751f\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
index be9b40e0669226c3b91d701233c7d4fca40d28e6..68c5cd7910fe837cbfdba48a039cb9a7c9c6b6ea 100644 (file)
@@ -19,8 +19,8 @@ menu.range.all=\ubaa8\ub450 \uc120\ud0dd
 menu.range.none=\uc120\ud0dd\ud558\uc9c0 \uc54a\uae30
 menu.range.start=\uc2dc\uc791 \uc9c0\uc810\uc73c\ub85c \uc9c0\uc815
 menu.range.end=\ub05d\uc9c0\uc810\uc73c\ub85c \uc9c0\uc815
-menu.range.deleterange=\uc5f0\uacb0\uc120 \uc0ad\uc81c
-menu.range.interpolate=\uc911\uac04\uc5d0 \ub123\uae30
+function.deleterange=\uc5f0\uacb0\uc120 \uc0ad\uc81c
+function.interpolate=\uc0bd\uc785\ud55c \uc9c0\uc810
 menu.range.average=\ud3c9\uade0\uc9c0\uc810\uc120\ud0dd
 menu.range.reverse=\ucc98\uc74c\uacfc \ub05d \ubc14\uafb8\uae30
 menu.range.mergetracksegments=\ud2b8\ub799 \ubd80\ubd84 \ubcd1\ud569
@@ -212,7 +212,6 @@ dialog.confirmreversetrack.title=\ubc18\uc804\uc778\uc9c0 \ud655\uc778
 dialog.confirmreversetrack.text=\uc774 \ud2b8\ub799\uc740 \ubc18\uc804\ud6c4 \uc21c\uc11c\uac00 \ubc14\uafe8\uc744 \uc218\ub3c4 \uc788\ub294\uc2dc\uac04\uc815\ubcf4\ub97c \ud3ec\ud568\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. /n \uadf8\ub798\ub3c4 \uc774\uc601\uc5ed\uc744 \ubc18\uc804\uc2dc\ud0a4\uc2dc\ub824\ub098\uc694?
 dialog.confirmcutandmove.title=\uc790\ub974\uace0 \uc62e\uae30\uae30 \ud655\uc778
 dialog.confirmcutandmove.text=\uc774 \ud2b8\ub799\uc740 \uc774\ub3d9\ud6c4 \uc21c\uc11c\uac00 \ubc14\uafe7\uc744 \uc218\ub3c4 \uc788\ub294 \uc2dc\uac04\uc815\ubcf4\ub97c \ud3ec\ud568\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. /n \uadf8\ub798\ub3c4 \uc774 \uc601\uc5ed\uc744 \uc62e\uae30\uc2dc\ub824\ub098\uc694?
-dialog.interpolate.title=\uc0bd\uc785\ud55c \uc9c0\uc810
 dialog.interpolate.parameter.text=\uc120\ud0dd\ud55c \uc9c0\uc810 \uc0ac\uc774\uc5d0 \ub123\uc744 \uc9c0\uc810\uc758 \uc218
 dialog.undo.title=\uc791\uc5c5 \ub418\ub3cc\ub9ac\uae30
 dialog.undo.pretext=\ub418\ub3cc\ub9b4 \uc791\uc5c5\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694
@@ -230,7 +229,7 @@ dialog.pointedit.changevalue.title=\ud544\ub4dc \uc218\uc815
 dialog.pointnameedit.name=\uacbd\uc720\uc9c0 \uc774\ub984
 dialog.pointnameedit.uppercase=\ub300\ubb38\uc790\ub85c
 dialog.pointnameedit.lowercase=\uc18c\ubb38\uc790\ub85c
-dialog.pointnameedit.sentencecase=\uccab\uae00\uc790\ub9cc \ub300\ubb38\uc790\ub85c
+dialog.pointnameedit.titlecase=\uccab\uae00\uc790\ub9cc \ub300\ubb38\uc790\ub85c
 dialog.addtimeoffset.add=\uc2dc\uac04 \ucd94\uac00
 dialog.addtimeoffset.subtract=\uc2dc\uac04 \ube7c\uae30
 dialog.addtimeoffset.days=\uc77c
@@ -333,7 +332,7 @@ 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.compress.nonefound=\uc9c0\uc810 \ub370\uc774\ud130\uac00 \uc81c\uac70\ub420 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.
+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
 dialog.compress.wackypoints.title=\uc5c9\ub6b1\ud558\uac70\ub098 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uc9c0\uc810 \uc81c\uac70
@@ -397,7 +396,6 @@ dialog.saveconfig.prune.languagefile=\uc5b8\uc5b4\ud30c\uc77c
 dialog.saveconfig.prune.gpsdevice=GPS\uc7a5\uce58
 dialog.saveconfig.prune.gpsformat=GPS \ud615\uc2dd
 dialog.saveconfig.prune.povrayfont=Povray \uae00\uaf34
-dialog.saveconfig.prune.metricunits=\ubbf8\ud130\ubc95 \ub2e8\uc704\ub97c \uc0ac\uc6a9\ud558\uc2dc\ub098\uc694?
 dialog.saveconfig.prune.gnuplotpath=gnjuplot \uacbd\ub85c
 dialog.saveconfig.prune.gpsbabelpath=gpsbabel \uacbd\ub85c
 dialog.saveconfig.prune.exiftoolpath=exiftool \uacbd\ub85c
@@ -556,7 +554,6 @@ display.range.time.mins=\ubd84
 display.range.time.hours=\uc2dc\uac04
 display.range.time.days=\uc77c
 details.range.avespeed=\ud3c9\uade0 \uc18d\ub3c4
-details.range.avemovingspeed=\ud3c9\uade0 \uc774\ub3d9
 details.range.maxspeed=\ucd5c\uace0 \uc18d\ub3c4
 details.range.numsegments=\ubd80\ubd84\ub4e4\uc758 \uc218
 details.range.pace=\ud398\uc774\uc2a4(1km\ub098 1mile\uc774\ub3d9 \uc2dc\uac04)
@@ -600,12 +597,12 @@ units.feet=\ud53c\ud2b8
 units.feet.short=\ud53c\ud2b8
 units.kilometres=\ud0ac\ub85c\ubbf8\ud130
 units.kilometres.short=\ud0ac\ub85c\ubbf8\ud130
-units.kmh=\ud0ac\ub85c\ubbf8\ud130-\uc2dc\uc18d
+units.kilometresperhour.short=\ud0ac\ub85c\ubbf8\ud130-\uc2dc\uc18d
 units.miles=\ub9c8\uc77c
 units.miles.short=\ub9c8\uc77c
-units.mph=\ub9c8\uc77c-\uc2dc\uc18d
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=\ub9c8\uc77c-\uc2dc\uc18d
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=\uc2dc\uac04
 units.degminsec=\ub3c4-\ubd84-\ucd08
 units.degmin=\ub3c4-\ubd84
@@ -631,7 +628,7 @@ undo.deletepoint=\uc9c0\uc810 \uc0ad\uc81c
 undo.removephoto=\uc0ac\uc9c4 \uc81c\uac70
 undo.removeaudio=\uc18c\ub9ac\ud30c\uc77c \uc81c\uac70
 undo.deleterange=\ubc94\uc704 \uc0ad\uc81c
-undo.compress=\ud2b8\ub799 \uc555\ucd95
+undo.deletemarked=\ud2b8\ub799 \uc555\ucd95
 undo.insert=\uc9c0\uc810\ub4e4 \uc0bd\uc785
 undo.reverse=\ubc94\uc704 \ubc18\uc804
 undo.mergetracksegments=\ud2b8\ub799 \ubd80\ubd84 \ubcd1\ud569
index 6b92a569691076d2ee9de0f2e85ab31cbc16be7f..5a75c2aac89438ac98a6d2f8bcea647f4a44a4d9 100644 (file)
@@ -10,6 +10,7 @@ menu.file.exit=Afsluiten
 menu.track=Route
 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
@@ -20,8 +21,6 @@ menu.range.all=Selecteer alles
 menu.range.none=Selecteer geen
 menu.range.start=Start van reeks
 menu.range.end=Einde van reeks
-menu.range.deleterange=Verwijder reeks
-menu.range.interpolate=Interpoleren
 menu.range.average=Cre\u00eber punt obv gemiddelde van reeks
 menu.range.reverse=Reeks omkeren
 menu.range.mergetracksegments=Samenvoegen route segmenten
@@ -54,6 +53,7 @@ menu.map.connect=Verbind route punten
 menu.map.autopan=Autopan
 menu.map.showmap=Toon kaart
 menu.map.showscalebar=Toon schaal
+menu.map.editmode=Wijzigen
 
 # Alt keys for menus
 altkey.menu.file=F
@@ -86,6 +86,9 @@ function.exportpov=Export POV
 function.exportsvg=Export SVG
 function.editwaypointname=Hernoem waypoint
 function.compress=Route comprimeren
+function.deleterange=Verwijder reeks
+function.croptrack=Route bijknippen
+function.interpolate=Interpoleer punten
 function.addtimeoffset=Tijdsverschil toevoegen
 function.addaltitudeoffset=Hoogteverschil toevoegen
 function.convertnamestotimes=Converteer waypointnamen naar tijden
@@ -140,6 +143,7 @@ dialog.deletepoint.title=Verwijder punt
 dialog.deletepoint.deletephoto=Wilt u de foto die aan dit punt is gekoppeld verwijderen?
 dialog.deletephoto.title=Verwijder foto
 dialog.deletephoto.deletepoint=Wilt u het punt dat aan deze foto is gekoppeld verwijderen?
+dialog.deleteaudio.deletepoint=Wilt u het punt dat aan dit geluidsbestand is gekoppeld verwijderen?
 dialog.openoptions.title=Instellingen openen
 dialog.openoptions.filesnippet=Samenvatting van bestand
 dialog.load.table.field=Veld
@@ -219,8 +223,8 @@ dialog.confirmreversetrack.title=Bevestig omkering
 dialog.confirmreversetrack.text=Deze route bevat tijd-informatie die niet meer klopt na een omkering.\nWeet u zeker dat u deze sectie wilt omkeren?
 dialog.confirmcutandmove.title=Bevestig knip en verplaats
 dialog.confirmcutandmove.text=Deze route bevat tijd-informatie die niet meer klopt na een verplaatsing.\nWeet u zeker dat u de sectie wilt verplaatsen?
-dialog.interpolate.title=Interpoleer punten
 dialog.interpolate.parameter.text=Aantal punten om in te voegen tussen de geselecteerde punten
+dialog.interpolate.betweenwaypoints=Interpoleren tussen waypoints?
 dialog.undo.title=Actie(s) ongedaan maken
 dialog.undo.pretext=Selecteer de acties die u ongedaan wilt maken.
 dialog.undo.none.title=Kan niet ongedaan gemaakt worden.
@@ -237,7 +241,7 @@ dialog.pointedit.changevalue.title=Wijzig veld
 dialog.pointnameedit.name=Naam van het waypoint
 dialog.pointnameedit.uppercase=HOOFDLETTERS
 dialog.pointnameedit.lowercase=kleine letters
-dialog.pointnameedit.sentencecase=Ieder woord begint met hoofdletter
+dialog.pointnameedit.titlecase=Ieder woord begint met hoofdletter
 dialog.addtimeoffset.add=Tijd toevoegen
 dialog.addtimeoffset.subtract=Tijd aftrekken
 dialog.addtimeoffset.days=Dagen
@@ -273,6 +277,8 @@ dialog.distances.column.to=Naar punt
 dialog.distances.currentpoint=Huidige punt
 dialog.distances.toofewpoints=Deze functie heeft waypoints nodig om de afstand ertussen te berekenen
 dialog.fullrangedetails.intro=Dit zijn de details van de geselecteerde reeks
+dialog.fullrangedetails.coltotal=Inclusief hiaten
+dialog.fullrangedetails.colsegments=Zonder hiaten
 dialog.setmapbg.intro=Selecteer een kaart-bron, of voeg een nieuwe bron toe.
 dialog.addmapsource.title=Nieuwe kaart-bron toevoegen
 dialog.addmapsource.sourcename=Naam van de bron
@@ -303,6 +309,7 @@ dialog.wikipedia.column.name=Artikelnaam
 dialog.wikipedia.column.distance=Afstand
 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?
 dialog.correlate.photoselect.intro=Selecteer \u00e9\u00e9n van deze gekoppelde foto's om het tijdsverschil te gebruiken
 dialog.correlate.select.photoname=Fotonaam
 dialog.correlate.select.timediff=Tijdsverschil
@@ -340,7 +347,6 @@ dialog.rearrangephotos.toend=Naar einde
 dialog.rearrangephotos.nosort=Niet sorteren
 dialog.rearrangephotos.sortbyfilename=Sorteren op bestandsnaam
 dialog.rearrangephotos.sortbytime=Sorteren op tijd
-dialog.compress.nonefound=Er konden geen punten verwijderd worden
 dialog.compress.closepoints.title=Verwijder nabijliggende punten
 dialog.compress.closepoints.paramdesc=Bereik
 dialog.compress.wackypoints.title=Vreemde punten verwijderen
@@ -351,6 +357,10 @@ dialog.compress.duplicates.title=Verwijderen duplicaten
 dialog.compress.douglaspeucker.title=Douglas-Peucker compressie
 dialog.compress.douglaspeucker.paramdesc=Span factor
 dialog.compress.summarylabel=Te verwijderen punten
+dialog.compress.confirm1=Er zijn
+dialog.compress.confirm2=punten zijn gemarkeerd.\nGebruik Track - Verwijder gemarkeerde punten om ze te verwijderen
+dialog.compress.confirmnone=er zijn geen punten gemarkeerd
+dialog.deletemarked.nonefound=Er konden geen punten verwijderd worden
 dialog.pastecoordinates.desc=Geef co\u00f6rdinaten in
 dialog.pastecoordinates.coords=Co\u00f6rdinaten
 dialog.pastecoordinates.nothingfound=Controleer de co\u00f6rdinaten en probeer het nogmaals
@@ -406,7 +416,6 @@ dialog.saveconfig.prune.languagefile=Taal bestand
 dialog.saveconfig.prune.gpsdevice=GPS apparaat
 dialog.saveconfig.prune.gpsformat=GPS formaat
 dialog.saveconfig.prune.povrayfont=Povray lettertype
-dialog.saveconfig.prune.metricunits=Metrisch systeem gebruiken?
 dialog.saveconfig.prune.gnuplotpath=Pad naar gnuplot
 dialog.saveconfig.prune.gpsbabelpath=Pad naar gpsbabel
 dialog.saveconfig.prune.exiftoolpath=Pad naar exiftool
@@ -447,6 +456,7 @@ dialog.diskcache.save=Kaartafbeeldingen opslaan op schijf
 dialog.diskcache.dir=Cache map
 dialog.diskcache.createdir=Cre\u00eber map
 dialog.diskcache.nocreate=Cache map niet aangemaakt
+dialog.diskcache.cannotwrite=Kaarttegels kunnen niet in de geselecteerde map worden opgeslagen
 dialog.diskcache.table.path=Pad
 dialog.diskcache.table.usedby=Gebruikt door
 dialog.diskcache.table.zoom=Zoom
@@ -460,6 +470,7 @@ dialog.diskcache.deleteall=Verwijder alle tegels
 dialog.diskcache.deleted1=
 dialog.diskcache.deleted2=bestanden uit de cache verwijderd
 dialog.deletefieldvalues.intro=Selecteer het te verwijderen veld voor de huidige reeks
+dialog.deletefieldvalues.nofields=Er zijn geen velden in deze reeks om te verwijderen
 dialog.setlinewidth.text=Geef lijndikte voor routes (1-4)
 dialog.downloadosm.desc=Bevestig het downloaden van ruwe OSM data voor dit gebied:
 dialog.searchwikipedianames.search=Zoeken naar:
@@ -485,6 +496,7 @@ confirm.addaltitudeoffset=Hoogteverschil toegevoegd
 confirm.rearrangewaypoints=Waypoints herschikt
 confirm.rearrangephotos=Foto herschikt
 confirm.cutandmove=Selectie verplaatst
+confirm.interpolate=Punten toegevoegd
 confirm.convertnamestotimes=Namen waypoint geconverteerd
 confirm.saveexif.ok1=Opgeslagen
 confirm.saveexif.ok2=foto bestanden
@@ -580,7 +592,6 @@ display.range.time.mins=m
 display.range.time.hours=u
 display.range.time.days=d
 details.range.avespeed=Gem snelheid
-details.range.avemovingspeed=Gem snelheid in beweging
 details.range.maxspeed=Max snelheid
 details.range.numsegments=Aantal segmenten
 details.range.pace=Tempo
@@ -593,6 +604,7 @@ details.nophoto=Geen foto geselecteerd
 details.photo.loading=Bezig met laden
 details.photo.bearing=Richting
 details.media.connected=Geconnecteerd
+details.media.fullpath=Volledig pad
 details.audiodetails=Audio details
 details.noaudio=Geen audiobestand geselecteerd
 details.audio.file=Audiobestand
@@ -626,12 +638,15 @@ units.feet=Voet
 units.feet.short=ft
 units.kilometres=Kilometers
 units.kilometres.short=km
-units.kmh=km/u
+units.kilometresperhour.short=km/u
 units.miles=Mijlen
 units.miles.short=mi
-units.mph=mph
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mph
+units.nauticalmiles=Nautische mijlen
+units.nauticalmiles.short=N.m.
+units.nauticalmilesperhour.short=Kn
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=uren
 units.degminsec=Grd-min-sec
 units.degmin=Grd-min
@@ -657,14 +672,15 @@ undo.deletepoint=verwijderen punt
 undo.removephoto=verwijderen foto
 undo.removeaudio=verwijderen audiobestand
 undo.deleterange=verwijderen reeks
-undo.compress=comprimeren route
+undo.croptrack=bijknippen track
+undo.deletemarked=verwijderen punten
 undo.insert=punten invoegen
 undo.reverse=reeks omkeren
 undo.mergetracksegments=samenvoegen route segmenten
 undo.addtimeoffset=tijdsverschil toevoegen
 undo.addaltitudeoffset=hoogteverschil toevoegen
 undo.rearrangewaypoints=herschikken waypoint
-undo.cutandmove=Verschuif sectie
+undo.cutandmove=verschuif sectie
 undo.connect=koppel
 undo.disconnect=loskoppelen
 undo.correlatephotos=correleer foto
@@ -705,7 +721,7 @@ error.undofailed.text=Kon actie niet terugdraaien
 error.function.noop.title=Functie had geen effect
 error.rearrange.noop=Herschikken van punten had geen effect
 error.function.notavailable.title=Functie niet beschikbaar
-error.function.nojava3d=Deze functie heeft Java3d nodig,verkrijgbaar bij sun.com.
+error.function.nojava3d=Deze functie heeft Java3d nodig,\nverkrijgbaar bij sun.com.
 error.3d=Er is een fout opgetreden bij de 3d afbeelding
 error.readme.notfound=Leesmij bestand niet gevonden
 error.osmimage.dialogtitle=Fout bij inlezen kaart afbeeldingen
@@ -716,7 +732,9 @@ error.lookupsrtm.nonefound=Geen hoogtewaarden beschikbaar voor deze punten
 error.lookupsrtm.nonerequired=Alle punten hebben reeds hoogte, er hoeft niets te worden opgezocht.
 error.gpsies.uploadnotok=Gpsies server antwoordde met
 error.gpsies.uploadfailed=De upload is mislukt. Fout
+error.showphoto.failed=Foto laden mislukt
 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
index f76d7af6164373b02400b82a4b2aa0159d451081..f58c8022a9b3722a5fdb71ae5ff55d43d4931a1c 100644 (file)
@@ -10,6 +10,7 @@ menu.file.exit=Zako\u0144cz
 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
@@ -20,8 +21,6 @@ menu.range.all=Zaznacz wszystko
 menu.range.none=Usu\u0144 zaznaczenie
 menu.range.start=Zaznacz pocz\u0105tek zakresu
 menu.range.end=Zaznacz koniec zakresu
-menu.range.deleterange=Usu\u0144 zakres
-menu.range.interpolate=Wstaw pomi\u0119dzy
 menu.range.average=U\u015brednij zaznaczenie
 menu.range.reverse=Odwr\u00f3\u0107 zakres
 menu.range.mergetracksegments=Po\u0142\u0105cz fragmenty \u015bcie\u017cek
@@ -54,6 +53,7 @@ menu.map.connect=Po\u0142\u0105cz punkty \u015bcie\u017cki
 menu.map.autopan=Przesuwanie mapy
 menu.map.showmap=Poka\u017c map\u0119
 menu.map.showscalebar=Poka\u017c skal\u0119
+menu.map.editmode=Tryb edycji
 
 # Alt keys for menus
 altkey.menu.file=P
@@ -86,6 +86,9 @@ function.exportpov=Eksportuj jako POV
 function.exportsvg=Eksportuj jako SVG
 function.editwaypointname=Zmie\u0144 nazw\u0119 punktu po\u015bredniego
 function.compress=Kompresuj \u015bcie\u017ck\u0119
+function.deleterange=Usu\u0144 zakres
+function.croptrack=Przytnij \u015bcie\u017ck\u0119
+function.interpolate=Wstaw pomi\u0119dzy punkty
 function.addtimeoffset=Dodaj przesuni\u0119cie czasu
 function.addaltitudeoffset=Dodaj przesuni\u0119cie wysoko\u015bci
 function.convertnamestotimes=Zamie\u0144 nazwy punkt\u00f3w na czas
@@ -140,6 +143,7 @@ dialog.deletepoint.title=Usu\u0144 punkt
 dialog.deletepoint.deletephoto=Usun\u0105\u0107 zdj\u0119cie do\u0142\u0105czone do tego punktu?
 dialog.deletephoto.title=Usu\u0144 zdj\u0119cie
 dialog.deletephoto.deletepoint=Usun\u0105\u0107 punkt do\u0142\u0105czony do tego zdj\u0119cia?
+dialog.deleteaudio.deletepoint=Usun\u0105\u0107 punkt do\u0142\u0105czony do tego pliku audio?
 dialog.openoptions.title=Otw\u00f3rz opcje
 dialog.openoptions.filesnippet=Fragment z pliku
 dialog.load.table.field=Pole
@@ -219,8 +223,8 @@ dialog.confirmreversetrack.title=Potwierd\u017a odwr\u00f3cenie
 dialog.confirmreversetrack.text=Ta \u015bcie\u017cka zawiera znaczniki czasu, kt\u00f3re po odwr\u00f3ceniu nie b\u0119d\u0105 ustawione w kolejno\u015bci.\nCzy na pewno chcesz odwr\u00f3ci\u0107 ten fragment?
 dialog.confirmcutandmove.title=Potwierd\u017a wytnij i przesu\u0144
 dialog.confirmcutandmove.text=Ta \u015bcie\u017cka zawiera znaczniki czasu, kt\u00f3re po przesuni\u0119ciu nie b\u0119d\u0105 ustawione w kolejno\u015bci.\nCzy na pewno chcesz przesun\u0105\u0107 ten fragment?
-dialog.interpolate.title=Interpoluj punkty
 dialog.interpolate.parameter.text=Ilo\u015b\u0107 punkt\u00f3w do wstawienia pomi\u0119dzy wybrane punkty
+dialog.interpolate.betweenwaypoints=Wstawi\u0107 pomi\u0119dzy punkty po\u015brednie?
 dialog.undo.title=Potwierd\u017a akcje
 dialog.undo.pretext=Wybierz akcje kt\u00f3re chcesz cofn\u0105\u0107
 dialog.undo.none.title=Nie mo\u017cna cofn\u0105\u0107
@@ -237,7 +241,7 @@ dialog.pointedit.changevalue.title=Edytuj pole
 dialog.pointnameedit.name=Nazwa punktu po\u015bredniego
 dialog.pointnameedit.uppercase=WIELKIE litery
 dialog.pointnameedit.lowercase=ma\u0142e litery
-dialog.pointnameedit.sentencecase=Jak W Zdaniu
+dialog.pointnameedit.titlecase=Jak W Zdaniu
 dialog.addtimeoffset.add=Dodaj czas
 dialog.addtimeoffset.subtract=Odejmij czas
 dialog.addtimeoffset.days=Dni
@@ -273,6 +277,8 @@ dialog.distances.column.to=Do punktu
 dialog.distances.currentpoint=Wybrany punkt
 dialog.distances.toofewpoints=Ta funkcja wymaga przynajmniej dw\u00f3ch punkt\u00f3w po\u015brednich, aby mo\u017cna by\u0142o obliczy\u0107 odleg\u0142o\u015bci
 dialog.fullrangedetails.intro=Szczeg\u00f3\u0142y wybranego zakresu
+dialog.fullrangedetails.coltotal=Z lukami
+dialog.fullrangedetails.colsegments=Bez luk
 dialog.setmapbg.intro=Wybierz dostawc\u0119 map t\u0142a lub dodaj nowego
 dialog.addmapsource.title=Dodaj dostawc\u0119 map
 dialog.addmapsource.sourcename=Nazwa dostawcy
@@ -303,6 +309,7 @@ dialog.wikipedia.column.name=Tytu\u0142 artyku\u0142u
 dialog.wikipedia.column.distance=Odleg\u0142o\u015b\u0107
 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?
 dialog.correlate.photoselect.intro=Wybierz jedno z powi\u0105zanych zdj\u0119\u0107 i u\u017cyj go jako wzorca do przesuni\u0119cia czasu
 dialog.correlate.select.photoname=Nazwa zdj\u0119cia
 dialog.correlate.select.timediff=R\u00f3\u017cnica czasowa
@@ -340,7 +347,6 @@ 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.compress.nonefound=Nie mo\u017cna usun\u0105\u0107 \u017cadnych punkt\u00f3w
 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
@@ -351,6 +357,10 @@ dialog.compress.duplicates.title=Usuwanie duplikat\u00f3w
 dialog.compress.douglaspeucker.title=kompresja Douglasa-Peuckera
 dialog.compress.douglaspeucker.paramdesc=wsp\u00f3\u0142czynnik rozpi\u0119to\u015bci (szeroko\u015bci korytarza)
 dialog.compress.summarylabel=Punkty do usuni\u0119cia
+dialog.compress.confirm1=
+dialog.compress.confirm2=punkt\u00f3w zosta\u0142o zaznaczonych\nU\u017cyj \u015acie\u017cka->Usu\u0144 zaznaczone punkty, by je usun\u0105\u0107
+dialog.compress.confirmnone=\u017cadne punkty nie zosta\u0142y zaznaczone
+dialog.deletemarked.nonefound=Nie mo\u017cna usun\u0105\u0107 \u017cadnych punkt\u00f3w
 dialog.pastecoordinates.desc=Wprowad\u017a lub wklej wsp\u00f3\u0142rz\u0119dne
 dialog.pastecoordinates.coords=Wsp\u00f3\u0142rz\u0119dne
 dialog.pastecoordinates.nothingfound=Sprawd\u017a wsp\u00f3\u0142rz\u0119dne i spr\u00f3buj jeszcze raz
@@ -406,7 +416,6 @@ dialog.saveconfig.prune.languagefile=Plik t\u0142umaczenia
 dialog.saveconfig.prune.gpsdevice=Urz\u0105dzenie GPS
 dialog.saveconfig.prune.gpsformat=Format pliku GPS
 dialog.saveconfig.prune.povrayfont=czcionka dla Povray-a
-dialog.saveconfig.prune.metricunits=U\u017cywaj jednostek metrycznych?
 dialog.saveconfig.prune.gnuplotpath=\u015bcie\u017cka do gnuplot
 dialog.saveconfig.prune.gpsbabelpath=\u015bcie\u017cka do gpsbabel
 dialog.saveconfig.prune.exiftoolpath=\u015bcie\u017cka do exiftool
@@ -447,6 +456,7 @@ dialog.diskcache.save=Zapisz mapy na dysk
 dialog.diskcache.dir=katalog pami\u0119ci podr\u0119cznej
 dialog.diskcache.createdir=stw\u00f3rz katalog
 dialog.diskcache.nocreate=Nie utworzono katalogu pami\u0119ci podr\u0119cznej
+dialog.diskcache.cannotwrite=W wybranym katalogu nie mo\u017cna zapisa\u0107 map
 dialog.diskcache.table.path=\u015acie\u017cka
 dialog.diskcache.table.usedby=u\u017cywane przez
 dialog.diskcache.table.zoom=Powi\u0119kszenie
@@ -485,6 +495,7 @@ confirm.addaltitudeoffset=Dodano przesuni\u0119cie wysoko\u015bci
 confirm.rearrangewaypoints=Przestawiono punkty po\u015brednie
 confirm.rearrangephotos=Zmieniono kolejno\u015b\u0107 zdj\u0119\u0107
 confirm.cutandmove=Przesuni\u0119to zaznaczenie
+confirm.interpolate=Dodano punkty
 confirm.convertnamestotimes=Zmieniono nazwy punkt\u00f3w po\u015brednich
 confirm.saveexif.ok1=Zapisano
 confirm.saveexif.ok2=plik(\u00f3w) zdj\u0119\u0107
@@ -504,7 +515,7 @@ confirm.running=Przetwarzam dane ...
 confirm.lookupsrtm1=Znaleziono
 confirm.lookupsrtm2=warto\u015bci wysoko\u015bci
 confirm.deletefieldvalues=Warto\u015bci p\u00f3l usuni\u0119to
-confirm.audioload=dodano
+confirm.audioload=dodano pliki audio
 confirm.correlateaudios.single=audio zosta\u0142o po\u0142\u0105czone
 confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone
 
@@ -580,7 +591,6 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=d
 details.range.avespeed=\u015arednia pr\u0119dko\u015b\u0107
-details.range.avemovingspeed=\u015arednie przesuni\u0119cie
 details.range.maxspeed=Pr\u0119dko\u015b\u0107 maksymalna
 details.range.numsegments=Liczba segment\u00f3w
 details.range.pace=Tempo
@@ -593,6 +603,7 @@ details.nophoto=Brak zaznaczonego zdj\u0119cia
 details.photo.loading=Wczytywanie
 details.photo.bearing=Kierunek
 details.media.connected=Pod\u0142\u0105czony
+details.media.fullpath=Pe\u0142na \u015bcie\u017cka
 details.audiodetails=Szczeg\u00f3\u0142y audio
 details.noaudio=Brak zaznaczonego audio
 details.audio.file=Plik audio
@@ -626,12 +637,15 @@ units.feet=Stopy
 units.feet.short=ft
 units.kilometres=Kilometry
 units.kilometres.short=km
-units.kmh=km/h
+units.kilometresperhour.short=km/h
 units.miles=Mile
 units.miles.short=mi
-units.mph=mi/h
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mi/h
+units.nauticalmiles=Mile morskie
+units.nauticalmiles.short=Mm
+units.nauticalmilesperhour.short=w.
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=Godziny
 units.degminsec=Sto-min-sek
 units.degmin=Sto-min
@@ -657,7 +671,8 @@ undo.deletepoint=usu\u0144 punkt
 undo.removephoto=usu\u0144 zdj\u0119cie (nie z dysku)
 undo.removeaudio=usu\u0144 audio
 undo.deleterange=usu\u0144 zakres
-undo.compress=skompresuj \u015bcie\u017ck\u0119
+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
@@ -716,7 +731,9 @@ error.lookupsrtm.nonefound=Nie znaleziono danych o wysoko\u015bci.
 error.lookupsrtm.nonerequired=Wszystkie pola maj\u0105 informacj\u0119 o wysoko\u015bci, nie ma czego szuka\u0107
 error.gpsies.uploadnotok=Serwer Gpsies zwr\u00f3ci\u0142 informacj\u0119
 error.gpsies.uploadfailed=B\u0142\u0105d wysy\u0142ania
+error.showphoto.failed=Nie powiod\u0142o si\u0119 za\u0142adowanie zdj\u0119cia
 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
index 6266d43dc350e7915ea22959cc3bf5adbb9de86b..1220049fcfcdf17c7a8abe761f9e88c648ddac25 100644 (file)
@@ -20,8 +20,8 @@ menu.range.all=Selectionar tudo
 menu.range.none=N\u00e3o selecionar nenhuns
 menu.range.start=Definir in\u00edcio do intervalo
 menu.range.end=Definir fim do intervalo
-menu.range.deleterange=Remover intervalo
-menu.range.interpolate=Interpolar
+function.deleterange=Remover intervalo
+function.interpolate=Interpolar pontos
 menu.range.average=Sele\u00e7\u00e3o m\u00e9dia
 menu.range.reverse=Reverter intervalo
 menu.range.mergetracksegments=Mesclar trechos da rota
@@ -219,7 +219,6 @@ dialog.confirmreversetrack.title=Confirmar invers\u00e3o
 dialog.confirmreversetrack.text=Esta rota possui informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s a revers\u00e3o.\n Voc\u00ea tem certeza que deseja reverter esta se\u00e7\u00e3o?
 dialog.confirmcutandmove.title=Confirmar cortar e mover
 dialog.confirmcutandmove.text=A rota cont\u00e9m informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s o movimento.\n Voc\u00ea tem certeza que deseja mover esta se\u00e7\u00e3o?
-dialog.interpolate.title=Interpolar pontos
 dialog.interpolate.parameter.text=N\u00famero de pontos para inserir entre os pontos selecionados
 dialog.undo.title=A\u00e7\u00e3o(\u00f5es) de desfazer
 dialog.undo.pretext=Por favor, selecione a a\u00e7\u00e3o(\u00f5es) a desfazer
@@ -237,7 +236,7 @@ dialog.pointedit.changevalue.title=Editar campo
 dialog.pointnameedit.name=Nome do ponto
 dialog.pointnameedit.uppercase=MAI\u00daSCULAS
 dialog.pointnameedit.lowercase=min\u00fasculas
-dialog.pointnameedit.sentencecase=Frase
+dialog.pointnameedit.titlecase=Frase
 dialog.addtimeoffset.add=Adicionar tempo
 dialog.addtimeoffset.subtract=Subtrair tempo
 dialog.addtimeoffset.days=Dias
@@ -340,7 +339,7 @@ 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.compress.nonefound=Nenhum dado dos pontos pode ser removido
+dialog.deletemarked.nonefound=Nenhum dado dos pontos pode ser removido
 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
@@ -406,7 +405,6 @@ dialog.saveconfig.prune.languagefile=Arquivo de idioma
 dialog.saveconfig.prune.gpsdevice=Dispositivo de GPS
 dialog.saveconfig.prune.gpsformat=Formato do GPS
 dialog.saveconfig.prune.povrayfont=Fonte Povray
-dialog.saveconfig.prune.metricunits=Usar unidades m\u00e9tricas?
 dialog.saveconfig.prune.gnuplotpath=Caminho para o gnuplot
 dialog.saveconfig.prune.gpsbabelpath=Caminho para o gpsbabel
 dialog.saveconfig.prune.exiftoolpath=Caminho para o exiftool
@@ -580,7 +578,6 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=d
 details.range.avespeed=Velocidade m\u00e9dia
-details.range.avemovingspeed=Movimento m\u00e9dio
 details.range.maxspeed=Velocidade m\u00e1xima
 details.range.numsegments=N\u00famero de segmentos
 details.range.pace=Passo
@@ -626,12 +623,12 @@ units.feet=P\u00e9s
 units.feet.short=ft
 units.kilometres=Quil\u00f4metros
 units.kilometres.short=km
-units.kmh=km/h
+units.kilometresperhour.short=km/h
 units.miles=Milhas
 units.miles.short=mi
-units.mph=mph
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mph
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=horas
 units.degminsec=Graus-min-seg
 units.degmin=Graus-min
@@ -657,7 +654,7 @@ undo.deletepoint=remover ponto
 undo.removephoto=remover foto
 undo.removeaudio=remover arquivo de \u00e1udio
 undo.deleterange=remover intervalo
-undo.compress=comprimir rota
+undo.deletemarked=remover pontos
 undo.insert=inserir pontos
 undo.reverse=inverter intervalo
 undo.mergetracksegments=mesclar segmentos de rota
index 4df01434bf170e53c2eb207a18a41452c515bd69..b2950f20a5565a9745a413cfc30795188f6d9d65 100644 (file)
@@ -1,5 +1,5 @@
 # Text entries for the GpsPrune application
-# Romanian entries
+# Romanian entries as extra
 
 # Menu entries
 menu.file=Fi\u015fier
@@ -9,36 +9,30 @@ menu.file.exit=Iesire
 menu.track=Traseu
 menu.track.undo=Anulare
 menu.track.clearundo=\u015etergere lista de anulari
-menu.point.editpoint=Editare punct
-menu.point.deletepoint=\u015etergere punct
-menu.range.deleterange=\u015etergere gama
 menu.track.deletemarked=\u015etergere puncte marcate
-menu.range.interpolate=Interpolare
-menu.range.average=Mediere selectie
-menu.range.reverse=Inversare selectie
-menu.range.mergetracksegments=Unire segmente traseu
 menu.track.rearrange=Rearanjare waypoint
 menu.track.rearrange.start=Toate la inceputul fisierului
 menu.track.rearrange.end=Toate la sfarsitul fisierului
 menu.track.rearrange.nearest=Fiecare la punctul cel mai apropiat al traseului
-menu.range.cutandmove=Taiere si mutare selectie
-menu.point=Punct
 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.mergetracksegments=Unire segmente traseu
+menu.range.cutandmove=Taiere si mutare selectie
+menu.point=Punct
+menu.point.editpoint=Editare punct
+menu.point.deletepoint=\u015etergere punct
 menu.photo=Foto
 menu.photo.saveexif=Salveaza la Exif
-function.connecttopoint=Conecteaza la punct
-function.disconnectfrompoint=Deconecteaza de la punct
-function.removephoto=Elimina foto
 menu.view=Vizualizare
 menu.view.browser=Harta in browser
 menu.view.browser.google=Harti Google
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Harti Yahoo
-menu.view.browser.bing=Harti Bing
 menu.help=Ajutor
 # Popup menu for map
 menu.map.zoomin=Apropie
@@ -73,6 +67,9 @@ function.exportpov=Export\u0103 \u00eentr-un fi\u015fier POV
 function.exportsvg=Export\u0103 \u00eentr-un fi\u015fier SVG
 function.editwaypointname=Editare nume waypoint
 function.compress=Comprima traseu
+function.deleterange=\u015etergere gama
+function.interpolate=Interpolare
+function.findwaypoint=Gasire waypoint
 function.charts=Grafice
 function.show3d=Vizualizare arborescenta
 function.distances=Distan\u0163e
@@ -80,6 +77,9 @@ function.setmapbg=Fundal
 function.correlatephotos=Corelare fotografii
 function.setcolours=Selectare culorile
 function.setlanguage=Selectare limba
+function.connecttopoint=Conecteaza la punct
+function.disconnectfrompoint=Deconecteaza de la punct
+function.removephoto=Elimina foto
 function.help=Ajutor
 function.showkeys=Arat\u0103 tastele scurt\u0103turi
 function.about=Despre GpsPrune
index 14caca89482ccef314bb0ced5eaee9ec3b08a130..f14174d2052c13b4d8240a80e7552a16baf107d1 100644 (file)
@@ -20,8 +20,8 @@ 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
 menu.range.start=\u041d\u0430\u0447\u0430\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430
 menu.range.end=\u041a\u043e\u043d\u0435\u0446 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430
-menu.range.deleterange=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b
-menu.range.interpolate=\u0422\u043e\u0447\u043a\u0430 \u043f\u043e \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0438
+function.deleterange=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b
+function.interpolate=\u0418\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u044f \u0442\u043e\u0447\u0435\u043a
 menu.range.average=\u0422\u043e\u0447\u043a\u0430 \u043f\u043e \u0441\u0440\u0435\u0434\u043d\u0435\u043c\u0443
 menu.range.reverse=\u041f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b
 menu.range.mergetracksegments=\u0421\u043b\u0438\u0442\u044c \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u044b \u0442\u0440\u0435\u043a\u0430
@@ -219,7 +219,6 @@ dialog.confirmreversetrack.title=\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u043
 dialog.confirmreversetrack.text=\u042d\u0442\u043e\u0442 \u0442\u0440\u0435\u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0440\u0443\u0448\u0435\u043d\u0430 \u043f\u043e\u0441\u043b\u0435 \u0440\u0430\u0437\u0432\u043e\u0440\u043e\u0442\u0430.\n\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435?
 dialog.confirmcutandmove.title=\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 "\u0432\u044b\u0440\u0435\u0437\u0430\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438"
 dialog.confirmcutandmove.text=\u042d\u0442\u043e\u0442 \u0442\u0440\u0435\u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0440\u0443\u0448\u0435\u043d\u0430 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f.\n\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435?
-dialog.interpolate.title=\u0418\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u044f \u0442\u043e\u0447\u0435\u043a
 dialog.interpolate.parameter.text=\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0447\u0435\u043a \u0434\u043b\u044f \u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u043c\u0435\u0436\u0434\u0443 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c\u0438
 dialog.undo.title=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435(\u044f)
 dialog.undo.pretext=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u043c\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435(\u044f)
@@ -237,7 +236,7 @@ dialog.pointedit.changevalue.title=\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0
 dialog.pointnameedit.name=\u0418\u043c\u044f \u043f\u0443\u0442\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438
 dialog.pointnameedit.uppercase=\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440
 dialog.pointnameedit.lowercase=\u043d\u0438\u0436\u043d\u0438\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440
-dialog.pointnameedit.sentencecase=\u041a\u0430\u0436\u0434\u043e\u0435 \u0421\u043b\u043e\u0432\u043e \u0441 \u0417\u0430\u0433\u043b\u0430\u0432\u043d\u043e\u0439
+dialog.pointnameedit.titlecase=\u041a\u0430\u0436\u0434\u043e\u0435 \u0421\u043b\u043e\u0432\u043e \u0441 \u0417\u0430\u0433\u043b\u0430\u0432\u043d\u043e\u0439
 dialog.addtimeoffset.add=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u043b\u0435
 dialog.addtimeoffset.subtract=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0434\u043e
 dialog.addtimeoffset.days=\u0414\u043d\u0438
@@ -340,7 +339,7 @@ dialog.rearrangephotos.toend=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0
 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.compress.nonefound=\u041d\u0435\u0442 \u0442\u043e\u0447\u0435\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0431\u044b\u0442\u044c \u0443\u0434\u0430\u043b\u0435\u043d\u044b
+dialog.deletemarked.nonefound=\u041d\u0435\u0442 \u0442\u043e\u0447\u0435\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0431\u044b\u0442\u044c \u0443\u0434\u0430\u043b\u0435\u043d\u044b
 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
@@ -406,7 +405,6 @@ dialog.saveconfig.prune.languagefile=\u042f\u0437\u044b\u043a\u043e\u0432\u043e\
 dialog.saveconfig.prune.gpsdevice=GPS \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e
 dialog.saveconfig.prune.gpsformat=GPS \u0444\u043e\u0440\u043c\u0430\u0442
 dialog.saveconfig.prune.povrayfont=Povray \u0448\u0440\u0438\u0444\u0442
-dialog.saveconfig.prune.metricunits=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0435\u0434\u0438\u043d\u0438\u0446\u044b?
 dialog.saveconfig.prune.gnuplotpath=\u041f\u0443\u0442\u044c \u043a GNUPLOT
 dialog.saveconfig.prune.gpsbabelpath=\u041f\u0443\u0442\u044c \u043a GPSBabel
 dialog.saveconfig.prune.exiftoolpath=\u041f\u0443\u0442\u044c \u043a ExifTool
@@ -438,7 +436,7 @@ dialog.colourchooser.red=\u041a\u0440\u0430\u0441\u043d\u044b\u0439
 dialog.colourchooser.green=\u0417\u0435\u043b\u0435\u043d\u044b\u0439
 dialog.colourchooser.blue=\u0421\u0438\u043d\u0438\u0439
 dialog.setlanguage.firstintro=\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u044f\u0437\u044b\u043a\u043e\u0432, <p> \u0438\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u044f\u0437\u044b\u043a\u043e\u0432\u043e\u0439 \u0444\u0430\u0439\u043b.
-dialog.setlanguage.secondintro=\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0438 <p> \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u044f\u0437\u044b\u043a\u0430 \u0432 GpsPrune.
+dialog.setlanguage.secondintro=\u0414\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u044f\u0437\u044b\u043a\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 <p>\u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c GpsPrune .
 dialog.setlanguage.language=\u042f\u0437\u044b\u043a
 dialog.setlanguage.languagefile=\u042f\u0437\u044b\u043a\u043e\u0432\u043e\u0439 \u0444\u0430\u0439\u043b
 dialog.setlanguage.endmessage=\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 GpsPrune,\n\u0447\u0442\u043e\u0431\u044b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u044f\u0437\u044b\u043a\u0430 \u0432\u0441\u0442\u0443\u043f\u0438\u043b\u043e \u0432 \u0441\u0438\u043b\u0443.
@@ -466,9 +464,9 @@ dialog.searchwikipedianames.search=\u041f\u043e\u0438\u0441\u043a \u0434\u043b\u
 
 # 3d window
 dialog.3d.title=GpsPrune 3D-\u0432\u0438\u0434
-dialog.3d.altitudefactor=\u043a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442 \u043f\u043e \u0432\u044b\u0441\u043e\u0442\u0435
-dialog.3dlines.title=\u0441\u0435\u0442\u043a\u0430 GpsPrune
-dialog.3dlines.empty=\u043d\u0435\u0442 \u0441\u0435\u0442\u043a\u0438 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f!
+dialog.3d.altitudefactor=\u041a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442 \u043f\u043e \u0432\u044b\u0441\u043e\u0442\u0435
+dialog.3dlines.title=\u0421\u0435\u0442\u043a\u0430 GpsPrune
+dialog.3dlines.empty=\u041d\u0435\u0442 \u0441\u0435\u0442\u043a\u0438 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f!
 dialog.3dlines.intro=\u041a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0441\u0435\u0442\u043a\u0438 3D-\u0432\u0438\u0434\u0430
 
 # Confirm messages
@@ -528,15 +526,15 @@ button.yestoall=\u0414\u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0445
 button.notoall=\u041d\u0435\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445
 button.select=\u0412\u044b\u0431\u0440\u0430\u0442\u044c
 button.selectall=\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435
-button.selectnone=\u041d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043e\u0442\u0431\u0438\u0440\u0430\u0442\u044c
+button.selectnone=\u041e\u0442\u043c\u0435\u043d\u0442\u0438\u0442\u044c \u0432\u044b\u0431\u043e\u0440\u043a\u0443
 button.preview=\u041f\u0440\u0435\u0434\u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440
 button.load=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c
 button.upload=\u0412\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c
 button.guessfields=\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0435 \u043f\u043e\u043b\u044f
-button.showwebpage=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443
+button.showwebpage=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0435\u0431\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443
 button.check=\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430
 button.resettodefaults=\u0421\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0435
-button.browse=\u0423\u043a\u0430\u0437\u0430\u0442\u044c...
+button.browse=\u041e\u0431\u0437\u043e\u0440...
 button.addnew=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u043e\u0435
 button.delete=\u0423\u0434\u0430\u043b\u0438\u0442\u044c
 button.manage=\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c
@@ -580,7 +578,6 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=d
 details.range.avespeed=\u0421\u0440\u0435\u0434\u043d\u044f\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c
-details.range.avemovingspeed=\u0421\u0440\u0435\u0434\u043d\u044f\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0430
 details.range.maxspeed=\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c
 details.range.numsegments=\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u043e\u0432
 details.range.pace=\u0422\u0435\u043c\u043f
@@ -626,14 +623,10 @@ units.feet=\u0444\u0443\u0442\u044b
 units.feet.short=ft
 units.kilometres=\u041a\u0438\u043b\u043e\u043c\u0435\u0442\u0440\u044b
 units.kilometres.short=km
-units.kmh=km/h
 units.kilometresperhour.short=km/h
 units.miles=\u041c\u0438\u043b\u0438
 units.miles.short=mi
-units.mph=mph
 units.milesperhour.short=mph
-units.metrespersec=m/s
-units.feetpersec=ft/s
 units.hours=\u0427\u0430\u0441\u044b
 units.degminsec=\u0413\u0440\u0430\u0434-\u043c\u0438\u043d-\u0441\u0435\u043a
 units.degmin=\u0413\u0440\u0430\u0434-\u043c\u0438\u043d
@@ -645,10 +638,10 @@ url.googlemaps=maps.google.ru
 wikipedia.lang=ru
 
 # Cardinals for 3d plots
-cardinal.n=\u0421
-cardinal.s=\u042e
-cardinal.e=\u0412
-cardinal.w=\u0417
+cardinal.n=\u0421\u0435\u0432\u0435\u0440
+cardinal.s=\u042e\u0433
+cardinal.e=\u0412\u043e\u0441\u0442\u043e\u043a
+cardinal.w=\u0417\u0430\u043f\u0430\u0434
 
 # Undo operations
 undo.load=\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445
@@ -659,7 +652,7 @@ undo.deletepoint=\u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u043e\u0447\u
 undo.removephoto=\u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0444\u043e\u0442\u043e
 undo.removeaudio=\u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u044c
 undo.deleterange=\u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b
-undo.compress=\u0441\u0436\u0430\u0442\u044c \u0442\u0440\u0435\u043a
+undo.deletemarked=\u0441\u0436\u0430\u0442\u044c \u0442\u0440\u0435\u043a
 undo.insert=\u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438
 undo.reverse=\u043f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b
 undo.mergetracksegments=\u0441\u043b\u0438\u044f\u043d\u0438\u0435 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u043e\u0432 \u0442\u0440\u0435\u043a\u0430
index 81602655e35e29df63fe23bdd5be266c8ad4b0f9..d0983667dbcb4621f7a8bb4613fa1fd8a44f888a 100644 (file)
@@ -11,9 +11,9 @@ menu.track.undo=Geri al
 menu.track.clearundo=Geri alma listesi s\u0131f\u0131rla
 menu.point.editpoint=Nokta d\u00fczenle
 menu.point.deletepoint=Noktay\u0131 sil
-menu.range.deleterange=S\u0131ray\u0131 sil
+function.deleterange=S\u0131ray\u0131 sil
 menu.track.deletemarked=Se\u00e7ili noktalar\u0131 sil
-menu.range.interpolate=\u0130nterpolasyon
+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
@@ -183,7 +183,7 @@ dialog.pointedit.changevalue.title=Alan d\u00fczenle
 dialog.pointnameedit.name=Nokta ad\u0131
 dialog.pointnameedit.uppercase=B\u00dcY\u00dcK HARFLER
 dialog.pointnameedit.lowercase=k\u00fc\u00e7\u00fck harfler
-dialog.pointnameedit.sentencecase=\u0130lk Harfi B\u00fcy\u00fck
+dialog.pointnameedit.titlecase=\u0130lk Harfi B\u00fcy\u00fck
 dialog.addtimeoffset.add=Zaman ekle
 dialog.addtimeoffset.subtract=Zaman \u00e7\u0131kart
 dialog.addtimeoffset.days=G\u00fcn
@@ -266,7 +266,6 @@ dialog.saveconfig.prune.languagecode=Dil kodu (TR)
 dialog.saveconfig.prune.gpsdevice=GPS ayg\u0131t
 dialog.saveconfig.prune.gpsformat=GPS bi\u00e7imi
 dialog.saveconfig.prune.povrayfont=Povray yaz\u0131tipi
-dialog.saveconfig.prune.metricunits=Metrik sistemi
 dialog.saveconfig.prune.gnuplotpath=gnuplot'un yeriyolu
 dialog.saveconfig.prune.gpsbabelpath=gpsbabel'in yeriyolu
 dialog.saveconfig.prune.exiftoolpath=exiftool'un yeriyolu
@@ -359,7 +358,6 @@ display.range.time.mins=dak
 display.range.time.hours=saat
 display.range.time.days=g\u011fn
 details.range.avespeed=Ortalama h\u0131z\u0131
-details.range.avemovingspeed=Ortalama hareketi
 details.lists.waypoints=Noktalar
 details.lists.photos=Fotolar
 details.photodetails=Foto ayr\u0131nt\u0131lar\u0131
@@ -393,12 +391,12 @@ units.feet=Ayak
 units.feet.short=ft
 units.kilometres=Kilometre
 units.kilometres.short=km
-units.kmh=km/h
+units.kilometresperhour.short=km/h
 units.miles=Mil
 units.miles.short=mi
-units.mph=mph
-units.metrespersec=m/s
-units.feetpersec=ft/s
+units.milesperhour.short=mph
+units.metrespersec.short=m/s
+units.feetpersec.short=ft/s
 units.hours=saat
 units.degminsec=Derece/Dakika/Saniye
 units.degmin=Derece/Dakika
@@ -421,7 +419,7 @@ undo.editpoint=noktay\u0131 d\u00fczenle
 undo.deletepoint=noktay\u0131 sil
 undo.removephoto=foto kald\u0131r
 undo.deleterange=s\u0131ra sil
-undo.compress=izi s\u0131k\u0131\u015ft\u0131r
+undo.deletemarked=izi s\u0131k\u0131\u015ft\u0131r
 undo.insert=noktalar\u0131 ekle
 undo.reverse=s\u0131ra tersine d\u00f6nd\u00fcr
 undo.mergetracksegments=iz par\u00e7alar\u0131 birle\u015ftir
index bce879697023babcbf6ad2e9f7e828bbcdb3b4b3..dc14d29e4448c6c496b8e18c46cbd8bb88e82857 100644 (file)
@@ -1,16 +1,17 @@
 # Text entries for the GpsPrune application
-# Chinese entries as extra
+# Chinese entries
 
 # Menu entries
 menu.file=\u6587\u4ef6
-menu.file.addphotos=\u6dfb\u52a0\u76f8\u7247
+menu.file.addphotos=\u6dfb\u52a0\u7167\u7247
 menu.file.recentfiles=\u6700\u8fd1\u6253\u5f00\u8fc7\u6587\u4ef6
 menu.file.save=\u4fdd\u5b58
 menu.file.exit=\u9000\u51fa
 menu.track=\u8f68\u8ff9
 menu.track.undo=\u64a4\u9500
 menu.track.clearundo=\u6e05\u9664\u64a4\u9500\u6e05\u5355
-menu.track.deletemarked=\u5220\u9664\u5df2\u6807\u793a\u8f68\u8ff9\u70b9
+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
@@ -20,16 +21,14 @@ menu.range.all=\u5168\u9009
 menu.range.none=\u64a4\u9500\u9009\u62e9
 menu.range.start=\u8bbe\u7f6e\u8d77\u70b9
 menu.range.end=\u8bbe\u7f6e\u7ec8\u70b9
-menu.range.deleterange=\u5220\u9664\u8f68\u8ff9\u70b9\u6bb5
-menu.range.interpolate=\u63d2\u5165\u8f68\u8ff9\u70b9
 menu.range.average=\u8bbe\u7f6e\u5e73\u5747\u8f68\u8ff9\u70b9
-menu.range.reverse=\u8f68\u8ff9\u70b9\u53cd\u5411
+menu.range.reverse=\u8f68\u8ff9\u70b9\u53cd\u8f6c
 menu.range.mergetracksegments=\u5408\u5e76\u8f68\u8ff9\u6bb5
 menu.range.cutandmove=\u79fb\u52a8
 menu.point=\u8f68\u8ff9\u70b9
 menu.point.editpoint=\u7f16\u8f91\u8f68\u8ff9\u70b9
 menu.point.deletepoint=\u5220\u9664\u8f68\u8ff9\u70b9
-menu.photo=\u76f8\u7247
+menu.photo=\u7167\u7247
 menu.photo.saveexif=\u5750\u6807\u4fdd\u5b58\u81f3Exif
 menu.audio=\u58f0\u97f3
 menu.view=\u67e5\u770b
@@ -39,7 +38,7 @@ menu.view.browser.google=Google\u5730\u56fe
 menu.view.browser.openstreetmap=Openstreet\u5730\u56fe
 menu.view.browser.mapquest=Mapquest\u5730\u56fe
 menu.view.browser.yahoo=Yahoo\u5730\u56fe
-menu.view.browser.bing=Bing(\u5fc5\u5e94\uff09\u5730\u56fe
+menu.view.browser.bing=Bing(\u5fc5\u5e94)\u5730\u56fe
 menu.settings=\u8bbe\u7f6e
 menu.settings.onlinemode=\u4ece\u7f51\u4e0a\u5bfc\u5165\u5730\u56fe
 menu.settings.autosave=\u9000\u51fa\u65f6\u81ea\u52a8\u4fdd\u5b58\u8bbe\u7f6e
@@ -54,6 +53,7 @@ menu.map.connect=\u8fde\u63a5\u8f68\u8ff9\u70b9
 menu.map.autopan=\u81ea\u52a8\u7f29\u653e
 menu.map.showmap=\u663e\u793a\u5730\u56fe
 menu.map.showscalebar=\u663e\u793a\u6bd4\u4f8b\u5c3a
+menu.map.editmode=\u7f16\u8f91\u6a21\u5f0f
 
 # Alt keys for menus
 altkey.menu.file=F
@@ -85,22 +85,25 @@ function.exportgpx=\u8f93\u51faGPX\u6587\u4ef6
 function.exportpov=\u8f93\u51faPOV\u6587\u4ef6
 function.exportsvg=\u8f93\u51faSVG\u6587\u4ef6
 function.editwaypointname=\u7f16\u8f91\u822a\u70b9\u540d
-function.compress=\u538b\u7f29\u8f68\u8ff9(\u6807\u793a\u8981\u5220\u9664\u822a\u70b9\uff09
+function.compress=\u538b\u7f29\u8f68\u8ff9(\u6807\u8bb0\u8981\u5220\u9664\u822a\u70b9)
+function.deleterange=\u5220\u9664\u8f68\u8ff9\u70b9\u6bb5
+function.croptrack=\u4fee\u526a\u8f68\u8ff9
+function.interpolate=\u91cd\u53e0\u8f68\u8ff9\u70b9
 function.addtimeoffset=\u52a0\u5165\u65f6\u95f4\u5dee
 function.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb
 function.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4
 function.deletefieldvalues=\u5220\u9664\u533a\u57df\u6570\u503c
 function.findwaypoint=\u67e5\u627e\u822a\u70b9
 function.pastecoordinates=\u8f93\u5165\u65b0\u5750\u6807
-function.charts=\u9ad8\u5ea6\u901f\u5ea6\u56fe\u8868
+function.charts=\u56fe\u8868
 function.show3d=3D\u89c6\u56fe
 function.distances=\u8ddd\u79bb
 function.fullrangedetails=\u5168\u822a\u6bb5\u8be6\u7ec6\u4fe1\u606f
 function.setmapbg=\u80cc\u666f\u5730\u56fe
 function.setkmzimagesize=\u8bbe\u7f6eKMZ\u56fe\u50cf\u5c3a\u5bf8
 function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84
-function.getgpsies=Gpsies\u8f68\u8ff9
-function.uploadgpsies=\u8f68\u8ff9\u4e0a\u4f20\u5230 Gpsies
+function.getgpsies=\u83b7\u53d6Gpsies\u8f68\u8ff9
+function.uploadgpsies=\u4e0a\u4f20\u8f68\u8ff9\u5230Gpsies
 function.lookupsrtm=\u4eceSRTM\u83b7\u5f97\u9ad8\u5ea6\u4fe1\u606f
 function.getwikipedia=\u7ef4\u57fa\u767e\u79d1\u6709\u5173\u672c\u5730\u6587\u7ae0
 function.searchwikipedianames=\u6309\u540d\u5b57\u4ece\u7ef4\u57fa\u767e\u79d1\u67e5\u627e
@@ -109,11 +112,11 @@ function.duplicatepoint=\u590d\u5236\u70b9
 function.setcolours=\u8bbe\u7f6e\u989c\u8272
 function.setlinewidth=\u8bbe\u7f6e\u7ebf\u4f53\u5bbd\u5ea6
 function.setlanguage=\u8bbe\u7f6e\u8bed\u8a00
-function.connecttopoint=\u94fe\u63a5\u76f8\u7247
+function.connecttopoint=\u94fe\u63a5\u5230\u5f53\u524d\u70b9
 function.disconnectfrompoint=\u64a4\u9500\u94fe\u63a5
 function.removephoto=\u5220\u9664\u7167\u7247
-function.correlatephotos=\u94fe\u63a5\u76f8\u7247
-function.rearrangephotos=\u91cd\u6392\u76f8\u7247
+function.correlatephotos=\u94fe\u63a5\u7167\u7247\u7167
+function.rearrangephotos=\u91cd\u6392\u7167\u7247
 function.rotatephotoleft=\u5de6\u65cb\u8f6c
 function.rotatephotoright=\u53f3\u65cb\u8f6c
 function.photopopup=\u663e\u793a\u5f39\u51fa\u7167\u7247
@@ -137,9 +140,10 @@ dialog.exit.confirm.text=\u6570\u636e\u672a\u4fdd\u5b58\uff0c\u662f\u5426\u9000\
 dialog.openappend.title=\u9644\u52a0\u81f3\u5df2\u5bfc\u5165\u6570\u636e
 dialog.openappend.text=\u9644\u52a0\u73b0\u6709\u6570\u636e\u81f3\u5df2\u5bfc\u5165\u6570\u636e
 dialog.deletepoint.title=\u5220\u9664\u8f68\u8ff9\u70b9
-dialog.deletepoint.deletephoto=\u5220\u9664\u8f68\u8ff9\u70b9\u94fe\u63a5\u7684\u76f8\u7247\uff1f
-dialog.deletephoto.title=\u5220\u9664\u76f8\u7247
-dialog.deletephoto.deletepoint=\u5220\u9664\u76f8\u7247\u94fe\u63a5\u7684\u8f68\u8ff9\u70b9\uff1f
+dialog.deletepoint.deletephoto=\u5220\u9664\u94fe\u63a5\u5230\u8f68\u8ff9\u70b9\u7684\u7167\u7247\uff1f
+dialog.deletephoto.title=\u5220\u9664\u7167\u7247
+dialog.deletephoto.deletepoint=\u5220\u9664\u94fe\u63a5\u5230\u7167\u7247\u7684\u8f68\u8ff9\u70b9\uff1f
+dialog.deleteaudio.deletepoint=\u5220\u9664\u94fe\u63a5\u5230\u97f3\u9891\u7684\u8f68\u8ff9\u70b9\uff1f
 dialog.openoptions.title=\u6253\u5f00\u9009\u9879
 dialog.openoptions.filesnippet=\u63d0\u53d6\u6587\u4ef6\u7247\u6bb5
 dialog.load.table.field=\u6570\u636e\u6bb5
@@ -159,10 +163,10 @@ dialog.open.contentsdoubled=\u6587\u4ef6\u542b\u6709\u4e24\u5957\u70b9\u4fe1\u60
 dialog.selecttracks.intro=\u9009\u62e9\u8981\u5bfc\u5165\u7684\u8f68\u8ff9
 dialog.selecttracks.noname=\u672a\u547d\u540d
 dialog.jpegload.subdirectories=\u542b\u6b21\u7ea7\u65b9\u4f4d
-dialog.jpegload.loadjpegswithoutcoords=\u542b\u65e0\u5750\u6807\u70b9\u76f8\u7247
-dialog.jpegload.loadjpegsoutsidearea=\u542b\u533a\u57df\u5916\u76f8\u7247
-dialog.jpegload.progress.title=\u5bfc\u5165\u76f8\u7247
-dialog.jpegload.progress=\u8bf7\u7b49\u5f85\uff0c\u6b63\u641c\u7d22\u76f8\u7247
+dialog.jpegload.loadjpegswithoutcoords=\u542b\u65e0\u5750\u6807\u70b9\u7167\u7247
+dialog.jpegload.loadjpegsoutsidearea=\u542b\u533a\u57df\u5916\u7167\u7247
+dialog.jpegload.progress.title=\u5bfc\u5165\u7167\u7247
+dialog.jpegload.progress=\u8bf7\u7b49\u5f85\uff0c\u6b63\u641c\u7d22\u7167\u7247
 dialog.gpsload.nogpsbabel=\u627e\u4e0d\u5230Gpsbabel\uff0c\u7ee7\u7eed\uff1f
 dialog.gpsload.device=GPS\u8bbe\u5907\u540d\u79f0
 dialog.gpsload.format=GPS\u6587\u4ef6\u683c\u5f0f
@@ -185,9 +189,9 @@ dialog.save.overwrite.title=\u6587\u4ef6\u5df2\u5b58\u5728
 dialog.save.overwrite.text=\u6587\u4ef6\u5df2\u5b58\u5728\uff0c\u662f\u5426\u8986\u76d6\uff1f
 dialog.save.notypesselected=\u70b9\u7c7b\u578b\u672a\u9009\u5b9a
 dialog.exportkml.text=\u6570\u636e\u540d\u79f0
-dialog.exportkml.altitude=\u7edd\u5bf9\u9ad8\u5ea6\uff08\u822a\u7a7a\u7528\uff09
+dialog.exportkml.altitude=\u7edd\u5bf9\u9ad8\u5ea6(\u822a\u7a7a\u7528)
 dialog.exportkml.kmz=\u538b\u7f29\u6210KMZ\u6587\u4ef6
-dialog.exportkml.exportimages=\u8f93\u51fa\u76f8\u7247\u7f29\u7565\u56fe\u81f3KMZ
+dialog.exportkml.exportimages=\u8f93\u51fa\u7f29\u7565\u56fe\u81f3KMZ
 dialog.exportkml.trackcolour=\u8f68\u8ff9\u989c\u8272
 dialog.exportgpx.name=\u540d\u79f0
 dialog.exportgpx.desc=\u63cf\u8ff0
@@ -212,32 +216,32 @@ dialog.exportsvg.gradients=\u4f7f\u7528\u6e10\u53d8\u8272
 dialog.pointtype.desc=\u4fdd\u5b58\u4e0b\u5217\u70b9\uff1a
 dialog.pointtype.track=\u8f68\u8ff9\u70b9
 dialog.pointtype.waypoint=\u822a\u70b9
-dialog.pointtype.photo=\u76f8\u7247\u70b9
+dialog.pointtype.photo=\u7167\u7247\u70b9
 dialog.pointtype.audio=\u5e26\u58f0\u97f3\u7684\u822a\u70b9
 dialog.pointtype.selection=\u4ec5\u5df2\u9009\u62e9\u822a\u6bb5
-dialog.confirmreversetrack.title=\u786e\u8ba4\u53cd\u5411
-dialog.confirmreversetrack.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u53cd\u5411\u540e\u53ef\u80fd\u4e22\u5931\u3002\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.confirmreversetrack.title=\u786e\u8ba4\u53cd\u8f6c
+dialog.confirmreversetrack.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u53cd\u8f6c\u540e\u53ef\u80fd\u4e22\u5931\u3002\n\u662f\u5426\u7ee7\u7eed\uff1f
 dialog.confirmcutandmove.title=\u786e\u8ba4\u526a\u5207\u548c\u79fb\u52a8
 dialog.confirmcutandmove.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u79fb\u52a8\u540e\u53ef\u80fd\u4e22\u5931\u3002\n\u662f\u5426\u7ee7\u7eed\uff1f
-dialog.interpolate.title=\u91cd\u53e0\u8f68\u8ff9\u70b9
 dialog.interpolate.parameter.text=\u6240\u9009\u4e24\u70b9\u4e2d\u63d2\u5165\u70b9\u7684\u4e2a\u6570
-dialog.undo.title=\u64a4\u9500\u52a8\u4f5c
-dialog.undo.pretext=\u8bf7\u9009\u62e9\u8981\u64a4\u9500\u7684\u52a8\u4f5c
+dialog.interpolate.betweenwaypoints=\u521b\u5efa\u4e2d\u7ee7\u822a\u70b9\uff1f
+dialog.undo.title=\u64a4\u9500\u64cd\u4f5c
+dialog.undo.pretext=\u8bf7\u9009\u62e9\u8981\u64a4\u9500\u7684\u64cd\u4f5c
 dialog.undo.none.title=\u4e0d\u80fd\u64a4\u9500
-dialog.undo.none.text=\u65e0\u52a8\u4f5c\u53ef\u64a4\u9500
-dialog.clearundo.title=\u6e05\u9664\u64a4\u9500\u6e05\u5355
-dialog.clearundo.text=\u662f\u5426\u786e\u5b9e\u8981\u6e05\u9664\u64a4\u9500\u6e05\u5355\uff1f\n\u64a4\u9500\u4fe1\u606f\u4f1a\u4e22\u5931\uff01
+dialog.undo.none.text=\u65e0\u64cd\u4f5c\u53ef\u64a4\u9500
+dialog.clearundo.title=\u6e05\u9664\u64a4\u9500\u64cd\u4f5c\u6e05\u5355
+dialog.clearundo.text=\u662f\u5426\u786e\u5b9e\u8981\u6e05\u9664\u64a4\u9500\u64cd\u4f5c\u6e05\u5355\uff1f\n\u64a4\u9500\u64cd\u4f5c\u4fe1\u606f\u4f1a\u4e22\u5931\uff01
 dialog.pointedit.title=\u7f16\u8f91\u8f68\u8ff9\u70b9
 dialog.pointedit.text=\u9009\u62e9\u8981\u7f16\u8f91\u7684\u533a\u57df\u5e76\u7528\u201c\u7f16\u8f91\u201d\u952e\u6539\u53d8\u6570\u503c
 dialog.pointedit.table.field=\u6570\u636e\u6bb5
 dialog.pointedit.table.value=\u6570\u503c
-dialog.pointedit.table.changed=\u6539\u53d8
+dialog.pointedit.table.changed=\u5df2\u6539\u53d8
 dialog.pointedit.changevalue.text=\u8f93\u5165\u65b0\u6570\u503c
 dialog.pointedit.changevalue.title=\u7f16\u8f91\u6570\u636e\u6bb5
 dialog.pointnameedit.name=\u822a\u70b9\u540d\u79f0
 dialog.pointnameedit.uppercase=\u4e0a\u6863\u952e
 dialog.pointnameedit.lowercase=\u4e0b\u6863\u952e
-dialog.pointnameedit.sentencecase=\u6807\u9898\u952e
+dialog.pointnameedit.titlecase=\u6807\u9898\u952e
 dialog.addtimeoffset.add=\u5ef6\u540e\u65f6\u95f4
 dialog.addtimeoffset.subtract=\u63d0\u524d\u65f6\u95f4
 dialog.addtimeoffset.days=\u5929\u6570
@@ -247,10 +251,10 @@ dialog.addtimeoffset.notimestamps=\u4e0d\u80fd\u6dfb\u52a0\u65f6\u95f4\u56e0\u4e
 dialog.findwaypoint.intro=\u8f93\u5165\u90e8\u5206\u822a\u70b9\u540d
 dialog.findwaypoint.search=\u641c\u7d22
 dialog.saveexif.title=\u4fdd\u5b58Exif
-dialog.saveexif.intro=\u9009\u62e9\u8981\u4fdd\u5b58\u7684\u76f8\u7247
+dialog.saveexif.intro=\u9009\u62e9\u8981\u4fdd\u5b58\u7684\u7167\u7247
 dialog.saveexif.nothingtosave=\u5750\u6807\u672a\u6539\u53d8\uff0c\u65e0\u4fdd\u5b58\u5185\u5bb9
-dialog.saveexif.noexiftool=\u672a\u627e\u5230Exif \u5de5\u5177\uff0c\u7ee7\u7eed\uff1f
-dialog.saveexif.table.photoname=\u76f8\u7247\u540d
+dialog.saveexif.noexiftool=\u672a\u627e\u5230Exiftool\uff0c\u7ee7\u7eed\uff1f
+dialog.saveexif.table.photoname=\u7167\u7247\u540d
 dialog.saveexif.table.status=\u72b6\u6001
 dialog.saveexif.table.save=\u4fdd\u5b58
 dialog.saveexif.photostatus.connected=\u5df2\u94fe\u63a5
@@ -273,6 +277,8 @@ dialog.distances.column.to=\u5230\u6b64\u70b9
 dialog.distances.currentpoint=\u5f53\u524d\u70b9
 dialog.distances.toofewpoints=\u9700\u8981\u822a\u70b9\u6765\u8ba1\u7b97\u8ddd\u79bb
 dialog.fullrangedetails.intro=\u822a\u6bb5\u8be6\u60c5
+dialog.fullrangedetails.coltotal=\u5305\u542b\u95f4\u65ad
+dialog.fullrangedetails.colsegments=\u4e0d\u5305\u542b\u95f4\u65ad
 dialog.setmapbg.intro=\u8bf7\u9009\u62e9\u5730\u56fe\uff0c\u6216\u6dfb\u52a0\u5730\u56fe
 dialog.addmapsource.title=\u6dfb\u52a0\u5730\u56fe
 dialog.addmapsource.sourcename=\u5730\u56fe\u6765\u6e90\u540d\u79f0
@@ -288,43 +294,44 @@ dialog.gpsies.nodescription=\u65e0\u63cf\u8ff0
 dialog.gpsies.nonefound=\u672a\u627e\u5230\u8f68\u8ff9
 dialog.gpsies.username=Gpsies\u7f51\u7ad9\u7528\u6237\u540d
 dialog.gpsies.password=Gpsies\u7f51\u7ad9\u5bc6\u7801
-dialog.gpsies.keepprivate=\u8f68\u8ff9\u4fdd\u5bc6\uff08\u4e0d\u516c\u5f00\uff09
+dialog.gpsies.keepprivate=\u4e0d\u516c\u5f00\u8f68\u8ff9
 dialog.gpsies.confirmopenpage=\u6253\u5f00\u4e0a\u4f20\u8f68\u8ff9\u7684\u7f51\u7ad9\uff1f
 dialog.gpsies.activities=\u6d3b\u52a8\u7c7b\u578b
 dialog.gpsies.activity.trekking=\u5f92\u6b65
-dialog.gpsies.activity.walking=\u7ade\u8d70
+dialog.gpsies.activity.walking=\u6b65\u884c
 dialog.gpsies.activity.jogging=\u8dd1\u6b65
-dialog.gpsies.activity.biking=\u9a91\u81ea\u884c\u8f66
+dialog.gpsies.activity.biking=\u81ea\u884c\u8f66
 dialog.gpsies.activity.motorbiking=\u7535\u52a8\u81ea\u884c\u8f66
 dialog.gpsies.activity.snowshoe=\u96ea\u978b\u5065\u884c
 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.correlate.notimestamps=\u6570\u636e\u70b9\u4e2d\u65e0\u65f6\u95f4\u4fe1\u606f\uff0c\u76f8\u7247\u65e0\u6cd5\u94fe\u63a5
-dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u76f8\u7247\u5df2\u94fe\u63a5\u3002\u7ee7\u7eed\uff1f
-dialog.correlate.photoselect.intro=\u9009\u62e9\u5df2\u94fe\u63a5\u76f8\u7247\u4f5c\u4e3a\u65f6\u95f4\u504f\u79fb
-dialog.correlate.select.photoname=\u76f8\u7247\u540d
+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\u3002\u7ee7\u7eed\uff1f
+dialog.correlate.nouncorrelatedaudios=\u6240\u6709\u97f3\u9891\u5df2\u94fe\u63a5\u3002\u7ee7\u7eed\uff1f
+dialog.correlate.photoselect.intro=\u9009\u62e9\u5df2\u94fe\u63a5\u7167\u7247\u4f5c\u4e3a\u65f6\u95f4\u504f\u79fb
+dialog.correlate.select.photoname=\u7167\u7247\u540d
 dialog.correlate.select.timediff=\u65f6\u95f4\u5dee
-dialog.correlate.select.photolater=\u76f8\u7247\u5ef6\u540e
-dialog.correlate.options.tip=\u63d0\u793a\uff1a\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u76f8\u7247\uff0c\u53ef\u81ea\u52a8\u8ba1\u7b97\u65f6\u95f4\u504f\u79fb
+dialog.correlate.select.photolater=\u7167\u7247\u5ef6\u540e
+dialog.correlate.options.tip=\u63d0\u793a\uff1a\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u7167\u7247\uff0c\u53ef\u81ea\u52a8\u8ba1\u7b97\u65f6\u95f4\u504f\u79fb
 dialog.correlate.options.intro=\u9009\u62e9\u81ea\u52a8\u94fe\u63a5\u8bbe\u7f6e
 dialog.correlate.options.offsetpanel=\u65f6\u95f4\u504f\u79fb
 dialog.correlate.options.offset=\u504f\u79fb
 dialog.correlate.options.offset.hours=\u5c0f\u65f6
-dialog.correlate.options.offset.minutes=\u5206\u949f
+dialog.correlate.options.offset.minutes=\u5206
 dialog.correlate.options.offset.seconds=\u79d2
-dialog.correlate.options.photolater=\u76f8\u7247\u6ede\u540e\u4e8e\u8f68\u8ff9\u70b9
-dialog.correlate.options.pointlaterphoto=\u8f68\u8ff9\u70b9\u6ede\u540e\u4e8e\u76f8\u7247
-dialog.correlate.options.audiolater=\u58f0\u97f3\u6ede\u540e\u4e8e\u822a\u70b9
-dialog.correlate.options.pointlateraudio=\u822a\u70b9\u6ede\u540e\u4e8e\u58f0\u97f3
+dialog.correlate.options.photolater=\u7167\u7247\u6ede\u540e\u4e8e\u8f68\u8ff9\u70b9
+dialog.correlate.options.pointlaterphoto=\u8f68\u8ff9\u70b9\u6ede\u540e\u4e8e\u7167\u7247
+dialog.correlate.options.audiolater=\u97f3\u9891\u6ede\u540e\u4e8e\u822a\u70b9
+dialog.correlate.options.pointlateraudio=\u822a\u70b9\u6ede\u540e\u4e8e\u97f3\u9891
 dialog.correlate.options.limitspanel=\u5173\u8054\u9650\u5236
 dialog.correlate.options.notimelimit=\u65e0\u65f6\u95f4\u9650\u5236
 dialog.correlate.options.timelimit=\u65f6\u95f4\u9650\u5236
 dialog.correlate.options.nodistancelimit=\u65e0\u8ddd\u79bb\u9650\u5236
 dialog.correlate.options.distancelimit=\u8ddd\u79bb\u9650\u5236
 dialog.correlate.options.correlate=\u5173\u8054
-dialog.correlate.alloutsiderange=\u65e0\u6cd5\u94fe\u63a5\uff0c\u6240\u6709\u76f8\u7247\u8d85\u51fa\u8f68\u8ff9\u65f6\u95f4\u8303\u56f4\u3002\n\u8bf7\u6539\u53d8\u65f6\u95f4\u504f\u79fb\u6216\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u76f8\u7247\u3002
+dialog.correlate.alloutsiderange=\u65e0\u6cd5\u94fe\u63a5\uff0c\u6240\u6709\u7167\u7247\u8d85\u51fa\u8f68\u8ff9\u65f6\u95f4\u8303\u56f4\u3002\n\u8bf7\u6539\u53d8\u65f6\u95f4\u504f\u79fb\u6216\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u5728\u7167\u7247\u3002
 dialog.correlate.filetimes=\u6587\u4ef6\u65f6\u95f4\u8868\u793a\u58f0\u97f3\u7684\uff1a
 dialog.correlate.filetimes2=\u90e8\u5206
 dialog.correlate.correltimes=\u5982\u8981\u5173\u8054\uff0c\u8bf7\u4f7f\u7528\uff1a
@@ -334,13 +341,12 @@ 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.rearrangephotos.desc=\u9009\u62e9\u76ee\u7684\u5730\u53ca\u6392\u76f8\u7247\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.compress.nonefound=\u65e0\u6cd5\u5220\u9664\u6570\u636e\u70b9
 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
@@ -351,6 +357,10 @@ dialog.compress.duplicates.title=\u91cd\u590d\u70b9\u5220\u9664
 dialog.compress.douglaspeucker.title=Douglas-Peucker \u538b\u7f29
 dialog.compress.douglaspeucker.paramdesc=\u95f4\u8ddd\u7cfb\u6570
 dialog.compress.summarylabel=\u8981\u5220\u9664\u7684\u70b9
+dialog.compress.confirm1=\u5df2\u6807\u8bb0
+dialog.compress.confirm2=\u70b9\u3002\n\u70b9\u51fb \u8f68\u8ff9->\u5220\u9664 \u5220\u9664\u8fd9\u4e9b\u70b9
+dialog.compress.confirmnone=\u672a\u6807\u8bb0\u4efb\u4f55\u70b9
+dialog.deletemarked.nonefound=\u65e0\u6cd5\u5220\u9664\u6570\u636e\u70b9
 dialog.pastecoordinates.desc=\u5728\u6b64\u8f93\u5165\u6216\u7c98\u8d34\u5750\u6807\u70b9
 dialog.pastecoordinates.coords=\u5750\u6807\u70b9
 dialog.pastecoordinates.nothingfound=\u8bf7\u68c0\u67e5\u5750\u6807\u6570\u636e\u5e76\u91cd\u8bd5
@@ -372,24 +382,24 @@ dialog.about.systeminfo.gpsbabel=Gpsbabel \u662f\u5426\u5b89\u88c5
 dialog.about.systeminfo.gnuplot=Gnuplot \u662f\u5426\u5b89\u88c5
 dialog.about.systeminfo.exiflib=Exif \u5e93
 dialog.about.systeminfo.exiflib.internal=\u5185\u90e8
-dialog.about.systeminfo.exiflib.internal.failed=\u5185\u90e8\uff08\u672a\u627e\u5230\uff09
+dialog.about.systeminfo.exiflib.internal.failed=\u5185\u90e8(\u672a\u627e\u5230)
 dialog.about.systeminfo.exiflib.external=\u5916\u90e8
-dialog.about.systeminfo.exiflib.external.failed=\u5916\u90e8\uff08\u672a\u627e\u5230\uff09
+dialog.about.systeminfo.exiflib.external.failed=\u5916\u90e8(\u672a\u627e\u5230)
 dialog.about.yes=\u662f
 dialog.about.no=\u5426
 dialog.about.credits=\u81f4\u8c22
 dialog.about.credits.code=GpsPrune \u539f\u7801\u7f16\u5199
 dialog.about.credits.exifcode=Exif \u539f\u7801\u7f16\u5199
-dialog.about.credits.icons=\u56fe\u6807\u6765\u81ea\u4e8e
+dialog.about.credits.icons=\u90e8\u5206\u56fe\u6807\u6765\u81ea\u4e8e
 dialog.about.credits.translators=\u8bd1\u8005
-dialog.about.credits.translations=\u7ffb\u8bd1\u52a9\u7406
+dialog.about.credits.translations=\u534f\u52a9\u7ffb\u8bd1
 dialog.about.credits.devtools=\u5f00\u53d1\u5de5\u5177
 dialog.about.credits.othertools=\u5176\u4ed6\u5de5\u5177
-dialog.about.credits.thanks=\u81f3\u8c22
+dialog.about.credits.thanks=\u81f4\u8c22
 dialog.about.readme=\u7248\u672c\u4fe1\u606f
 dialog.checkversion.error=\u65e0\u6cd5\u68c0\u6d4b\u7248\u672c\u66f4\u65b0\n\u8bf7\u68c0\u67e5\u7f51\u7edc\u8fde\u63a5
 dialog.checkversion.uptodate=\u4f60\u4f7f\u7528\u7684\u5df2\u662f\u6700\u65b0\u7248\u672c
-dialog.checkversion.newversion1=\u65b0\u7248\u672c\u5b58\u5728\uff0c \u6700\u65b0\u7248\u672c\u53f7\u662f\uff1a
+dialog.checkversion.newversion1=\u53d1\u73b0\u65b0\u7248\u672c\uff01\u6700\u65b0\u7248\u672c\u662f\uff1a
 dialog.checkversion.newversion2=
 dialog.checkversion.releasedate1=\u65b0\u7248\u672c\u53d1\u884c\u4e8e
 dialog.checkversion.releasedate2=
@@ -400,18 +410,17 @@ dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
 dialog.saveconfig.desc=\u4e0b\u5217\u8bbe\u7f6e\u5c06\u4fdd\u5b58\u5230\u8bbe\u7f6e\u6587\u4ef6
 dialog.saveconfig.prune.trackdirectory=\u8f68\u8ff9\u6587\u4ef6\u5939
-dialog.saveconfig.prune.photodirectory=\u76f8\u7247\u6587\u4ef6\u5939
+dialog.saveconfig.prune.photodirectory=\u7167\u7247\u6587\u4ef6\u5939
 dialog.saveconfig.prune.languagecode=\u8bed\u8a00\u9009\u62e9(ZH)
 dialog.saveconfig.prune.languagefile=\u8bed\u8a00\u6587\u4ef6\u5305
 dialog.saveconfig.prune.gpsdevice=GPS\u7aef\u53e3\u540d\u79f0
 dialog.saveconfig.prune.gpsformat=GPS\u6587\u4ef6\u683c\u5f0f
 dialog.saveconfig.prune.povrayfont=Povray \u5b57\u4f53
-dialog.saveconfig.prune.metricunits=\u4f7f\u7528\u516c\u5236\uff1f
 dialog.saveconfig.prune.gnuplotpath=gnuplot\u8def\u5f84
 dialog.saveconfig.prune.gpsbabelpath=gpsbabel\u8def\u5f84
 dialog.saveconfig.prune.exiftoolpath=exiftool\u8def\u5f84
-dialog.saveconfig.prune.mapsource=\u5df2\u9009\u62e9\u7684\u5730\u56fe\u6e90
-dialog.saveconfig.prune.mapsourcelist=\u5730\u56fe\u6e90
+dialog.saveconfig.prune.mapsource=\u5df2\u9009\u62e9\u7684\u5730\u56fe\u6570\u636e\u6e90
+dialog.saveconfig.prune.mapsourcelist=\u5730\u56fe\u6570\u636e\u6e90
 dialog.saveconfig.prune.diskcache=\u5b58\u50a8\u8def\u5f84
 dialog.saveconfig.prune.kmzimagewidth=KMZ\u56fe\u50cf\u5bbd\u5ea6
 dialog.saveconfig.prune.kmzimageheight=KMZ\u56fe\u50cf\u9ad8\u5ea6
@@ -419,7 +428,7 @@ dialog.saveconfig.prune.colourscheme=\u989c\u8272
 dialog.saveconfig.prune.linewidth=\u7ebf\u4f53\u5bbd\u5ea6
 dialog.saveconfig.prune.kmltrackcolour=KML\u8f68\u8ff9\u989c\u8272
 dialog.saveconfig.prune.autosavesettings=\u81ea\u52a8\u4fdd\u5b58\u8bbe\u7f6e
-dialog.setpaths.intro=\u82e5\u9700\u8981\uff0c\u53ef\u8bbe\u5b9a\u5916\u6302\u7a0b\u5e8f\u8def\u5f84
+dialog.setpaths.intro=\u5982\u679c\u9700\u8981\uff0c\u53ef\u8bbe\u5b9a\u5916\u6302\u7a0b\u5e8f\u8def\u5f84
 dialog.setpaths.found=\u627e\u5230\u8def\u5f84\uff1f
 dialog.addaltitude.noaltitudes=\u8f68\u8ff9\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f
 dialog.addaltitude.desc=\u9ad8\u5ea6\u504f\u79fb
@@ -443,26 +452,27 @@ dialog.setlanguage.language=\u8bed\u8a00
 dialog.setlanguage.languagefile=\u8bed\u8a00\u5305
 dialog.setlanguage.endmessage=\u73b0\u5728\u8bf7\u4fdd\u5b58\u8bbe\u7f6e\u5e76\u91cd\u542fGpsPrune\n\u4f7f\u8bbe\u7f6e\u751f\u6548
 dialog.setlanguage.endmessagewithautosave=\u8981\u4f7f\u6240\u9009\u8bed\u8a00\u751f\u6548\uff0c\u8bf7\u91cd\u65b0\u542f\u52a8GosPrune
-dialog.diskcache.save=\u5730\u56fe\u56fe\u7247\u4fdd\u5b58\u5230\u7535\u8111
-dialog.diskcache.dir=\u4fdd\u5b58\u8def\u5f84
-dialog.diskcache.createdir=\u65b0\u5efa\u8def\u5f84
-dialog.diskcache.nocreate=\u672a\u65b0\u5efa\u8def\u5f84
+dialog.diskcache.save=\u4fdd\u5b58\u5730\u56fe\u56fe\u7247
+dialog.diskcache.dir=\u4e34\u65f6\u6587\u4ef6\u5939
+dialog.diskcache.createdir=\u65b0\u5efa\u6587\u4ef6\u5939
+dialog.diskcache.nocreate=\u6587\u4ef6\u5939\u672a\u521b\u5efa
+dialog.diskcache.cannotwrite=\u65e0\u6cd5\u5728\u6307\u5b9a\u6587\u4ef6\u5939\u4e0b\u4fdd\u5b58\u5730\u56fe\u5757
 dialog.diskcache.table.path=\u8def\u5f84
 dialog.diskcache.table.usedby=\u4f7f\u7528\u8005
-dialog.diskcache.table.zoom=\u653e\u5927\u7f29\u5c0f
-dialog.diskcache.table.tiles=\u627e\u5230\u7684\u5730\u56fe\u533a\u57df\u6570\u636e
+dialog.diskcache.table.zoom=\u7f29\u653e\u7ea7\u522b
+dialog.diskcache.table.tiles=\u5730\u56fe\u5757
 dialog.diskcache.table.megabytes=MB
-dialog.diskcache.tileset=\u5730\u56fe\u533a\u57df\u6570\u5b58\u653e\u8def\u5f84
+dialog.diskcache.tileset=\u5730\u56fe\u5757\u5b58\u653e\u8def\u5f84
 dialog.diskcache.tileset.multiple=\u6570\u76ee
-dialog.diskcache.deleteold=\u5220\u9664\u65e7\u533a\u57df\u6570\u636e
+dialog.diskcache.deleteold=\u5220\u9664\u65e7\u5730\u56fe\u5757
 dialog.diskcache.maximumage=\u6700\u957f\u65f6\u95f4(\u5929)
-dialog.diskcache.deleteall=\u5220\u9664\u6240\u6709\u533a\u57df\u6570\u636e
+dialog.diskcache.deleteall=\u5220\u9664\u6240\u6709\u5730\u56fe\u5757
 dialog.diskcache.deleted1=\u5df2\u5220\u9664
 dialog.diskcache.deleted2=\u7f13\u5b58\u5185\u6587\u4ef6
 dialog.deletefieldvalues.intro=\u9009\u62e9\u5f53\u524d\u8303\u56f4\u5185\u8981\u5220\u9664\u7684\u533a\u57df
-dialog.setlinewidth.text=\u8f93\u5165\u8f68\u8ff9\u7ebf\u5bbd\u50cf\u7d20\u503c\uff081-4\uff09
-dialog.downloadosm.desc=\u786e\u8ba4\u4eceOSM\u4e0b\u8f7d\u8be5\u5730\u533a\u539f\u59cb\u6570\u636e
-dialog.searchwikipedianames.search=\u67e5\u627e
+dialog.setlinewidth.text=\u8f93\u5165\u8f68\u8ff9\u7ebf\u5bbd\u50cf\u7d20\u503c(1-4)
+dialog.downloadosm.desc=\u786e\u8ba4\u4eceOSM\u4e0b\u8f7d\u8be5\u5730\u533a\u539f\u59cb\u6570\u636e:
+dialog.searchwikipedianames.search=\u67e5\u627e:
 
 # 3d window
 dialog.3d.title=GpsPrune 3D \u663e\u793a
@@ -479,27 +489,28 @@ confirm.deletepoint.single=\u5df1\u5220\u9664\u7684\u8f68\u8ff9\u70b9
 confirm.deletepoint.multi=\u5df2\u5220\u9664\u7684\u8f68\u8ff9\u70b9
 confirm.point.edit=\u5df2\u7f16\u8f91\u7684\u8f68\u8ff9\u70b9
 confirm.mergetracksegments=\u5df2\u5408\u5e76\u7684\u8f68\u8ff9\u6bb5
-confirm.reverserange=\u53cd\u5411\u7684\u8303\u56f4
+confirm.reverserange=\u53cd\u8f6c\u7684\u8303\u56f4
 confirm.addtimeoffset=\u5df2\u52a0\u4e0a\u65f6\u95f4\u504f\u5dee
 confirm.addaltitudeoffset=\u5df2\u52a0\u4e0a\u9ad8\u5ea6\u504f\u5dee
 confirm.rearrangewaypoints=\u91cd\u65b0\u914d\u7f6e\u7684\u822a\u70b9
-confirm.rearrangephotos=\u76f8\u7247\u5df2\u91cd\u6392
+confirm.rearrangephotos=\u7167\u7247\u5df2\u91cd\u6392
 confirm.cutandmove=\u5df2\u79fb\u52a8\u7684\u8f68\u8ff9\u6bb5
+confirm.interpolate=\u8f68\u8ff9\u70b9\u5df2\u6dfb\u52a0
 confirm.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u5df2\u8f6c\u6362
 confirm.saveexif.ok1=\u5df2\u4fdd\u5b58
-confirm.saveexif.ok2=\u76f8\u7247\u6587\u4ef6
+confirm.saveexif.ok2=\u7167\u7247\u6587\u4ef6
 confirm.undo.single=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c
 confirm.undo.multi=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c
-confirm.jpegload.single=\u5df2\u52a0\u5165\u76f8\u7247
-confirm.jpegload.multi=\u5df2\u52a0\u5165\u76f8\u7247
+confirm.jpegload.single=\u5df2\u52a0\u5165\u7167\u7247
+confirm.jpegload.multi=\u5df2\u52a0\u5165\u7167\u7247
 confirm.media.connect=\u5a92\u4f53\u5df2\u5173\u8054
-confirm.photo.disconnect=\u76f8\u7247\u672a\u94fe\u63a5
+confirm.photo.disconnect=\u7167\u7247\u672a\u94fe\u63a5
 confirm.audio.disconnect=\u58f0\u97f3\u672a\u94fe\u63a5
 confirm.media.removed=\u5df2\u5220\u9664
-confirm.correlatephotos.single=\u76f8\u7247\u5df2\u94fe\u63a5
-confirm.correlatephotos.multi=\u76f8\u7247\u5df2\u94fe\u63a5
+confirm.correlatephotos.single=\u7167\u7247\u5df2\u94fe\u63a5
+confirm.correlatephotos.multi=\u7167\u7247\u5df2\u94fe\u63a5
 confirm.createpoint=\u5df2\u521b\u5efa\u70b9
-confirm.rotatephoto=\u76f8\u7247\u5df2\u65cb\u8f6c
+confirm.rotatephoto=\u7167\u7247\u5df2\u65cb\u8f6c
 confirm.running=\u8bf7\u7a0d\u7b49...
 confirm.lookupsrtm1=\u627e\u5230
 confirm.lookupsrtm2=\u9ad8\u5ea6\u503c
@@ -565,7 +576,7 @@ details.pointdetails=\u8f68\u8ff9\u70b9\u4fe1\u606f
 details.index.selected=\u7b2c
 details.index.of=\u70b9\u53d6\u81ea
 details.nopointselection=\u6ca1\u6709\u9009\u62e9\u70b9
-details.photofile=\u76f8\u7247\u6587\u4ef6
+details.photofile=\u7167\u7247\u6587\u4ef6
 details.norangeselection=\u6ca1\u6709\u9009\u62e9\u8f68\u8ff9/\u822a\u70b9\u6bb5
 details.rangedetails=\u8f68\u8ff9/\u822a\u70b9\u6bb5\u4fe1\u606f
 details.range.selected=\u9009\u4e2d
@@ -580,19 +591,19 @@ display.range.time.mins=\u5206
 display.range.time.hours=\u5c0f\u65f6
 display.range.time.days=\u5929
 details.range.avespeed=\u5e73\u5747\u901f\u5ea6
-details.range.avemovingspeed=\u5e73\u5747\u79fb\u52a8
 details.range.maxspeed=\u6700\u5927\u901f\u5ea6
 details.range.numsegments=\u6bb5\u6570
 details.range.pace=\u6b65\u901f
 details.range.gradient=\u5761\u5ea6
 details.lists.waypoints=\u822a\u70b9
-details.lists.photos=\u76f8\u7247
+details.lists.photos=\u7167\u7247
 details.lists.audio=\u58f0\u97f3
-details.photodetails=\u76f8\u7247\u4fe1\u606f
-details.nophoto=\u65e0\u76f8\u7247\u88ab\u9009\u4e2d
-details.photo.loading=\u6b63\u5bfc\u5165
+details.photodetails=\u7167\u7247\u4fe1\u606f
+details.nophoto=\u6ca1\u6709\u9009\u4e2d\u7167\u7247
+details.photo.loading=\u5bfc\u5165\u4e2d
 details.photo.bearing=\u65b9\u5411
 details.media.connected=\u5df2\u94fe\u63a5
+details.media.fullpath=\u5b8c\u6574\u8def\u5f84
 details.audiodetails=\u8be6\u7ec6\u4fe1\u606f
 details.noaudio=\u672a\u9009\u62e9\u58f0\u97f3\u6587\u4ef6
 details.audio.file=\u58f0\u97f3\u6587\u4ef6
@@ -626,10 +637,15 @@ units.feet=\u82f1\u5c3a
 units.feet.short=\u82f1\u5c3a
 units.kilometres=\u5343\u7c73
 units.kilometres.short=\u5343\u7c73
-units.kmh=\u5343\u7c73/\u65f6
+units.kilometresperhour.short=\u5343\u7c73/\u65f6
 units.miles=\u82f1\u91cc
 units.miles.short=\u82f1\u91cc
-units.mph=\u82f1\u91cc/\u65f6
+units.milesperhour.short=\u82f1\u91cc/\u65f6
+units.nauticalmiles=\u6d77\u91cc
+units.nauticalmiles.short=\u6d77\u91cc
+units.nauticalmilesperhour.short=\u6d77\u91cc/\u65f6
+units.metrespersec.short=\u7c73/\u79d2
+units.feetpersec.short=\u82f1\u5c3a/\u79d2
 units.hours=\u5c0f\u65f6
 units.degminsec=\u5ea6-\u5206-\u79d2
 units.degmin=\u5ea6-\u5206
@@ -648,16 +664,17 @@ cardinal.w=W
 
 # Undo operations
 undo.load=\u5bfc\u5165\u6570\u636e
-undo.loadphotos=\u5bfc\u5165\u76f8\u7247
+undo.loadphotos=\u5bfc\u5165\u7167\u7247
 undo.loadaudios=\u8f7d\u5165\u58f0\u97f3\u6587\u4ef6
 undo.editpoint=\u7f16\u8f91\u8f68\u8ff9\u70b9
 undo.deletepoint=\u5220\u9664\u8f68\u8ff9\u70b9
-undo.removephoto=\u5220\u9664\u76f8\u7247
+undo.removephoto=\u5220\u9664\u7167\u7247
 undo.removeaudio=\u5220\u9664\u58f0\u97f3\u6587\u4ef6
 undo.deleterange=\u5220\u9664\u6bb5
-undo.compress=\u538b\u7f29\u8f68\u8ff9
+undo.croptrack=\u4fee\u526a\u8f68\u8ff9
+undo.deletemarked=\u538b\u7f29\u8f68\u8ff9
 undo.insert=\u63d2\u5165\u822a\u70b9
-undo.reverse=\u53cd\u5411\u6bb5
+undo.reverse=\u53cd\u8f6c\u6bb5
 undo.mergetracksegments=\u5408\u5e76\u6bb5
 undo.addtimeoffset=\u6dfb\u52a0\u65f6\u95f4\u504f\u79fb
 undo.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb
@@ -665,33 +682,33 @@ undo.rearrangewaypoints=\u91cd\u65b0\u914d\u7f6e\u822a\u70b9
 undo.cutandmove=\u79fb\u52a8\u6bb5
 undo.connect=\u94fe\u63a5
 undo.disconnect=\u65ad\u5f00
-undo.correlatephotos=\u94fe\u63a5\u76f8\u7247
-undo.rearrangephotos=\u91cd\u62cd\u76f8\u7247
+undo.correlatephotos=\u94fe\u63a5\u7167\u7247
+undo.rearrangephotos=\u91cd\u6392\u7167\u7247
 undo.createpoint=\u521b\u5efa\u8f68\u8ff9\u70b9
-undo.rotatephoto=\u65cb\u8f6c\u76f8\u7247
-undo.convertnamestotimes=\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4
+undo.rotatephoto=\u65cb\u8f6c\u7167\u7247
+undo.convertnamestotimes=\u5c06\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4
 undo.lookupsrtm=\u4eceSRTM\u67e5\u627e\u9ad8\u5ea6
 undo.deletefieldvalues=\u5220\u9664\u533a\u57df\u6570\u636e
-undo.correlateaudios=\u5173\u8054\u58f0\u97f3
+undo.correlateaudios=\u5173\u8054\u97f3\u9891
 
 # Error messages
 error.save.dialogtitle=\u4fdd\u5b58\u6570\u636e\u9519\u8bef
 error.save.nodata=\u65e0\u6570\u636e\u4fdd\u5b58
 error.save.failed=\u5411\u6587\u4ef6\u4fdd\u5b58\u6570\u636e\u5931\u8d25
-error.saveexif.filenotfound=\u627e\u4e0d\u5230\u76f8\u7247\u6587\u4ef6
-error.saveexif.cannotoverwrite1=\u76f8\u7247\u6587\u4ef6
-error.saveexif.cannotoverwrite2=\u53ea\u8bfb\u6587\u4ef6\u3002\u4fdd\u5b58\u526f\u672c\uff1f
-error.saveexif.failed1=\u56fe\u7247
-error.saveexif.failed2=\u4fdd\u5b58\u5931\u8d25
+error.saveexif.filenotfound=\u627e\u4e0d\u5230\u7167\u7247\u6587\u4ef6
+error.saveexif.cannotoverwrite1=\u7167\u7247
+error.saveexif.cannotoverwrite2=\u662f\u53ea\u8bfb\u6587\u4ef6\u3002\u4fdd\u5b58\u526f\u672c\uff1f
+error.saveexif.failed1=\u65e0\u6cd5\u4fdd\u5b58
+error.saveexif.failed2=\u5f20\u7167\u7247
 error.saveexif.forced1=
-error.saveexif.forced2=\u4e2a\u56fe\u7247\u9700\u8981\u5f3a\u5236\u6267\u884c
+error.saveexif.forced2=\u5f20\u7167\u7247\u9700\u8981\u5f3a\u5236\u6267\u884c
 error.load.dialogtitle=\u5bfc\u5165\u6570\u636e\u9519\u8bef
 error.load.noread=\u65e0\u6cd5\u8bfb\u6587\u4ef6
 error.load.nopoints=\u6587\u4ef6\u4e2d\u65e0\u5750\u6807\u4fe1\u606f
 error.load.unknownxml=XML\u683c\u5f0f\u9519\u8bef
 error.load.noxmlinzip=Zip\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230XML
 error.load.othererror=\u8bfb\u6587\u4ef6\u9519\u8bef
-error.jpegload.dialogtitle=\u5bfc\u5165\u76f8\u7247\u9519\u8bef
+error.jpegload.dialogtitle=\u5bfc\u5165\u7167\u7247\u9519\u8bef
 error.jpegload.nofilesfound=\u627e\u4e0d\u5230\u6587\u4ef6
 error.jpegload.nojpegsfound=\u627e\u4e0d\u5230Jpeg\u6587\u4ef6
 error.jpegload.nogpsfound=\u627e\u4e0d\u5230GPS\u4fe1\u606f
@@ -714,7 +731,9 @@ error.lookupsrtm.nonefound=\u65e0\u9ad8\u5ea6\u4fe1\u606f
 error.lookupsrtm.nonerequired=\u6240\u6709\u70b9\u5747\u542b\u9ad8\u5ea6\u4fe1\u606f
 error.gpsies.uploadnotok=gpsies\u670d\u52a1\u5668\u8fd4\u56de\u4fe1\u606f\uff1a
 error.gpsies.uploadfailed=\u4e0a\u4f20\u5931\u8d25
+error.showphoto.failed=\u52a0\u8f7d\u7167\u7247\u5931\u8d25
 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
index c55fc557ec6ee7bc5b58df0e19879b35935362ba..ab4f366d820967c67674e916c15985cddc5debb2 100644 (file)
@@ -88,7 +88,11 @@ public abstract class MediaHelper
                }
 
                // If we haven't got a result by now, try to load plain file
-               File file = (inSourceFile == null ? new File(inPath) : new File(inSourceFile.getParent(), inPath));
+               File file = new File(inPath);
+               if (inSourceFile != null && !file.isAbsolute()) {
+                       file = new File(inSourceFile.getParent(), inPath);
+               }
+               // awkward construction because new File(startPath, absolutePath) doesn't work
                return createMediaObject(file);
        }
 
index 76ed15785dc1fff93ec18661790e8c9955539711..2dd63dec16088fa4350b5b914c2c25a8d59ccdbc 100644 (file)
@@ -92,7 +92,8 @@ public class NmeaFileLoader
                if (messages.size() > 0)
                {
                        _app.informDataLoaded(getFieldArray(), makeDataArray(messages),
-                               Altitude.Format.METRES, new SourceInfo(inFile, SourceInfo.FILE_TYPE.NMEA));
+                               Altitude.Format.METRES, new SourceInfo(inFile, SourceInfo.FILE_TYPE.NMEA),
+                               null);
                }
        }
 
index 512b7e55fac5d9a72f58b8bd1aa1e0c870a8363c..1f88012807485d8010f387947bb00d9621375a03 100644 (file)
@@ -538,7 +538,7 @@ public class TextFileLoader
                // give data to App
                SourceInfo sourceInfo = new SourceInfo(_file, SourceInfo.FILE_TYPE.TEXT);
                _app.informDataLoaded(_fieldTableModel.getFieldArray(),
-                       _fileExtractTableModel.getData(), altitudeFormat, sourceInfo);
+                       _fileExtractTableModel.getData(), altitudeFormat, sourceInfo, null);
                // clear up file cacher
                _fileCacher.clear();
                // dispose of dialog
index a66883fa2fbd202203cfd10e0230968d94a6cf0d..a951cf75bf705009d720f4ffe642df8e1f28f3fb 100644 (file)
@@ -1,5 +1,5 @@
-GpsPrune version 13.4
-=====================
+GpsPrune version 14
+===================
 
 GpsPrune is an application for viewing, editing and managing coordinate data from GPS systems,
 including format conversion, charting and photo correlation.
@@ -17,7 +17,7 @@ Running
 =======
 
 To run GpsPrune from the jar file, simply call it from a command prompt or shell:
-   java -jar gpsprune_13.4.jar
+   java -jar gpsprune_14.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,41 +25,18 @@ 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_13.4.jar --lang=DE
-
+   java -jar gpsprune_14.jar --lang=DE
 
-New with version 13.4
-=====================
-The following features were added since version 13.3:
-  - Fix for empty settings
-  - Fix for file suffixes of cached tiles
-  - Removed Osma source as tiles@home has been discontinued
 
-New with version 13.3
-=====================
-The following features were added since version 13.2:
-  - Completion of Italian translations
-  - Fix for exporting track names to version 1.1 gpx files
-  - Changed OpenCycleMap url
-  - Added diagnostics if unrecognised command-line parameter wasn't a valid file
-  - Improve error handling when tile downloading fails
-  - Fix for editing a cloudmade source
-
-New with version 13.2
-=====================
-The following features were added since version 13.1:
-  - Russian translation
-  - Fix for loading kml with placemarks with multiple coordinate lists
-  - Fix for exporting descriptions to version 1.1 gpx files
-
-New with version 13.1
+New with version 14
 =====================
 The following features were added since version 13:
-  - Allow loading of photos and audio using relative paths from the gpx/kml file
-  - Cosmetic fixes to make the map controls more visible
-  - Allow osm-style map sources to use gifs or jpgs as well as pngs
-  - Allow edit of custom map sources
-  - Addition of a few more built-in map sources, such as hikebikemap and openseamap
+  - Dragging of existing points
+  - Creation of new points by dragging the halfway point between two points
+  - Nautical miles option including knots
+  - Full range details dialog
+  - Extension of interpolation function
+  - Selecting points within a rectangle to delete
 
 New with version 13
 ===================
@@ -76,6 +53,8 @@ The following features were added since version 12:
   - Importing of files through GPSBabel
   - List of recently used files in the menu
   - Display of bearing at which a photo was taken (display only)
+  - Allow edit of custom map sources
+  - Russian translation
 
 New with version 12
 ===================
index debe0e799bb1134416b950ca29700c7c111d7143..6be44eb80bc3eb6beb828f0270db15c75255954e 100644 (file)
@@ -596,7 +596,7 @@ public class FileSaver
                        {
                                if (inTimestampFormat == Timestamp.FORMAT_ORIGINAL) {
                                        // output original string
-                                       inBuffer.append(inPoint.getFieldValue(Field.TIMESTAMP));
+                                       inBuffer.append(inPoint.getTimestamp().getText(Timestamp.FORMAT_ORIGINAL));
                                }
                                else {
                                        // format value accordingly
index 76ec88f4de89db662f6761ae21e737c378fd2d1d..5c74a7a3894d22dd7c06d9e3e3bdb4b3fca7f1c3 100644 (file)
@@ -273,7 +273,7 @@ public class GpsSaver extends GenericFunction implements Runnable
                // Generate the GPX file and send to the GPS
                OutputStreamWriter writer = new OutputStreamWriter(process.getOutputStream());
                boolean[] saveFlags = {true, true, true, true, false, true}; // export everything
-               GpxExporter.exportData(writer, _app.getTrackInfo(), trackName, null, saveFlags, false);
+               GpxExporter.exportData(writer, _app.getTrackInfo(), trackName, null, saveFlags, null);
                writer.close();
 
                // Read the error stream to see if there's a better error message there
index d35c35a8696be7e9863fca94fcb6c4c91dcf49ad..19007ac920dc33c45c2522def2adb648ea1368c7 100644 (file)
@@ -278,6 +278,11 @@ public class GpxExporter extends GenericFunction implements Runnable
         */
        public void run()
        {
+               // Instantiate source file cachers in case we want to copy output
+               GpxCacherList gpxCachers = null;
+               if (_copySourceCheckbox.isSelected()) {
+                       gpxCachers = new GpxCacherList(_trackInfo.getFileInfo());
+               }
                OutputStreamWriter writer = null;
                try
                {
@@ -291,7 +296,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                                _pointTypeSelector.getJustSelection(), _timestampsCheckbox.isSelected()};
                        // write file
                        final int numPoints = exportData(writer, _trackInfo, _nameField.getText(),
-                               _descriptionField.getText(), saveFlags, _copySourceCheckbox.isSelected());
+                               _descriptionField.getText(), saveFlags, gpxCachers);
 
                        // close file
                        writer.close();
@@ -331,19 +336,16 @@ public class GpxExporter extends GenericFunction implements Runnable
         * @param inName name of track (optional)
         * @param inDesc description of track (optional)
         * @param inSaveFlags array of booleans to export tracks, waypoints, photos, audios, selection, timestamps
-        * @param inUseCopy true to copy source if available
+        * @param inGpxCachers list of Gpx cachers containing input data
         * @return number of points written
         * @throws IOException if io errors occur on write
         */
        public static int exportData(OutputStreamWriter inWriter, TrackInfo inInfo, String inName,
-               String inDesc, boolean[] inSaveFlags, boolean inUseCopy) throws IOException
+               String inDesc, boolean[] inSaveFlags, GpxCacherList inGpxCachers) throws IOException
        {
-               // Instantiate source file cachers in case we want to copy output
-               GpxCacherList gpxCachers = null;
-               if (inUseCopy) gpxCachers = new GpxCacherList(inInfo.getFileInfo());
                // Write or copy headers
                inWriter.write(getXmlHeaderString(inWriter));
-               final String gpxHeader = getGpxHeaderString(gpxCachers);
+               final String gpxHeader = getGpxHeaderString(inGpxCachers);
                final boolean isVersion1_1 = (gpxHeader.toUpperCase().indexOf("GPX/1/1") > 0);
                inWriter.write(gpxHeader);
                // Name field
@@ -375,8 +377,13 @@ public class GpxExporter extends GenericFunction implements Runnable
                                // Make a wpt element for each waypoint
                                if (point.isWaypoint() && exportWaypoints)
                                {
-                                       String pointSource = (inUseCopy?getPointSource(gpxCachers, point):null);
-                                       if (pointSource != null) {
+                                       String pointSource = (inGpxCachers == null? null : getPointSource(inGpxCachers, point));
+                                       if (pointSource != null)
+                                       {
+                                               // If timestamp checkbox is off, strip time
+                                               if (!exportTimestamps) {
+                                                       pointSource = stripTime(pointSource);
+                                               }
                                                inWriter.write(pointSource);
                                                inWriter.write('\n');
                                        }
@@ -392,12 +399,12 @@ public class GpxExporter extends GenericFunction implements Runnable
                {
                        // Output all route points (if any)
                        numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
-                               exportAudios, exportTimestamps, true, gpxCachers, "<rtept", "\t<rte><number>1</number>\n",
+                               exportAudios, exportTimestamps, true, inGpxCachers, "<rtept", "\t<rte><number>1</number>\n",
                                null, "\t</rte>\n");
                        // Output all track points, if any
                        String trackStart = "\t<trk><name>" + trackName + "</name><number>1</number><trkseg>\n";
                        numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
-                               exportAudios, exportTimestamps, false, gpxCachers, "<trkpt", trackStart,
+                               exportAudios, exportTimestamps, false, inGpxCachers, "<trkpt", trackStart,
                                "\t</trkseg>\n\t<trkseg>\n", "\t</trkseg></trk>\n");
                }
 
@@ -487,7 +494,12 @@ public class GpxExporter extends GenericFunction implements Runnable
                                                        inWriter.write(inSegmentTag);
                                                }
                                                if (numSaved == 0) {inWriter.write(inStartTag);}
-                                               if (pointSource != null) {
+                                               if (pointSource != null)
+                                               {
+                                                       // If timestamps checkbox is off, strip the time
+                                                       if (!exportTimestamps) {
+                                                               pointSource = stripTime(pointSource);
+                                                       }
                                                        inWriter.write(pointSource);
                                                        inWriter.write('\n');
                                                }
@@ -630,10 +642,6 @@ public class GpxExporter extends GenericFunction implements Runnable
                        encoding =  Charset.forName(encoding).name();
                }
                catch (Exception e) {} // ignore failure to find encoding
-               // Hack to fix bugs with Mac OSX (which reports MacRoman but is actually UTF-8)
-               if (encoding == null || encoding.toLowerCase().startsWith("macroman")) {
-                       encoding = "UTF-8";
-               }
                return encoding;
        }
 
@@ -849,4 +857,15 @@ public class GpxExporter extends GenericFunction implements Runnable
                // No link available, must have been loaded from zip file - no link possible
                return "";
        }
+
+
+       /**
+        * Strip the time from a GPX point source string
+        * @param inPointSource point source to copy
+        * @return point source with timestamp removed
+        */
+       private static String stripTime(String inPointSource)
+       {
+               return inPointSource.replaceAll("<time>.*?</time>", "");
+       }
 }
index e360878006443a82da87f60e7bf38594ae7bcac5..ee30841d6d779f7790b95006a58a75eda7759433 100644 (file)
@@ -312,7 +312,7 @@ public class PovExporter extends Export3dFunction
        {
                FileWriter writer = null;
                // find out the line separator for this system
-               String lineSeparator = System.getProperty("line.separator");
+               final String lineSeparator = System.getProperty("line.separator");
                try
                {
                        // create and scale model
@@ -436,7 +436,7 @@ public class PovExporter extends Export3dFunction
                  "  sphere {",
                  "   <0, 0, 0>, 0.3", // size should depend on model size
                  "   texture {",
-                 "      pigment {color rgb <0.2 1.0 0.2>}",
+                 "      pigment {color rgb <0.1 0.6 0.1>}", // dark green
                  "      finish { phong 1 }",
                  "   }",
                  " }",
@@ -444,7 +444,7 @@ public class PovExporter extends Export3dFunction
                  "  sphere {",
                  "   <0, 0, 0>, 0.3", // size should depend on model size
                  "   texture {",
-                 "      pigment {color rgb <0.6 1.0 0.2>}",
+                 "      pigment {color rgb <0.4 0.9 0.2>}", // green
                  "      finish { phong 1 }",
                  "   }",
                  " }",
@@ -452,7 +452,7 @@ public class PovExporter extends Export3dFunction
                  "  sphere {",
                  "   <0, 0, 0>, 0.3", // size should depend on model size
                  "   texture {",
-                 "      pigment {color rgb <1.0 1.0 0.1>}",
+                 "      pigment {color rgb <0.7 0.8 0.2>}", // yellow
                  "      finish { phong 1 }",
                  "   }",
                  " }",
@@ -460,7 +460,7 @@ public class PovExporter extends Export3dFunction
                  "  sphere {",
                  "   <0, 0, 0>, 0.3", // size should depend on model size
                  "   texture {",
-                 "      pigment {color rgb <1.0 1.0 1.0>}",
+                 "      pigment {color rgb <0.5 0.8 0.6>}", // greeny
                  "      finish { phong 1 }",
                  "   }",
                  " }",
@@ -468,7 +468,15 @@ public class PovExporter extends Export3dFunction
                  "  sphere {",
                  "   <0, 0, 0>, 0.3", // size should depend on model size
                  "   texture {",
-                 "      pigment {color rgb <0.1 1.0 1.0>}",
+                 "      pigment {color rgb <0.2 0.9 0.9>}", // cyan
+                 "      finish { phong 1 }",
+                 "   }",
+                 " }",
+                 "#declare track_sphere5 =",
+                 "  sphere {",
+                 "   <0, 0, 0>, 0.3", // size should depend on model size
+                 "   texture {",
+                 "      pigment {color rgb <1.0 1.0 1.0>}", // white
                  "      finish { phong 1 }",
                  "   }",
                  " }",
@@ -533,7 +541,7 @@ public class PovExporter extends Export3dFunction
         * @param inLineSeparator line separator to use
         * @throws IOException on file writing error
         */
-       private void writeLatLongLines(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator)
+       private static void writeLatLongLines(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator)
        throws IOException
        {
                inWriter.write("// Latitude and longitude lines:");
@@ -563,7 +571,7 @@ public class PovExporter extends Export3dFunction
         * @param inLineSeparator line separator to use
         * @throws IOException on file writing error
         */
-       private void writeDataPointsBallsAndSticks(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator)
+       private static void writeDataPointsBallsAndSticks(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator)
        throws IOException
        {
                inWriter.write("// Data points:");
@@ -605,7 +613,7 @@ public class PovExporter extends Export3dFunction
         * @param inLineSeparator line separator to use
         * @throws IOException on file writing error
         */
-       private void writeDataPointsTubesAndWalls(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator)
+       private static void writeDataPointsTubesAndWalls(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator)
        throws IOException
        {
                inWriter.write("// Data points:");
@@ -757,7 +765,7 @@ public class PovExporter extends Export3dFunction
         */
        private static byte checkHeightCode(byte inCode)
        {
-               final byte maxHeightCode = 4;
+               final byte maxHeightCode = 5;
                if (inCode < 0) return 0;
                if (inCode > maxHeightCode) return maxHeightCode;
                return inCode;
index 4dffd90dcb03233031b603d2c255f57521a17e25..1671607d5e9c1a417b4b75e878aef5aa9465eec4 100644 (file)
@@ -127,9 +127,10 @@ public class Java3DWindow implements ThreeDWindow
                Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")};
                if (_track.getNumPoints() > MAX_TRACK_SIZE && !TRACK_SIZE_WARNING_GIVEN)
                {
-                       if (JOptionPane.showOptionDialog(_frame,
+                       // FIXME: Change text reference from exportpov to java3d
+                       if (JOptionPane.showOptionDialog(_parentFrame,
                                        I18nManager.getText("dialog.exportpov.warningtracksize"),
-                                       I18nManager.getText("function.exportpov"), JOptionPane.OK_CANCEL_OPTION,
+                                       I18nManager.getText("function.show3d"), JOptionPane.OK_CANCEL_OPTION,
                                        JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
                                == JOptionPane.OK_OPTION)
                        {
index 7245633aa0667b936a0d5307fc8b0b84f2b38136..2c9ac97784e79f24e2c378766283720e354c8a15 100644 (file)
@@ -14,14 +14,13 @@ public class ThreeDModel
 {
        private Track _track = null;
        private PointScaler _scaler = null;
-       private double _modelSize;
        private double _scaleFactor = 1.0;
        private double _altFactor = 1.0;
        // MAYBE: How to store rods (lifts) in data?
        private byte[] _pointTypes = null;
        private byte[] _pointHeights = null;
 
-       private static final double DEFAULT_MODEL_SIZE = 10.0;
+       private static final double MODEL_SIZE = 10.0;
 
        // Constants for point types
        public static final byte POINT_TYPE_WAYPOINT      = 1;
@@ -34,21 +33,8 @@ public class ThreeDModel
         * @param inTrack Track object
         */
        public ThreeDModel(Track inTrack)
-       {
-               this(inTrack, DEFAULT_MODEL_SIZE);
-       }
-
-
-       /**
-        * Constructor
-        * @param inTrack Track object
-        * @param inSize model size
-        */
-       public ThreeDModel(Track inTrack, double inSize)
        {
                _track = inTrack;
-               _modelSize = inSize;
-               if (_modelSize <= 0.0) _modelSize = DEFAULT_MODEL_SIZE;
        }
 
 
@@ -86,19 +72,19 @@ public class ThreeDModel
                        if (_scaler.getMaximumHoriz() > _scaler.getMaximumVert())
                        {
                                // scale limited by longitude
-                               _scaleFactor = _modelSize / _scaler.getMaximumHoriz();
+                               _scaleFactor = MODEL_SIZE / _scaler.getMaximumHoriz();
                        }
                        else
                        {
                                // scale limited by latitude
-                               _scaleFactor = _modelSize / _scaler.getMaximumVert();
+                               _scaleFactor = MODEL_SIZE / _scaler.getMaximumVert();
                        }
                }
                // cap altitude scale factor if it's too big
                double maxScaledAlt = _scaler.getMaxScaledAlt() * _altFactor;
-               if (maxScaledAlt > _modelSize) {
+               if (maxScaledAlt > MODEL_SIZE) {
                        // capped
-                       _altFactor = _altFactor * _modelSize / maxScaledAlt;
+                       _altFactor = _altFactor * MODEL_SIZE / maxScaledAlt;
                }
                // calculate lat/long lines
                _scaler.calculateLatLongLines();
@@ -219,6 +205,6 @@ public class ThreeDModel
         */
        public double getModelSize()
        {
-               return _modelSize;
+               return MODEL_SIZE;
        }
 }
index 10f71c23773be747c372c298151bf0abb90b4e82..9a9f7a428ce95be69b66a3741efe120b2afe6c81 100644 (file)
@@ -54,7 +54,8 @@ public class UndoAddAltitudeOffset implements UndoOperation
        {
                // Perform the inverse operation
                final int numPoints = _altitudes.length;
-               for (int i=0; i<numPoints; i++) {
+               for (int i=0; i<numPoints; i++)
+               {
                        DataPoint point = inTrackInfo.getTrack().getPoint(i+_startIndex);
                        point.getAltitude().reset(_altitudes[i]);
                        point.setModified(true);
similarity index 81%
rename from tim/prune/undo/UndoCompress.java
rename to tim/prune/undo/UndoDeleteMarked.java
index 50582215aaf82aeb265c52b3cddac6ca5cb7b7b7..63ef7493b6836e99042b8cc0fb28f65662ef905e 100644 (file)
@@ -6,9 +6,9 @@ import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;\r
 \r
 /**\r
- * Operation to undo a track compression\r
+ * Operation to undo the deletion of marked points\r
  */\r
-public class UndoCompress implements UndoOperation\r
+public class UndoDeleteMarked implements UndoOperation\r
 {\r
        private DataPoint[] _contents = null;\r
        protected int _numPointsDeleted = -1;\r
@@ -19,7 +19,7 @@ public class UndoCompress implements UndoOperation
         * Constructor\r
         * @param inTrack track contents to copy\r
         */\r
-       public UndoCompress(Track inTrack)\r
+       public UndoDeleteMarked(Track inTrack)\r
        {\r
                _contents = inTrack.cloneContents();\r
                // Copy boolean segment start flags\r
@@ -46,7 +46,7 @@ public class UndoCompress implements UndoOperation
         */\r
        public String getDescription()\r
        {\r
-               String desc = I18nManager.getText("undo.compress");\r
+               String desc = I18nManager.getText("undo.deletemarked");\r
                if (_numPointsDeleted > 0)\r
                        desc = desc + " (" + _numPointsDeleted + ")";\r
                return desc;\r
@@ -64,7 +64,7 @@ public class UndoCompress implements UndoOperation
                // Copy boolean segment start flags\r
                Track track = inTrackInfo.getTrack();\r
                if (_segmentStarts.length != track.getNumPoints())\r
-                       throw new UndoException("Cannot undo compress - track length no longer matches");\r
+                       throw new UndoException("Cannot undo delete - track length no longer matches");\r
                for (int i=0; i<_segmentStarts.length; i++) {\r
                        track.getPoint(i).setSegmentStart(_segmentStarts[i]);\r
                }\r
index adb2ea397d0baffcb3d5f410c850c8d3cc3b3cd1..9919d0d3a7dd9cac197bc2059a15a38c996bb88e 100644 (file)
@@ -12,6 +12,7 @@ public class UndoDeletePoint implements UndoOperation
        private int _pointIndex = -1;\r
        private DataPoint _point = null;\r
        private int _photoIndex = -1;\r
+       private int _audioIndex = -1;\r
        private boolean _segmentStart = false;\r
 \r
 \r
@@ -20,13 +21,16 @@ public class UndoDeletePoint implements UndoOperation
         * @param inPointIndex index number of point within track\r
         * @param inPoint data point\r
         * @param inPhotoIndex index number of photo within photo list\r
+        * @param inAudioIndex index number of audio within audio list\r
         * @param inSegmentStart true if following track point starts new segment\r
         */\r
-       public UndoDeletePoint(int inPointIndex, DataPoint inPoint, int inPhotoIndex, boolean inSegmentStart)\r
+       public UndoDeletePoint(int inPointIndex, DataPoint inPoint, int inPhotoIndex, int inAudioIndex,\r
+               boolean inSegmentStart)\r
        {\r
                _pointIndex = inPointIndex;\r
                _point = inPoint;\r
                _photoIndex = inPhotoIndex;\r
+               _audioIndex = inAudioIndex;\r
                _segmentStart = inSegmentStart;\r
        }\r
 \r
@@ -69,6 +73,13 @@ public class UndoDeletePoint implements UndoOperation
                                _point.getPhoto().setDataPoint(_point);\r
                        }\r
                }\r
+               // Re-add audio as well if necessary\r
+               if (_point.getAudio() != null && _audioIndex > -1)\r
+               {\r
+                       // add audio object to list\r
+                       inTrackInfo.getAudioList().addAudio(_point.getAudio(), _audioIndex);\r
+                       _point.getAudio().setDataPoint(_point);\r
+               }\r
                // Restore previous status of following track point if necessary\r
                if (!_segmentStart)\r
                {\r
index e2a76995ebb478ec868c9e4a8c68b8a9d4fcfed8..ba2b31ab5ef89540b8496be4e8908f0a9855ae7e 100644 (file)
@@ -1,6 +1,7 @@
 package tim.prune.undo;\r
 \r
 import tim.prune.I18nManager;\r
+import tim.prune.data.AudioList;\r
 import tim.prune.data.DataPoint;\r
 import tim.prune.data.PhotoList;\r
 import tim.prune.data.TrackInfo;\r
@@ -10,37 +11,116 @@ import tim.prune.data.TrackInfo;
  */\r
 public class UndoDeleteRange implements UndoOperation\r
 {\r
-       private int _startIndex = -1;\r
-       private DataPoint[] _points = null;\r
+       /**\r
+        * Inner class to hold a single range information set\r
+        */\r
+       class RangeInfo\r
+       {\r
+               public int _startIndex = -1;\r
+               public DataPoint[] _points = null;\r
+               public DataPoint _nextTrackPoint = null;\r
+               public boolean _segmentStart = false;\r
+\r
+               /**\r
+                * @param inPoint next track point after deleted section, or null\r
+                */\r
+               public void setNextTrackPoint(DataPoint inPoint)\r
+               {\r
+                       _nextTrackPoint = inPoint;\r
+                       if (inPoint != null) {\r
+                               _segmentStart = inPoint.getSegmentStart();\r
+                       }\r
+\r
+               }\r
+\r
+               /**\r
+                * @return true if the range is valid\r
+                */\r
+               public boolean isValid()\r
+               {\r
+                       return _startIndex >= 0 && _points != null && _points.length > 0;\r
+               }\r
+       }\r
+\r
+\r
+       // Instance variables for UndoDeleteRange\r
+       private RangeInfo _rangeInfo1 = null;\r
+       private RangeInfo _rangeInfo2 = null;\r
        private PhotoList _photoList = null;\r
-       private DataPoint _nextTrackPoint = null;\r
-       private boolean _segmentStart = false;\r
+       private AudioList _audioList = null;\r
+       private String _nameKey = null;\r
+       private int _totalDeleted = 0;\r
 \r
 \r
        /**\r
         * Constructor\r
         * @param inTrackInfo track info object\r
+        * @param inNameKey key to use for undo text\r
+        * @param inStartIndex1 start index of first deleted segment\r
+        * @param inDeleteMedias1 flags to delete media for range1\r
+        * @param inStartIndex2 start index of second segment\r
+        * @param inDeleteMedias2 flags to delete media for range2\r
         */\r
-       public UndoDeleteRange(TrackInfo inTrackInfo)\r
+       public UndoDeleteRange(TrackInfo inTrackInfo, String inNameKey,\r
+               int inStartIndex1, boolean[] inDeleteMedias1,\r
+               int inStartIndex2, boolean[] inDeleteMedias2)\r
        {\r
-               _startIndex = inTrackInfo.getSelection().getStart();\r
-               _points = inTrackInfo.cloneSelectedRange();\r
-               _photoList = inTrackInfo.getPhotoList().cloneList();\r
-               // Save segment flag of following track point\r
-               _nextTrackPoint = inTrackInfo.getTrack().getNextTrackPoint(_startIndex + _points.length);\r
-               if (_nextTrackPoint != null) {\r
-                       _segmentStart = _nextTrackPoint.getSegmentStart();\r
+               _nameKey = inNameKey;\r
+               boolean mediaDeleted = false;\r
+               _totalDeleted = 0;\r
+               // Check if there's a valid first range\r
+               if (inStartIndex1 >= 0 && inDeleteMedias1 != null)\r
+               {\r
+                       final int numPoints = inDeleteMedias1.length;\r
+                       if (numPoints > 0)\r
+                       {\r
+                               _totalDeleted += numPoints;\r
+                               _rangeInfo1 = new RangeInfo();\r
+                               _rangeInfo1._startIndex = inStartIndex1;\r
+\r
+                               for (int i=0; i<numPoints && !mediaDeleted; i++) {\r
+                                       if (inDeleteMedias1[i]) mediaDeleted = true;\r
+                               }\r
+                               // Clone points\r
+                               _rangeInfo1._points = inTrackInfo.getTrack().cloneRange(inStartIndex1, inStartIndex1 + numPoints - 1);\r
+                               // Save segment flag of following track point\r
+                               _rangeInfo1.setNextTrackPoint(inTrackInfo.getTrack().getNextTrackPoint(inStartIndex1 + numPoints));\r
+                       }\r
+               }\r
+               // And the same for the second range, if any\r
+               if (inStartIndex2 >= 0 && inDeleteMedias2 != null)\r
+               {\r
+                       final int numPoints = inDeleteMedias2.length;\r
+                       if (numPoints > 0)\r
+                       {\r
+                               _totalDeleted += numPoints;\r
+                               _rangeInfo2 = new RangeInfo();\r
+                               _rangeInfo2._startIndex = inStartIndex2;\r
+                               for (int i=0; i<numPoints && !mediaDeleted; i++) {\r
+                                       if (inDeleteMedias2[i]) mediaDeleted = true;\r
+                               }\r
+\r
+                               // Clone points\r
+                               _rangeInfo2._points = inTrackInfo.getTrack().cloneRange(inStartIndex2, inStartIndex2 + numPoints - 1);\r
+                               // Save segment flag of following track point\r
+                               _rangeInfo2.setNextTrackPoint(inTrackInfo.getTrack().getNextTrackPoint(inStartIndex2 + numPoints));\r
+                       }\r
+               }\r
+               // If any media have been deleted, then the lists must be copied\r
+               if (mediaDeleted)\r
+               {\r
+                       _photoList = inTrackInfo.getPhotoList().cloneList();\r
+                       _audioList = inTrackInfo.getAudioList().cloneList();\r
                }\r
        }\r
 \r
 \r
        /**\r
-        * @return description of operation including range length\r
+        * @return description of operation including number of points deleted\r
         */\r
        public String getDescription()\r
        {\r
-               return I18nManager.getText("undo.deleterange")\r
-                       + " (" + _points.length + ")";\r
+               return I18nManager.getText(_nameKey) + " (" + _totalDeleted + ")";\r
        }\r
 \r
 \r
@@ -50,22 +130,48 @@ public class UndoDeleteRange implements UndoOperation
         */\r
        public void performUndo(TrackInfo inTrackInfo)\r
        {\r
-               // restore photos to how they were before\r
-               inTrackInfo.getPhotoList().restore(_photoList);\r
-               // reconnect photos to points\r
-               for (int i=0; i<_points.length; i++)\r
+               // restore photos and audios to how they were before\r
+               if (_photoList != null) {\r
+                       inTrackInfo.getPhotoList().restore(_photoList);\r
+               }\r
+               if (_audioList != null) {\r
+                       inTrackInfo.getAudioList().restore(_audioList);\r
+               }\r
+\r
+               // Undo both the ranges\r
+               performUndo(inTrackInfo, _rangeInfo1);\r
+               performUndo(inTrackInfo, _rangeInfo2);\r
+       }\r
+\r
+       /**\r
+        * Perform the undo on a single deleted range\r
+        * @param inTrackInfo track info object\r
+        * @param inRangeInfo info object describing deleted range\r
+        */\r
+       private void performUndo(TrackInfo inTrackInfo, RangeInfo inRangeInfo)\r
+       {\r
+               if (inRangeInfo == null || !inRangeInfo.isValid()) return;\r
+\r
+               // reconnect photos and audios to points\r
+               final int numPoints = inRangeInfo._points.length;\r
+               for (int i=0; i<numPoints; i++)\r
                {\r
-                       DataPoint point = _points[i];\r
-                       if (point != null && point.getPhoto() != null)\r
+                       DataPoint point = inRangeInfo._points[i];\r
+                       if (point != null && point.hasMedia())\r
                        {\r
-                               point.getPhoto().setDataPoint(point);\r
+                               if (point.getPhoto() != null) {\r
+                                       point.getPhoto().setDataPoint(point);\r
+                               }\r
+                               if (point.getAudio() != null) {\r
+                                       point.getAudio().setDataPoint(point);\r
+                               }\r
                        }\r
                }\r
                // restore point array into track\r
-               inTrackInfo.getTrack().insertRange(_points, _startIndex);\r
+               inTrackInfo.getTrack().insertRange(inRangeInfo._points, inRangeInfo._startIndex);\r
                // Restore segment flag of following track point\r
-               if (_nextTrackPoint != null) {\r
-                       _nextTrackPoint.setSegmentStart(_segmentStart);\r
+               if (inRangeInfo._nextTrackPoint != null) {\r
+                       inRangeInfo._nextTrackPoint.setSegmentStart(inRangeInfo._segmentStart);\r
                }\r
        }\r
-}
\ No newline at end of file
+}\r
index 228795b6d5d5d34e0b15d9bf7e955fc5f3544345..be41ea0d88f92c7ce15ef966a253a607b2fe853f 100644 (file)
@@ -2,6 +2,7 @@ package tim.prune.undo;
 \r
 import tim.prune.I18nManager;\r
 import tim.prune.data.DataPoint;\r
+import tim.prune.data.Field;\r
 import tim.prune.data.TrackInfo;\r
 import tim.prune.function.edit.FieldEditList;\r
 \r
@@ -32,7 +33,9 @@ public class UndoEditPoint implements UndoOperation
        public String getDescription()\r
        {\r
                String desc = I18nManager.getText("undo.editpoint");\r
-               String newName = _undoFieldList.getEdit(0).getValue();\r
+               String newName = null;\r
+               if (_undoFieldList.getEdit(0).getField() == Field.WAYPT_NAME)\r
+                       newName = _undoFieldList.getEdit(0).getValue();\r
                String pointName = _originalPoint.getWaypointName();\r
                if (newName != null && !newName.equals(""))\r
                        desc = desc + " " + newName;\r
index 39676af411d0eaa374aae01070bd2139cd01505a..f23892126824a01849c83be9fb4adf8ab40fa117 100644 (file)
@@ -5,7 +5,7 @@ import tim.prune.data.DataPoint;
 import tim.prune.data.TrackInfo;\r
 \r
 /**\r
- * Operation to undo an insertion (eg interpolate, average)\r
+ * Operation to undo an insertion (eg average)\r
  */\r
 public class UndoInsert implements UndoOperation\r
 {\r
diff --git a/tim/prune/undo/UndoInterpolate.java b/tim/prune/undo/UndoInterpolate.java
new file mode 100644 (file)
index 0000000..975a731
--- /dev/null
@@ -0,0 +1,60 @@
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo an interpolation\r
+ */\r
+public class UndoInterpolate implements UndoOperation\r
+{\r
+       private int _startIndex = 0;\r
+       private int _totalInserted = 0;\r
+       private DataPoint[] _points = null;\r
+\r
+\r
+       /**\r
+        * Constructor\r
+        * @param inTrackInfo track info object\r
+        * @param inTotalInserted total number of points inserted\r
+        */\r
+       public UndoInterpolate(TrackInfo inTrackInfo, int inTotalInserted)\r
+       {\r
+               _startIndex = inTrackInfo.getSelection().getStart();\r
+               _points = inTrackInfo.cloneSelectedRange();\r
+               _totalInserted = inTotalInserted;\r
+       }\r
+\r
+\r
+       /**\r
+        * @return description of operation including parameters\r
+        */\r
+       public String getDescription()\r
+       {\r
+               return I18nManager.getText("undo.insert") + " (" + _totalInserted + ")";\r
+       }\r
+\r
+\r
+       /**\r
+        * Perform the undo operation on the given TrackInfo\r
+        * @param inTrackInfo TrackInfo object on which to perform the operation\r
+        */\r
+       public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+       {\r
+               // Work out how many points were in the track before the interpolation\r
+               final int newSize = inTrackInfo.getTrack().getNumPoints() - _totalInserted;\r
+               DataPoint[] oldPoints = inTrackInfo.getTrack().cloneContents();\r
+               DataPoint[] newPoints = new DataPoint[newSize];\r
+\r
+               // Restore track to previous values\r
+               System.arraycopy(oldPoints, 0, newPoints, 0, _startIndex);\r
+               System.arraycopy(_points, 0, newPoints, _startIndex, _points.length);\r
+               int endIndex = _startIndex + _points.length;\r
+               System.arraycopy(oldPoints, endIndex + _totalInserted, newPoints, endIndex, newSize - endIndex);\r
+\r
+               inTrackInfo.getTrack().replaceContents(newPoints);\r
+               // reset selection\r
+               inTrackInfo.getSelection().clearAll();\r
+       }\r
+}\r
index bb6a21176c48673f3a293115a97e6969fd98255e..b8c2b35970fbb05482ad8cfc4dd1bbe95aa0f08a 100644 (file)
@@ -1,6 +1,8 @@
 package tim.prune.undo;\r
 \r
 import tim.prune.I18nManager;\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.Photo;\r
 import tim.prune.data.TrackInfo;\r
 \r
 /**\r
@@ -50,6 +52,18 @@ public class UndoLoadPhotos implements UndoOperation
                        cropIndex = inTrackInfo.getTrack().getNumPoints() - _numPoints;\r
                        inTrackInfo.getTrack().cropTo(cropIndex);\r
                }\r
+               else\r
+               {\r
+                       // Loop through the points (if any) and detach them\r
+                       for (int i=0; i<_numPhotos; i++)\r
+                       {\r
+                               Photo photo = inTrackInfo.getPhotoList().getPhoto(inTrackInfo.getPhotoList().getNumPhotos() - 1 - i);\r
+                               if (photo.isConnected()) {\r
+                                       DataPoint point = photo.getDataPoint();\r
+                                       if (point != null) {point.setPhoto(null);}\r
+                               }\r
+                       }\r
+               }\r
                // crop photo list to previous size\r
                cropIndex = inTrackInfo.getPhotoList().getNumPhotos() - _numPhotos;\r
                inTrackInfo.getPhotoList().cropTo(cropIndex);\r
index d82714ae1e6444ce18fda96d1d2cddbc1a0fcff9..d028b82a568a98da8c7b46ea8d7539f767c891c1 100644 (file)
@@ -29,9 +29,11 @@ public class UndoLookupSrtm implements UndoOperation
                // Make arrays of points and altitudes
                _points = new DataPoint[numPoints];
                _altitudes = new String[numPoints];
-               for (int i=0; i<numPoints; i++) {
+               for (int i=0; i<numPoints; i++)
+               {
                        DataPoint point = track.getPoint(i);
-                       if (!point.hasAltitude() || point.getAltitude().getValue() == 0) {
+                       if (!point.hasAltitude() || point.getAltitude().getValue() == 0)
+                       {
                                _points[i] = point;
                                if (point.hasAltitude()) {
                                        _altitudes[i] = point.getFieldValue(Field.ALTITUDE);