]> gitweb.fperrin.net Git - GpsPrune.git/commitdiff
Version 11, August 2010
authoractivityworkshop <mail@activityworkshop.net>
Sat, 14 Feb 2015 14:46:01 +0000 (15:46 +0100)
committeractivityworkshop <mail@activityworkshop.net>
Sat, 14 Feb 2015 14:46:01 +0000 (15:46 +0100)
85 files changed:
tim/prune/App.java
tim/prune/FunctionLibrary.java
tim/prune/GpsPruner.java
tim/prune/UpdateMessageBroker.java
tim/prune/correlate/PhotoCorrelator.java
tim/prune/correlate/TimeIndexPair.java
tim/prune/data/Altitude.java
tim/prune/data/Coordinate.java
tim/prune/data/DataPoint.java
tim/prune/data/Photo.java
tim/prune/data/PointScaler.java
tim/prune/data/SourceInfo.java
tim/prune/data/Timestamp.java
tim/prune/data/Track.java
tim/prune/function/AboutScreen.java
tim/prune/function/DeleteFieldValues.java [new file with mode: 0644]
tim/prune/function/Export3dFunction.java [new file with mode: 0644]
tim/prune/function/FieldListModel.java [new file with mode: 0644]
tim/prune/function/SelectTracksFunction.java [new file with mode: 0644]
tim/prune/function/SetLanguage.java
tim/prune/function/ShowKeysScreen.java
tim/prune/function/browser/BrowserLauncher.java
tim/prune/function/compress/DuplicatePointAlgorithm.java
tim/prune/function/edit/PointEditor.java
tim/prune/function/gpsies/FormPoster.java
tim/prune/function/gpsies/UploadGpsiesFunction.java [new file with mode: 0644]
tim/prune/function/srtm/LookupSrtmFunction.java
tim/prune/gui/GuiGridLayout.java
tim/prune/gui/MenuManager.java
tim/prune/gui/SelectorDisplay.java
tim/prune/gui/SidebarController.java [new file with mode: 0644]
tim/prune/gui/map/MapCanvas.java
tim/prune/gui/map/ScaleBar.java
tim/prune/gui/profile/AltitudeData.java
tim/prune/gui/profile/ProfileChart.java
tim/prune/gui/profile/ProfileData.java
tim/prune/jpeg/ExifGateway.java
tim/prune/jpeg/ExifLibrarySwitch.java [new file with mode: 0644]
tim/prune/jpeg/ExternalExifLibrary.java
tim/prune/jpeg/JpegData.java
tim/prune/jpeg/drew/ExifReader.java
tim/prune/lang/prune-texts_cz.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 [new file with mode: 0644]
tim/prune/lang/prune-texts_fr.properties
tim/prune/lang/prune-texts_it.properties
tim/prune/lang/prune-texts_ja.properties
tim/prune/lang/prune-texts_nl.properties [new file with mode: 0644]
tim/prune/lang/prune-texts_pl.properties
tim/prune/lang/prune-texts_pt.properties
tim/prune/lang/prune-texts_tr.properties
tim/prune/lang/prune-texts_zh.properties
tim/prune/load/FieldGuesser.java
tim/prune/load/GpsLoader.java
tim/prune/load/JpegLoader.java
tim/prune/load/TextFileLoader.java
tim/prune/load/TrackNameList.java [new file with mode: 0644]
tim/prune/load/xml/GpxHandler.java
tim/prune/load/xml/GzipFileLoader.java
tim/prune/load/xml/XmlFileLoader.java
tim/prune/load/xml/XmlHandler.java
tim/prune/load/xml/ZipFileLoader.java
tim/prune/readme.txt
tim/prune/save/ExifSaver.java
tim/prune/save/FileSaver.java
tim/prune/save/GpxCacher.java [deleted file]
tim/prune/save/GpxExporter.java
tim/prune/save/PovExporter.java
tim/prune/save/SvgExporter.java [new file with mode: 0644]
tim/prune/save/SvgFragment.java [new file with mode: 0644]
tim/prune/save/xml/GpxCacher.java [new file with mode: 0644]
tim/prune/save/xml/GpxCacherList.java [moved from tim/prune/save/GpxCacherList.java with 92% similarity]
tim/prune/save/xml/GpxSlicer.java [new file with mode: 0644]
tim/prune/save/xml/TagReceiver.java [new file with mode: 0644]
tim/prune/threedee/Java3DWindow.java
tim/prune/threedee/LineDialog.java
tim/prune/threedee/ThreeDModel.java
tim/prune/undo/UndoAddAltitudeOffset.java
tim/prune/undo/UndoAddTimeOffset.java
tim/prune/undo/UndoCorrelatePhotos.java
tim/prune/undo/UndoDeleteFieldValues.java [new file with mode: 0644]
tim/prune/undo/UndoLookupSrtm.java

index 07f68b1222e5dbc4b2214e94dfacc91405c8f896..88cafea3cd1901e23949ccba07579f3bdeabe8d4 100644 (file)
@@ -20,35 +20,21 @@ import tim.prune.data.PhotoList;
 import tim.prune.data.SourceInfo;
 import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;
+import tim.prune.function.SelectTracksFunction;
 import tim.prune.function.browser.BrowserLauncher;
 import tim.prune.function.browser.UrlGenerator;
 import tim.prune.function.edit.FieldEditList;
 import tim.prune.function.edit.PointEditor;
+import tim.prune.gui.SidebarController;
 import tim.prune.gui.MenuManager;
 import tim.prune.gui.UndoManager;
 import tim.prune.gui.Viewport;
 import tim.prune.load.FileLoader;
 import tim.prune.load.JpegLoader;
+import tim.prune.load.TrackNameList;
 import tim.prune.save.ExifSaver;
 import tim.prune.save.FileSaver;
-import tim.prune.undo.UndoAddAltitudeOffset;
-import tim.prune.undo.UndoAddTimeOffset;
-import tim.prune.undo.UndoCompress;
-import tim.prune.undo.UndoConnectPhoto;
-import tim.prune.undo.UndoCreatePoint;
-import tim.prune.undo.UndoCutAndMove;
-import tim.prune.undo.UndoDeletePhoto;
-import tim.prune.undo.UndoDeletePoint;
-import tim.prune.undo.UndoDeleteRange;
-import tim.prune.undo.UndoDisconnectPhoto;
-import tim.prune.undo.UndoEditPoint;
-import tim.prune.undo.UndoException;
-import tim.prune.undo.UndoInsert;
-import tim.prune.undo.UndoLoad;
-import tim.prune.undo.UndoLoadPhotos;
-import tim.prune.undo.UndoMergeTrackSegments;
-import tim.prune.undo.UndoOperation;
-import tim.prune.undo.UndoReverseSection;
+import tim.prune.undo.*;
 
 
 /**
@@ -62,6 +48,7 @@ public class App
        private TrackInfo _trackInfo = null;
        private int _lastSavePosition = 0;
        private MenuManager _menuManager = null;
+       private SidebarController __sidebarController = null;
        private FileLoader _fileLoader = null;
        private JpegLoader _jpegLoader = null;
        private FileSaver _fileSaver = null;
@@ -465,7 +452,7 @@ public class App
                int selStart = _trackInfo.getSelection().getStart();
                int selEnd = _trackInfo.getSelection().getEnd();
                UndoAddTimeOffset undo = new UndoAddTimeOffset(selStart, selEnd, inTimeOffset);
-               if (_trackInfo.getTrack().addTimeOffset(selStart, selEnd, inTimeOffset))
+               if (_trackInfo.getTrack().addTimeOffset(selStart, selEnd, inTimeOffset, false))
                {
                        _undoStack.add(undo);
                        UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_EDITED);
@@ -501,6 +488,7 @@ public class App
                if (success)
                {
                        _undoStack.add(undo);
+                       _trackInfo.getSelection().markInvalid();
                        UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_EDITED);
                        UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.addaltitudeoffset"));
                }
@@ -641,7 +629,7 @@ public class App
        }
 
        /**
-        * Receive loaded data and optionally merge with current Track
+        * Receive loaded data and start load
         * @param inFieldArray array of fields
         * @param inDataArray array of data
         * @param inAltFormat altitude format
@@ -649,6 +637,20 @@ public class App
         */
        public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray,
                Altitude.Format inAltFormat, SourceInfo inSourceInfo)
+       {
+               informDataLoaded(inFieldArray, inDataArray, inAltFormat, inSourceInfo, null);
+       }
+
+       /**
+        * Receive loaded data and determine whether to filter on tracks or not
+        * @param inFieldArray array of fields
+        * @param inDataArray array of data
+        * @param inAltFormat altitude format
+        * @param inSourceInfo information about the source of the data
+        * @param inTrackNameList information about the track names
+        */
+       public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray,
+               Altitude.Format inAltFormat, SourceInfo inSourceInfo, TrackNameList inTrackNameList)
        {
                // Check whether loaded array can be properly parsed into a Track
                Track loadedTrack = new Track();
@@ -665,6 +667,26 @@ public class App
                        JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.open.contentsdoubled"),
                                I18nManager.getText("function.open"), JOptionPane.WARNING_MESSAGE);
                }
+               // Look at TrackNameList, decide whether to filter or not
+               if (inTrackNameList != null && inTrackNameList.getNumTracks() > 1)
+               {
+                       // Launch a dialog to let the user choose which tracks to load, then continue
+                       new SelectTracksFunction(this, inFieldArray, inDataArray, inAltFormat, inSourceInfo,
+                               inTrackNameList).begin();
+               }
+               else {
+                       // go directly to load
+                       informDataLoaded(loadedTrack, inSourceInfo);
+               }
+       }
+
+       /**
+        * Receive loaded data and optionally merge with current Track
+        * @param inLoadedTrack loaded track
+        * @param inSourceInfo information about the source of the data
+        */
+       public void informDataLoaded(Track inLoadedTrack, SourceInfo inSourceInfo)
+       {
                // Decide whether to load or append
                if (_track.getNumPoints() > 0)
                {
@@ -683,28 +705,26 @@ public class App
                        if (answer == JOptionPane.YES_OPTION)
                        {
                                // append data to current Track
-                               _undoStack.add(new UndoLoad(_track.getNumPoints(), loadedTrack.getNumPoints()));
-                               _track.combine(loadedTrack);
+                               _undoStack.add(new UndoLoad(_track.getNumPoints(), inLoadedTrack.getNumPoints()));
+                               _track.combine(inLoadedTrack);
                                // set source information
-                               inSourceInfo.populatePointObjects(_track, loadedTrack.getNumPoints());
+                               inSourceInfo.populatePointObjects(_track, inLoadedTrack.getNumPoints());
                                _trackInfo.getFileInfo().addSource(inSourceInfo);
                        }
                        else if (answer == JOptionPane.NO_OPTION)
                        {
                                // Don't append, replace data
                                PhotoList photos = null;
-                               if (_trackInfo.getPhotoList().hasCorrelatedPhotos())
-                               {
+                               if (_trackInfo.getPhotoList().hasCorrelatedPhotos()) {
                                        photos = _trackInfo.getPhotoList().cloneList();
                                }
-                               _undoStack.add(new UndoLoad(_trackInfo, inDataArray.length, photos));
+                               _undoStack.add(new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), photos));
                                _lastSavePosition = _undoStack.size();
                                _trackInfo.getSelection().clearAll();
-                               _track.load(loadedTrack);
+                               _track.load(inLoadedTrack);
                                inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
                                _trackInfo.getFileInfo().replaceSource(inSourceInfo);
-                               if (photos != null)
-                               {
+                               if (photos != null) {
                                        _trackInfo.getPhotoList().removeCorrelatedPhotos();
                                }
                        }
@@ -712,10 +732,10 @@ public class App
                else
                {
                        // Currently no data held, so transfer received data
-                       _undoStack.add(new UndoLoad(_trackInfo, inDataArray.length, null));
+                       _undoStack.add(new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), null));
                        _lastSavePosition = _undoStack.size();
                        _trackInfo.getSelection().clearAll();
-                       _track.load(loadedTrack);
+                       _track.load(inLoadedTrack);
                        inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
                        _trackInfo.getFileInfo().addSource(inSourceInfo);
                }
@@ -776,12 +796,10 @@ public class App
                                // Save numbers so load can be undone
                                _undoStack.add(new UndoLoadPhotos(numPhotosAdded, numPointsAdded));
                        }
-                       if (numPhotosAdded == 1)
-                       {
+                       if (numPhotosAdded == 1) {
                                UpdateMessageBroker.informSubscribers("" + numPhotosAdded + " " + I18nManager.getText("confirm.jpegload.single"));
                        }
-                       else
-                       {
+                       else {
                                UpdateMessageBroker.informSubscribers("" + numPhotosAdded + " " + I18nManager.getText("confirm.jpegload.multi"));
                        }
                        // MAYBE: Improve message when photo(s) fail to load (eg already added)
@@ -1032,4 +1050,21 @@ public class App
        {
                return _viewport;
        }
+
+       /**
+        * Set the controller for the full screen mode
+        * @param inController controller object
+        */
+       public void setSidebarController(SidebarController inController)
+       {
+               __sidebarController = inController;
+       }
+
+       /**
+        * Toggle sidebars on and off
+        */
+       public void toggleSidebars()
+       {
+               __sidebarController.toggle();
+       }
 }
index 1f7bcfd2d8562091816c4984cfd01ae3b712584f..92947f01a4885ddda15d0ef9d7934057c615359b 100644 (file)
@@ -7,12 +7,14 @@ import tim.prune.function.compress.CompressTrackFunction;
 import tim.prune.function.distance.DistanceFunction;
 import tim.prune.function.edit.PointNameEditor;
 import tim.prune.function.gpsies.GetGpsiesFunction;
+import tim.prune.function.gpsies.UploadGpsiesFunction;
 import tim.prune.function.srtm.LookupSrtmFunction;
 import tim.prune.load.GpsLoader;
 import tim.prune.save.GpsSaver;
 import tim.prune.save.GpxExporter;
 import tim.prune.save.KmlExporter;
 import tim.prune.save.PovExporter;
+import tim.prune.save.SvgExporter;
 
 /**
  * Class to provide access to functions
@@ -22,6 +24,7 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_GPXEXPORT = null;
        public static GenericFunction FUNCTION_KMLEXPORT = null;
        public static PovExporter FUNCTION_POVEXPORT     = null;
+       public static SvgExporter FUNCTION_SVGEXPORT     = null;
        public static GenericFunction FUNCTION_GPSLOAD  = null;
        public static GenericFunction FUNCTION_GPSSAVE  = null;
        public static GenericFunction FUNCTION_SAVECONFIG  = null;
@@ -33,6 +36,7 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_ADD_TIME_OFFSET  = null;
        public static GenericFunction FUNCTION_ADD_ALTITUDE_OFFSET  = null;
        public static GenericFunction FUNCTION_CONVERT_NAMES_TO_TIMES  = null;
+       public static GenericFunction FUNCTION_DELETE_FIELD_VALUES  = null;
        public static GenericFunction FUNCTION_PASTE_COORDINATES = null;
        public static GenericFunction FUNCTION_FIND_WAYPOINT = null;
        public static GenericFunction FUNCTION_DUPLICATE_POINT = null;
@@ -45,6 +49,7 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_DISTANCES  = null;
        public static GenericFunction FUNCTION_FULL_RANGE_DETAILS = null;
        public static GenericFunction FUNCTION_GET_GPSIES = null;
+       public static GenericFunction FUNCTION_UPLOAD_GPSIES = null;
        public static GenericFunction FUNCTION_SET_MAP_BG = null;
        public static GenericFunction FUNCTION_SET_DISK_CACHE = null;
        public static GenericFunction FUNCTION_SET_PATHS  = null;
@@ -66,6 +71,7 @@ public abstract class FunctionLibrary
                FUNCTION_GPXEXPORT = new GpxExporter(inApp);
                FUNCTION_KMLEXPORT = new KmlExporter(inApp);
                FUNCTION_POVEXPORT = new PovExporter(inApp);
+               FUNCTION_SVGEXPORT = new SvgExporter(inApp);
                FUNCTION_GPSLOAD   = new GpsLoader(inApp);
                FUNCTION_GPSSAVE   = new GpsSaver(inApp);
                FUNCTION_SAVECONFIG = new SaveConfig(inApp);
@@ -77,6 +83,7 @@ public abstract class FunctionLibrary
                FUNCTION_ADD_TIME_OFFSET = new AddTimeOffset(inApp);
                FUNCTION_ADD_ALTITUDE_OFFSET = new AddAltitudeOffset(inApp);
                FUNCTION_CONVERT_NAMES_TO_TIMES = new ConvertNamesToTimes(inApp);
+               FUNCTION_DELETE_FIELD_VALUES = new DeleteFieldValues(inApp);
                FUNCTION_PASTE_COORDINATES = new PasteCoordinates(inApp);
                FUNCTION_FIND_WAYPOINT = new FindWaypoint(inApp);
                FUNCTION_DUPLICATE_POINT = new DuplicatePoint(inApp);
@@ -89,6 +96,7 @@ public abstract class FunctionLibrary
                FUNCTION_DISTANCES = new DistanceFunction(inApp);
                FUNCTION_FULL_RANGE_DETAILS = new FullRangeDetails(inApp);
                FUNCTION_GET_GPSIES = new GetGpsiesFunction(inApp);
+               FUNCTION_UPLOAD_GPSIES = new UploadGpsiesFunction(inApp);
                FUNCTION_SET_MAP_BG = new SetMapBgFunction(inApp);
                FUNCTION_SET_DISK_CACHE = new DiskCacheConfig(inApp);
                FUNCTION_SET_PATHS = new SetPathsFunction(inApp);
index ac61501a2cadc025315918261f138fcc91f367ef..9e290098da6c57193663ce62a448d4f30fc7624e 100644 (file)
@@ -2,6 +2,7 @@ package tim.prune;
 
 import java.awt.event.WindowAdapter;
 import java.awt.BorderLayout;
+import java.awt.Component;
 import java.awt.event.WindowEvent;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -15,6 +16,7 @@ import javax.swing.WindowConstants;
 import tim.prune.config.Config;
 import tim.prune.config.ConfigException;
 import tim.prune.gui.DetailsDisplay;
+import tim.prune.gui.SidebarController;
 import tim.prune.gui.IconManager;
 import tim.prune.gui.MenuManager;
 import tim.prune.gui.SelectorDisplay;
@@ -33,9 +35,9 @@ import tim.prune.gui.profile.ProfileChart;
 public class GpsPruner
 {
        /** Version number of application, used in about screen and for version check */
-       public static final String VERSION_NUMBER = "10";
+       public static final String VERSION_NUMBER = "11";
        /** Build number, just used for about screen */
-       public static final String BUILD_NUMBER = "189";
+       public static final String BUILD_NUMBER = "204";
        /** Static reference to App object */
        private static App APP = null;
 
@@ -206,15 +208,15 @@ public class GpsPruner
                UpdateMessageBroker.informSubscribers("Prune v" + VERSION_NUMBER);
 
                // Arrange in the frame using split panes
-               JSplitPane midPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mapDisp, profileDisp);
-               midPane.setResizeWeight(1.0); // allocate as much space as poss to map
-               JSplitPane triplePane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, midPane, rightPanel);
-               triplePane.setResizeWeight(1.0); // allocate as much space as poss to map
+               JSplitPane midSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mapDisp, profileDisp);
+               midSplit.setResizeWeight(1.0); // allocate as much space as poss to map
+               JSplitPane rightSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, midSplit, rightPanel);
+               rightSplit.setResizeWeight(1.0); // allocate as much space as poss to map
 
                frame.getContentPane().setLayout(new BorderLayout());
                frame.getContentPane().add(toolbar, BorderLayout.NORTH);
-               frame.getContentPane().add(new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel,
-                       triplePane), BorderLayout.CENTER);
+               JSplitPane leftSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightSplit);
+               frame.getContentPane().add(leftSplit, BorderLayout.CENTER);
                frame.getContentPane().add(statusBar, BorderLayout.SOUTH);
 
                // add closing listener
@@ -237,8 +239,12 @@ public class GpsPruner
                frame.setSize(650, 450);
                frame.setVisible(true);
                // Set position of map/profile splitter
-               midPane.setDividerLocation(0.75);
+               midSplit.setDividerLocation(0.75);
 
+               // Make a full screen toggler
+               SidebarController fsc = new SidebarController(new Component[] {leftPanel, profileDisp, rightPanel},
+                       new JSplitPane[] {leftSplit, midSplit, rightSplit});
+               APP.setSidebarController(fsc);
                // Finally, give the files to load to the App
                APP.loadDataFiles(inDataFiles);
        }
index 0b21f52d0a381de1c6a293717393792fadc7ea62..337fc27967712542addcde896b07568558058d03 100644 (file)
@@ -38,6 +38,7 @@ public abstract class UpdateMessageBroker
         */
        public static void informSubscribers(byte inChange)
        {
+               // TODO: Launch separate thread so that whatever caused the inform can finish
                for (int i=0; i<_subscribers.length; i++)
                {
                        if (_subscribers[i] != null)
index d60e905f09d048d84d13c63fc7d893c8efb3265a..84c179ec3d3a23576d9e6ee83cdf1133758e20af 100644 (file)
@@ -166,8 +166,11 @@ public class PhotoCorrelator extends GenericFunction
                JPanel card2Top = new JPanel();
                card2Top.setLayout(new BoxLayout(card2Top, BoxLayout.Y_AXIS));
                _tipLabel = new JLabel(I18nManager.getText("dialog.correlate.options.tip"));
+               _tipLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
                card2Top.add(_tipLabel);
-               card2Top.add(new JLabel(I18nManager.getText("dialog.correlate.options.intro")));
+               JLabel introLabel = new JLabel(I18nManager.getText("dialog.correlate.options.intro"));
+               introLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
+               card2Top.add(introLabel);
                // time offset section
                JPanel offsetPanel = new JPanel();
                offsetPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.offsetpanel")));
@@ -262,7 +265,7 @@ public class PhotoCorrelator extends GenericFunction
                card2Top.add(previewButton);
                card2.add(card2Top, BorderLayout.NORTH);
                // preview
-               _previewTable = new JTable();
+               _previewTable = new JTable(new PhotoPreviewTableModel());
                JScrollPane previewScrollPane = new JScrollPane(_previewTable);
                previewScrollPane.setPreferredSize(new Dimension(300, 100));
                card2.add(previewScrollPane, BorderLayout.CENTER);
@@ -336,7 +339,9 @@ public class PhotoCorrelator extends GenericFunction
                for (int i=0; i<numPhotos; i++)
                {
                        Photo photo = inTrackInfo.getPhotoList().getPhoto(i);
-                       if (photo.getDataPoint() != null && photo.getDataPoint().hasTimestamp())
+                       // For working out time differences, can't use photos which already had point information
+                       if (photo.getDataPoint() != null && photo.getDataPoint().hasTimestamp()
+                               && photo.getOriginalStatus() == Photo.Status.NOT_CONNECTED)
                        {
                                // Calculate time difference, add to table model
                                long timeDiff = photo.getTimestamp().getSecondsSince(photo.getDataPoint().getTimestamp());
@@ -428,6 +433,8 @@ public class PhotoCorrelator extends GenericFunction
                        PhotoPreviewTableRow row = new PhotoPreviewTableRow(pair);
                        // Don't try to correlate photos which don't have points either side
                        boolean correlatePhoto = pair.isValid();
+                       // Don't select photos which already have a point
+                       if (photo.getCurrentStatus() != Photo.Status.NOT_CONNECTED) {correlatePhoto = false;}
                        // Check time limits, distance limits
                        if (timeLimit != null && correlatePhoto) {
                                long numSecs = pair.getMinSeconds();
@@ -442,8 +449,7 @@ public class PhotoCorrelator extends GenericFunction
                                correlatePhoto = (angDistPhoto < angDistLimit);
                        }
                        // Don't select photos which are already correlated to the same point
-                       if (pair.getSecondsBefore() == 0L && pair.getPointBefore().getPhoto() != null
-                               && pair.getPointBefore().getPhoto().equals(photo)) {
+                       if (pair.getSecondsBefore() == 0L && pair.getPointBefore().isDuplicate(photo.getDataPoint())) {
                                correlatePhoto = false;
                        }
                        row.setCorrelateFlag(correlatePhoto);
@@ -548,11 +554,14 @@ public class PhotoCorrelator extends GenericFunction
                for (int i=0; i<numPoints; i++)
                {
                        DataPoint point = inTrack.getPoint(i);
-                       Timestamp pointStamp = point.getTimestamp();
-                       if (pointStamp != null && pointStamp.isValid())
+                       if (point.getPhoto() == null || point.getPhoto().getCurrentStatus() != Photo.Status.TAGGED)
                        {
-                               long numSeconds = pointStamp.getSecondsSince(photoStamp);
-                               pair.addPoint(point, numSeconds);
+                               Timestamp pointStamp = point.getTimestamp();
+                               if (pointStamp != null && pointStamp.isValid())
+                               {
+                                       long numSeconds = pointStamp.getSecondsSince(photoStamp);
+                                       pair.addPoint(point, numSeconds);
+                               }
                        }
                }
                return pair;
@@ -692,12 +701,10 @@ public class PhotoCorrelator extends GenericFunction
                                                pair.getPointBefore().setPhoto(pair.getPhoto());
                                                pair.getPhoto().setDataPoint(pair.getPointBefore());
                                        }
-                                       else if (pointPhoto.equals(pair.getPhoto()))
-                                       {
+                                       else if (pointPhoto.equals(pair.getPhoto())) {
                                                // photo is already connected, nothing to do
                                        }
-                                       else
-                                       {
+                                       else {
                                                // point is already connected to a different photo, so need to clone point
                                                numPointsToCreate++;
                                        }
index 775b151ac32e92005ae8ca005ba1b8e67ebf5aba..23401120755ca0a96fdecf30ce3cf7be1cbd5929 100644 (file)
@@ -39,7 +39,9 @@ public class TimeIndexPair implements Comparable<TimeIndexPair>
         */
        public int compareTo(TimeIndexPair inOther)
        {
-               return (int) (_time - inOther._time);
+               int compare = (int) (_time - inOther._time);
+               if (compare == 0) {compare = _index - inOther._index;}
+               return compare;
        }
 
        /**
index 3328817fa6cccdb0dfb88e36c228412c0239fa33..06a32911197fc3e0560e81c45ac042bb5a080ed3 100644 (file)
@@ -50,7 +50,7 @@ public class Altitude
 
 
        /**
-        * Constructor with int vaue
+        * Constructor with int value
         * @param inValue int value of altitude
         * @param inFormat format of altitude, either metres or feet
         */
index 44ab779ab6c1a0d66f1254c3df6fb82391a955fb..556bd892797cac3834f89d316dadbb6a726d0fab 100644 (file)
@@ -263,11 +263,7 @@ public abstract class Coordinate
         */
        public boolean equals(Coordinate inOther)
        {
-               return (inOther != null && _cardinal == inOther._cardinal
-                       && _degrees == inOther._degrees
-                       && _minutes == inOther._minutes
-                       && _seconds == inOther._seconds
-                       && _fracs == inOther._fracs);
+               return (_asDouble == inOther._asDouble);
        }
 
 
@@ -304,8 +300,12 @@ public abstract class Coordinate
                                }
                                case FORMAT_DEG_WHOLE_MIN:
                                {
-                                       answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "\u00B0"
-                                               + (int) Math.floor(_minutes + _seconds / 60.0 + _fracs / 60.0 / _fracDenom + 0.5) + "'";
+                                       int deg = _degrees;
+                                       int min = (int) Math.floor(_minutes + _seconds / 60.0 + _fracs / 60.0 / _fracDenom + 0.5);
+                                       if (min == 60) {
+                                               min = 0; deg++;
+                                       }
+                                       answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(deg) + "\u00B0" + min + "'";
                                        break;
                                }
                                case FORMAT_DEG:
index 7acd4f9e364314985d0ea2abb85996901352037b..bba7adc670f90078a8deb0a3280b6fa109ef8a39 100644 (file)
@@ -152,12 +152,7 @@ public class DataPoint
                _fieldValues[fieldIndex] = inValue;
                // Increment edit count on all field edits except segment
                if (inField != Field.NEW_SEGMENT) {
-                       if (!inUndo) {
-                               _modifyCount++;
-                       }
-                       else {
-                               _modifyCount--;
-                       }
+                       setModified(inUndo);
                }
                // Change Coordinate, Altitude, Name or Timestamp fields after edit
                if (_altitude != null && _altitude.getFormat() != Altitude.Format.NO_FORMAT) {
@@ -170,6 +165,20 @@ public class DataPoint
                }
        }
 
+       /**
+        * Either increment or decrement the modify count, depending on whether it's an undo or not
+        * @param inUndo true for undo, false otherwise
+        */
+       public void setModified(boolean inUndo)
+       {
+               if (!inUndo) {
+                       _modifyCount++;
+               }
+               else {
+                       _modifyCount--;
+               }
+       }
+
        /**
         * @return field list for this point
         */
index 9eacac2706a87d1c2ef451c879ddc717b6c565d6..7ffe35dec984511a6de8d7e522cb1aa25b4c7932 100644 (file)
@@ -65,12 +65,10 @@ public class Photo
        {
                _dataPoint = inPoint;
                // set status according to point
-               if (inPoint == null)
-               {
+               if (inPoint == null) {
                        setCurrentStatus(Status.NOT_CONNECTED);
                }
-               else
-               {
+               else {
                        setCurrentStatus(Status.CONNECTED);
                }
        }
index 4d2290f0b5e0f6b26f9dea13850efcf9d9db585d..b3b94f3d2b6ce8751c78830fc5dad4ebe85c1b64 100644 (file)
@@ -8,21 +8,20 @@ public class PointScaler
        // Original data
        private Track _track = null;
        // Range information
-       private AltitudeRange _altRange = null;
-       private DoubleRange _latRange = null;
-       private DoubleRange _lonRange = null;
        private double _latMedian = 0.0;
        private double _lonMedian = 0.0;
        private int _minAltitude = 0;
-       private int _maxAltitude = 0;
        // Scaling information
        private double _longFactor = 0.0;
+       private double _altFactor = 0.0;
        // Scaled points
        private double[] _xValues = null;
        private double[] _yValues = null;
-       private int[] _altValues = null;
+       private double[] _altValues = null;
+       // max values
        private double _maxX = 0.0;
        private double _maxY = 0.0;
+       private double _maxScaledAlt = 0.0;
        // lat/long lines
        private double[] _latLinesDegs = null;
        private double[] _lonLinesDegs = null;
@@ -45,9 +44,6 @@ public class PointScaler
        public PointScaler(Track inTrack)
        {
                _track = inTrack;
-               _altRange = new AltitudeRange();
-               _latRange = new DoubleRange();
-               _lonRange = new DoubleRange();
        }
 
 
@@ -57,9 +53,9 @@ public class PointScaler
        public void scale()
        {
                // Clear data
-               _altRange.clear();
-               _latRange.clear();
-               _lonRange.clear();
+               DoubleRange latRange = new DoubleRange();
+               DoubleRange lonRange = new DoubleRange();
+               DoubleRange altRange = new DoubleRange();
                int numPoints = 0;
                int p = 0;
                DataPoint point = null;
@@ -71,24 +67,32 @@ public class PointScaler
                                point = _track.getPoint(p);
                                if (point != null)
                                {
-                                       _latRange.addValue(point.getLatitude().getDouble());
-                                       _lonRange.addValue(point.getLongitude().getDouble());
-                                       _altRange.addValue(point.getAltitude());
+                                       latRange.addValue(point.getLatitude().getDouble());
+                                       lonRange.addValue(point.getLongitude().getDouble());
+                                       altRange.addValue(point.getAltitude().getValue(Altitude.Format.METRES));
                                }
                        }
 
                        // Find median latitude and calculate factor
-                       _latMedian = (_latRange.getMinimum() + _latRange.getMaximum()) / 2;
-                       _lonMedian = (_lonRange.getMinimum() + _lonRange.getMaximum()) / 2;
-                       _minAltitude = _altRange.getMinimum();
+                       _latMedian = (latRange.getMinimum() + latRange.getMaximum()) / 2;
+                       _lonMedian = (lonRange.getMinimum() + lonRange.getMaximum()) / 2;
+                       _minAltitude = (int) altRange.getMinimum();
                        _longFactor = Math.cos(_latMedian / 180.0 * Math.PI); // quite rough
+                       // Find altitude scale factor using distance
+                       DataPoint p1 = new DataPoint(new Latitude(latRange.getMinimum(), Coordinate.FORMAT_DEG),
+                               new Longitude(_lonMedian, Coordinate.FORMAT_DEG), null);
+                       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);
+                       _altFactor = 1.0 / horizDist;
 
                        // create new arrays for scaled values
                        if (_xValues == null || _xValues.length != numPoints)
                        {
                                _xValues = new double[numPoints];
                                _yValues = new double[numPoints];
-                               _altValues = new int[numPoints];
+                               _altValues = new double[numPoints];
                        }
                        // Calculate scaled values
                        for (p=0; p<numPoints; p++)
@@ -99,12 +103,12 @@ public class PointScaler
                                        _xValues[p] = getScaledLongitude(point.getLongitude().getDouble());
                                        _yValues[p] = getScaledLatitude(point.getLatitude().getDouble());
                                        _altValues[p] = getScaledAltitude(point.getAltitude());
+                                       if (_altValues[p] > _maxScaledAlt) {_maxScaledAlt = _altValues[p];}
                                }
                        }
                        // Calculate x and y range
-                       _maxX = getScaledLongitude(_lonRange.getMaximum());
-                       _maxY = getScaledLatitude(_latRange.getMaximum());
-                       _maxAltitude = _altRange.getMaximum() - _altRange.getMinimum();
+                       _maxX = getScaledLongitude(lonRange.getMaximum());
+                       _maxY = getScaledLatitude(latRange.getMaximum());
                }
        }
 
@@ -117,10 +121,9 @@ public class PointScaler
         * @return maximum vert value
         */
        public double getMaximumVert() { return _maxY; }
-       /**
-        * @return maximum alt value
-        */
-       public int getMaximumAlt() { return _maxAltitude; }
+
+       /** @return maximum scaled altitude value */
+       public double getMaxScaledAlt() { return _maxScaledAlt; }
 
        /**
         * Get the horizontal value for the specified point
@@ -145,7 +148,7 @@ public class PointScaler
         * @param inIndex index of point, starting at 0
         * @return scaled altitude value
         */
-       public int getAltValue(int inIndex)
+       public double getAltValue(int inIndex)
        {
                return _altValues[inIndex];
        }
@@ -155,7 +158,7 @@ public class PointScaler
         * @param inLatitude latitude in degrees
         * @return scaled latitude
         */
-       public double getScaledLatitude(double inLatitude)
+       private double getScaledLatitude(double inLatitude)
        {
                return inLatitude - _latMedian;
        }
@@ -164,7 +167,7 @@ public class PointScaler
         * @param inLongitude longitude in degrees
         * @return scaled longitude
         */
-       public double getScaledLongitude(double inLongitude)
+       private double getScaledLongitude(double inLongitude)
        {
                return (inLongitude - _lonMedian) * _longFactor;
        }
@@ -173,10 +176,10 @@ public class PointScaler
         * @param inAltitude Altitude object
         * @return scaled altitude
         */
-       public int getScaledAltitude(Altitude inAltitude)
+       private double getScaledAltitude(Altitude inAltitude)
        {
                if (inAltitude == null) return -1;
-               return inAltitude.getValue(_altRange.getFormat()) - _minAltitude;
+               return (inAltitude.getValue(Altitude.Format.METRES) - _minAltitude) * _altFactor;
        }
 
        /**
@@ -184,7 +187,7 @@ public class PointScaler
         * @param inScaledLatitude scaled latitude
         * @return latitude in degrees
         */
-       public double getUnscaledLatitude(double inScaledLatitude)
+       private double getUnscaledLatitude(double inScaledLatitude)
        {
                return inScaledLatitude + _latMedian;
        }
@@ -193,7 +196,7 @@ public class PointScaler
         * @param inScaledLongitude scaled longitude
         * @return longitude in degrees
         */
-       public double getUnscaledLongitude(double inScaledLongitude)
+       private double getUnscaledLongitude(double inScaledLongitude)
        {
                return inScaledLongitude / _longFactor + _lonMedian;
        }
index bbe8baa3d9cfb269eb9efcbd268b4a48976a2d8f..341bf0299410a3f767adcf9fbe9f3064d9053aef 100644 (file)
@@ -3,9 +3,8 @@ package tim.prune.data;
 import java.io.File;
 
 /**
- * Class to hold the source of the point data,
- * including original file and file type, and
- * also file offsets for source copying
+ * Class to hold the source of the point data, including the original file
+ * and file type, and references to each of the point objects
  */
 public class SourceInfo
 {
@@ -21,6 +20,10 @@ public class SourceInfo
 
        /** Array of datapoints */
        private DataPoint[] _points = null;
+       /** Number of points */
+       private int _numPoints = 0;
+       /** Array of point indices (if necessary) */
+       private int[] _pointIndices = null;
 
 
        /**
@@ -76,7 +79,22 @@ public class SourceInfo
         */
        public int getNumPoints()
        {
-               return _points.length;
+               return _numPoints;
+       }
+
+       /**
+        * Set the indices of the points selected out of a loaded track
+        * @param inSelectedFlags array of booleans showing whether each point in the original data was loaded or not
+        */
+       public void setPointIndices(boolean[] inSelectedFlags)
+       {
+               _numPoints = inSelectedFlags.length;
+               _pointIndices = new int[_numPoints];
+               int p=0;
+               for (int i=0; i<_numPoints; i++) {
+                       if (inSelectedFlags[i]) {_pointIndices[p++] = i;}
+               }
+               // Now the point indices array holds the index of each of the selected points
        }
 
        /**
@@ -86,6 +104,7 @@ public class SourceInfo
         */
        public void populatePointObjects(Track inTrack, int inNumPoints)
        {
+               if (_numPoints == 0) {_numPoints = inNumPoints;}
                if (inNumPoints > 0)
                {
                        _points = new DataPoint[inNumPoints];
@@ -106,6 +125,7 @@ public class SourceInfo
                for (int i=0; i<_points.length && (idx < 0); i++) {
                        if (_points[i] == inPoint) {idx = i;}
                }
-               return idx;
+               if (_pointIndices == null) {return idx;} // All points loaded
+               return _pointIndices[idx]; // use point index mapping
        }
 }
index f1d056980de680d5343edf25776ebe56402700d1..8de7c4d7d76e62c5b1041d497a6139613be14f24 100644 (file)
@@ -5,6 +5,8 @@ import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Class to hold the timestamp of a track point
@@ -23,6 +25,9 @@ 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 GENERAL_TIMESTAMP_PATTERN
+               = Pattern.compile("(\\d{4})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})");
+       private static Matcher GENERAL_TIMESTAMP_MATCHER = null;
        private static long SECS_SINCE_1970 = 0L;
        private static long SECS_SINCE_GARTRIP = 0L;
        private static long MSECS_SINCE_1970 = 0L;
@@ -72,35 +77,7 @@ public class Timestamp
                        try
                        {
                                long rawValue = Long.parseLong(inString.trim());
-                               // check for each format possibility and pick nearest
-                               long diff1 = Math.abs(SECS_SINCE_1970 - rawValue);
-                               long diff2 = Math.abs(MSECS_SINCE_1970 - rawValue);
-                               long diff3 = Math.abs(MSECS_SINCE_1990 - rawValue);
-                               long diff4 = Math.abs(SECS_SINCE_GARTRIP - rawValue);
-
-                               // Start off with "seconds since 1970" format
-                               long smallestDiff = diff1;
-                               _seconds = rawValue;
-                               // Now check millis since 1970
-                               if (diff2 < smallestDiff)
-                               {
-                                       // milliseconds since 1970
-                                       _seconds = rawValue / 1000L;
-                                       smallestDiff = diff2;
-                               }
-                               // Now millis since 1990
-                               if (diff3 < smallestDiff)
-                               {
-                                       // milliseconds since 1990
-                                       _seconds = rawValue / 1000L + TWENTY_YEARS_IN_SECS;
-                                       smallestDiff = diff3;
-                               }
-                               // Lastly, check gartrip offset
-                               if (diff4 < smallestDiff)
-                               {
-                                       // seconds since gartrip offset
-                                       _seconds = rawValue + GARTRIP_OFFSET;
-                               }
+                               _seconds = getSeconds(rawValue);
                                _valid = true;
                        }
                        catch (NumberFormatException nfe)
@@ -119,6 +96,21 @@ public class Timestamp
                                        }
                                        catch (ParseException e) {}
                                }
+                               if (!_valid && inString.length() == 19) {
+                                       GENERAL_TIMESTAMP_MATCHER = GENERAL_TIMESTAMP_PATTERN.matcher(inString);
+                                       if (GENERAL_TIMESTAMP_MATCHER.matches()) {
+                                               try {
+                                                       _seconds = getSeconds(Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(1)),
+                                                               Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(2)),
+                                                               Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(3)),
+                                                               Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(4)),
+                                                               Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(5)),
+                                                               Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(6)));
+                                                       _valid = true;
+                                               }
+                                               catch (NumberFormatException nfe2) {} // parse shouldn't fail if matcher matched
+                                       }
+                               }
                        }
                }
        }
@@ -135,15 +127,7 @@ public class Timestamp
         */
        public Timestamp(int inYear, int inMonth, int inDay, int inHour, int inMinute, int inSecond)
        {
-               Calendar cal = Calendar.getInstance();
-               cal.set(Calendar.YEAR, inYear);
-               cal.set(Calendar.MONTH, inMonth - 1);
-               cal.set(Calendar.DAY_OF_MONTH, inDay);
-               cal.set(Calendar.HOUR_OF_DAY, inHour);
-               cal.set(Calendar.MINUTE, inMinute);
-               cal.set(Calendar.SECOND, inSecond);
-               cal.set(Calendar.MILLISECOND, 0);
-               _seconds = cal.getTimeInMillis() / 1000;
+               _seconds = getSeconds(inYear, inMonth, inDay, inHour, inMinute, inSecond);
                _valid = true;
        }
 
@@ -159,6 +143,68 @@ public class Timestamp
        }
 
 
+       /**
+        * Convert the given timestamp parameters into a number of seconds
+        * @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
+        */
+       private static long getSeconds(int inYear, int inMonth, int inDay, int inHour, int inMinute, int inSecond)
+       {
+               Calendar cal = Calendar.getInstance();
+               cal.set(Calendar.YEAR, inYear);
+               cal.set(Calendar.MONTH, inMonth - 1);
+               cal.set(Calendar.DAY_OF_MONTH, inDay);
+               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;
+       }
+
+       /**
+        * Convert the given long parameters into a number of seconds
+        * @param inRawValue long value representing seconds / milliseconds
+        * @return number of seconds
+        */
+       private static long getSeconds(long inRawValue)
+       {
+               // check for each format possibility and pick nearest
+               long diff1 = Math.abs(SECS_SINCE_1970 - inRawValue);
+               long diff2 = Math.abs(MSECS_SINCE_1970 - inRawValue);
+               long diff3 = Math.abs(MSECS_SINCE_1990 - inRawValue);
+               long diff4 = Math.abs(SECS_SINCE_GARTRIP - inRawValue);
+
+               // Start off with "seconds since 1970" format
+               long smallestDiff = diff1;
+               long seconds = inRawValue;
+               // Now check millis since 1970
+               if (diff2 < smallestDiff)
+               {
+                       // milliseconds since 1970
+                       seconds = inRawValue / 1000L;
+                       smallestDiff = diff2;
+               }
+               // Now millis since 1990
+               if (diff3 < smallestDiff)
+               {
+                       // milliseconds since 1990
+                       seconds = inRawValue / 1000L + TWENTY_YEARS_IN_SECS;
+                       smallestDiff = diff3;
+               }
+               // Lastly, check gartrip offset
+               if (diff4 < smallestDiff)
+               {
+                       // seconds since gartrip offset
+                       seconds = inRawValue + GARTRIP_OFFSET;
+               }
+               return seconds;
+       }
+
        /**
         * @return true if timestamp is valid
         */
@@ -232,15 +278,12 @@ public class Timestamp
         */
        public String getText(int inFormat)
        {
+               if (!_valid) {return "";}
                if (inFormat == FORMAT_ISO_8601) {
                        return format(ISO_8601_FORMAT);
                }
-               if (_text == null)
-               {
-                       if (_valid) {
-                               _text = format(DEFAULT_DATE_FORMAT);
-                       }
-                       else _text = "";
+               if (_text == null) {
+                       _text = (_valid?format(DEFAULT_DATE_FORMAT):"");
                }
                return _text;
        }
index 83ac9547f3ede67149ee5a1d0cce85758d45adae..9b88ade102519207d2bb730a44edc9b477386157 100644 (file)
@@ -292,9 +292,10 @@ public class Track
         * @param inStart start of range
         * @param inEnd end of range
         * @param inOffset offset to add (-ve to subtract)
+        * @param inUndo true for undo operation
         * @return true on success
         */
-       public boolean addTimeOffset(int inStart, int inEnd, long inOffset)
+       public boolean addTimeOffset(int inStart, int inEnd, long inOffset, boolean inUndo)
        {
                // sanity check
                if (inStart < 0 || inEnd < 0 || inStart >= inEnd || inEnd >= _numPoints) {
@@ -310,6 +311,7 @@ public class Track
                                // This point has a timestamp so add the offset to it
                                foundTimestamp = true;
                                timestamp.addOffset(inOffset);
+                               _dataPoints[i].setModified(inUndo);
                        }
                }
                return foundTimestamp;
@@ -341,6 +343,7 @@ public class Track
                                // This point has an altitude so add the offset to it
                                foundAlt = true;
                                alt.addOffset(inOffset, inFormat, inDecimals);
+                               _dataPoints[i].setModified(false);
                        }
                }
                // needs to be scaled again
index e0334b583ead9ff891aabb13a2f4edac69a4c091..c8985071243861bd5fe8ec3893bc4720aca02bd5 100644 (file)
@@ -45,6 +45,7 @@ public class AboutScreen extends GenericFunction
        private JDialog _dialog = null;
        private JTabbedPane _tabs = null;
        private JButton _okButton = null;
+       private JTextArea _aboutTextArea = null;
        /** Labels for whether tools installed or not */
        private JLabel[] _installedLabels = null;
 
@@ -96,9 +97,9 @@ public class AboutScreen extends GenericFunction
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.summarytext2")).append("</p>");
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.summarytext3")).append("</p>");
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.languages")).append(" : ")
-                       .append("deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano, polski, \u4e2d\u6587; (chinese)<br>" +
-                               "schwiizerd\u00FC\u00FCtsch, \u65E5\u672C\u8A9E (japanese), t\u00FCrk\u00E7e, portugu\u00EAs, " +
-                               "bahasa indonesia, rom\u00E2n\u0103").append("</p>");
+                       .append("deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano, nederlands,<br>" +
+                               " polski, portugu\u00EAs, \u4e2d\u6587 (chinese), \u65E5\u672C\u8A9E (japanese), schwiizerd\u00FC\u00FCtsch, t\u00FCrk\u00E7e, " +
+                               "<br> \u010de\u0161tina, rom\u00E2n\u0103, afrikaans, bahasa indonesia, farsi").append("</p>");
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.translatedby")).append("</p>");
                JEditorPane descPane = new JEditorPane("text/html", descBuffer.toString());
                descPane.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
@@ -194,7 +195,7 @@ public class AboutScreen extends GenericFunction
                        new JLabel(" theYinYeti, Rothermographer, Sam, Rudolph, nazotoko,"),
                        1, 4);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
-                       new JLabel(" katpatuka, R\u00E9mi"),
+                       new JLabel(" katpatuka, R\u00E9mi, Marcus, Ali, Javier, Jeroen, prot_d"),
                        1, 5);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
                        new JLabel(I18nManager.getText("dialog.about.credits.translations") + " : "),
@@ -225,10 +226,16 @@ public class AboutScreen extends GenericFunction
                // Read me
                JPanel readmePanel = new JPanel();
                readmePanel.setLayout(new BorderLayout());
-               JTextArea textArea = new JTextArea(getReadmeText());
-               textArea.setEditable(false);
-               textArea.setLineWrap(true); textArea.setWrapStyleWord(true);
-               JScrollPane scrollPane = new JScrollPane(textArea);
+               _aboutTextArea = new JTextArea(I18nManager.getText("details.photo.loading"));
+               // Set readme text in separate thread so that about screen pops up sooner
+               new Thread(new Runnable() {
+                       public void run() {
+                               _aboutTextArea.setText(getReadmeText());
+                       }
+               }).start();
+               _aboutTextArea.setEditable(false);
+               _aboutTextArea.setLineWrap(true); _aboutTextArea.setWrapStyleWord(true);
+               JScrollPane scrollPane = new JScrollPane(_aboutTextArea);
                scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
                scrollPane.setPreferredSize(new Dimension(600, 130));
                readmePanel.add(scrollPane, BorderLayout.CENTER);
diff --git a/tim/prune/function/DeleteFieldValues.java b/tim/prune/function/DeleteFieldValues.java
new file mode 100644 (file)
index 0000000..b6c97f5
--- /dev/null
@@ -0,0 +1,172 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.Field;
+import tim.prune.data.FieldList;
+import tim.prune.data.Track;
+import tim.prune.undo.UndoDeleteFieldValues;
+
+/**
+ * Class to provide the function to delete the values of a single field
+ * for all points in the current range
+ */
+public class DeleteFieldValues extends GenericFunction
+{
+       private JDialog _dialog = null;
+       private JList _fieldList = null;
+       private FieldListModel _listModel = null;
+       private JButton _okButton = null;
+
+
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public DeleteFieldValues(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.deletefieldvalues";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               // Make dialog window
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
+               // refresh the dialog
+               initDialog();
+               // Check whether any fields left
+               if (_listModel.getSize() < 1) {
+                       _app.showErrorMessage(getNameKey(), "dialog.deletefieldvalues.nofields");
+               }
+               else {
+                       _dialog.setVisible(true);
+               }
+       }
+
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private JPanel makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout());
+               dialogPanel.add(new JLabel(I18nManager.getText("dialog.deletefieldvalues.intro")), BorderLayout.NORTH);
+               // List in centre
+               _fieldList = new JList(new String[] {"First field", "Second field"});
+               // These entries will be replaced by the initDialog method
+               _fieldList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               _fieldList.addListSelectionListener(new ListSelectionListener() {
+                       public void valueChanged(ListSelectionEvent e) {
+                               _okButton.setEnabled(_fieldList.getSelectedIndex() >= 0);
+                       }
+               });
+               dialogPanel.add(new JScrollPane(_fieldList), BorderLayout.CENTER);
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               ActionListener okListener = new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               finish();
+                       }
+               };
+               _okButton.addActionListener(okListener);
+               _okButton.setEnabled(false);
+               buttonPanel.add(_okButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               return dialogPanel;
+       }
+
+       /**
+        * Initialise the dialog with the field list
+        */
+       private void initDialog()
+       {
+               _listModel = new FieldListModel();
+               int selStart = _app.getTrackInfo().getSelection().getStart();
+               int selEnd = _app.getTrackInfo().getSelection().getEnd();
+               // Loop over fields in track
+               final Track track = _app.getTrackInfo().getTrack();
+               FieldList fieldsInTrack = track.getFieldList();
+               for (int i=0; i<fieldsInTrack.getNumFields(); i++) {
+                       Field field = fieldsInTrack.getField(i);
+                       if (field != Field.LATITUDE && field != Field.LONGITUDE
+                               && track.hasData(field, selStart, selEnd))
+                       {
+                               _listModel.addField(field);
+                       }
+               }
+               _fieldList.setModel(_listModel);
+               _fieldList.clearSelection();
+               _okButton.setEnabled(false);
+       }
+
+       /**
+        * Finish the dialog when OK pressed
+        */
+       private void finish()
+       {
+               // Complete function
+               Field field = _listModel.getField(_fieldList.getSelectedIndex());
+               if (field != null)
+               {
+                       UndoDeleteFieldValues undo = new UndoDeleteFieldValues(_app.getTrackInfo(), field);
+                       final int selStart = _app.getTrackInfo().getSelection().getStart();
+                       final int selEnd = _app.getTrackInfo().getSelection().getEnd();
+                       final Track track = _app.getTrackInfo().getTrack();
+                       for (int i=selStart; i<= selEnd; i++) {
+                               track.getPoint(i).setFieldValue(field, null, false);
+                       }
+                       _dialog.dispose();
+                       _app.getTrackInfo().getSelection().markInvalid();
+                       UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_EDITED);
+                       _app.completeFunction(undo, I18nManager.getText("confirm.deletefieldvalues"));
+               }
+       }
+}
diff --git a/tim/prune/function/Export3dFunction.java b/tim/prune/function/Export3dFunction.java
new file mode 100644 (file)
index 0000000..5b12125
--- /dev/null
@@ -0,0 +1,39 @@
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+
+/**
+ * Abstract superclass for pov and svg export functions
+ */
+public abstract class Export3dFunction extends GenericFunction
+{
+       /** altitude exaggeration factor */
+       protected double _altFactor = 50.0;
+
+       /**
+        * Required constructor
+        * @param inApp App object
+        */
+       public Export3dFunction(App inApp) {
+               super(inApp);
+       }
+
+       /**
+        * Set the coordinates for the camera
+        * @param inX X coordinate of camera
+        * @param inY Y coordinate of camera
+        * @param inZ Z coordinate of camera
+        */
+       public abstract void setCameraCoordinates(double inX, double inY, double inZ);
+
+       /**
+        * @param inFactor exaggeration factor
+        */
+       public void setAltitudeExaggeration(double inFactor)
+       {
+               if (inFactor >= 1.0) {
+                       _altFactor = inFactor;
+               }
+       }
+}
diff --git a/tim/prune/function/FieldListModel.java b/tim/prune/function/FieldListModel.java
new file mode 100644 (file)
index 0000000..89a721c
--- /dev/null
@@ -0,0 +1,52 @@
+package tim.prune.function;
+
+import java.util.ArrayList;
+import javax.swing.AbstractListModel;
+import tim.prune.data.Field;
+
+/**
+ * Class to act as a list model for the delete field values function
+ */
+public class FieldListModel extends AbstractListModel
+{
+       /** ArrayList containing fields */
+       private ArrayList<Field> _fields = new ArrayList<Field>();
+
+
+       /**
+        * Add a field to the list
+        * @param inField field object to add
+        */
+       public void addField(Field inField)
+       {
+               if (inField != null) {_fields.add(inField);}
+       }
+
+       /**
+        * @return number of elements in list
+        */
+       public int getSize()
+       {
+               return _fields.size();
+       }
+
+       /**
+        * @param inRow row number
+        * @return String for specified row
+        */
+       public Object getElementAt(int inRow)
+       {
+               if (inRow < 0 || inRow >= getSize()) {return null;}
+               return _fields.get(inRow).getName();
+       }
+
+       /**
+        * @param inRow row number
+        * @return specified Field object
+        */
+       public Field getField(int inRow)
+       {
+               if (inRow < 0 || inRow >= getSize()) {return null;}
+               return _fields.get(inRow);
+       }
+}
diff --git a/tim/prune/function/SelectTracksFunction.java b/tim/prune/function/SelectTracksFunction.java
new file mode 100644 (file)
index 0000000..b7ff0a4
--- /dev/null
@@ -0,0 +1,204 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.ListSelectionModel;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.Altitude;
+import tim.prune.data.Field;
+import tim.prune.data.SourceInfo;
+import tim.prune.data.Track;
+import tim.prune.load.TrackNameList;
+
+/**
+ * Function to allow the selection of which tracks to load from the file / stream
+ */
+public class SelectTracksFunction extends GenericFunction
+{
+       private Field[] _fieldArray = null;
+       private Object[][] _dataArray = null;
+       private Altitude.Format _altFormat = Altitude.Format.NO_FORMAT;
+       private SourceInfo _sourceInfo = null;
+       private TrackNameList _trackNameList = null;
+       private JDialog _dialog = null;
+       private JList _trackList = null;
+
+       /**
+        * Constructor
+        * @param inApp app object to use for load
+        * @param inFieldArray field array
+        * @param inDataArray data array
+        * @param inAltFormat altitude format
+        * @param inSourceInfo source information
+        * @param inTrackNameList track name list
+        */
+       public SelectTracksFunction(App inApp, Field[] inFieldArray, Object[][] inDataArray,
+               Altitude.Format inAltFormat, SourceInfo inSourceInfo, TrackNameList inTrackNameList)
+       {
+               super(inApp);
+               _fieldArray = inFieldArray;
+               _dataArray = inDataArray;
+               _altFormat = inAltFormat;
+               _sourceInfo = inSourceInfo;
+               _trackNameList = inTrackNameList;
+       }
+
+       /**
+        * Start the function
+        */
+       public void begin()
+       {
+               _dialog = new JDialog(_parentFrame, I18nManager.getText("function.open"));
+               _dialog.setLocationRelativeTo(_parentFrame);
+               _dialog.getContentPane().add(makeContents());
+               _dialog.pack();
+               _dialog.setVisible(true);
+               selectAll();
+       }
+
+       /**
+        * @return the contents of the window as a Component
+        */
+       private Component makeContents()
+       {
+               JPanel mainPanel = new JPanel();
+               mainPanel.setLayout(new BorderLayout());
+               mainPanel.add(new JLabel(I18nManager.getText("dialog.selecttracks.intro")), BorderLayout.NORTH);
+               // track list
+               final int numTracks = _trackNameList.getNumTracks();
+               String[] names = new String[numTracks];
+               for (int i=0; i<numTracks; i++)
+               {
+                       String name = _trackNameList.getTrackName(i);
+                       if (name == null || name.equals("")) {
+                               name = I18nManager.getText("dialog.selecttracks.noname");
+                       }
+                       names[i] = name + " (" + _trackNameList.getNumPointsInTrack(i) + ")";
+               }
+               _trackList = new JList(names);
+               _trackList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+               mainPanel.add(new JScrollPane(_trackList), BorderLayout.CENTER);
+               // select all button
+               JButton selectAllButton = new JButton(I18nManager.getText("button.selectall"));
+               selectAllButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               selectAll();
+                       }
+               });
+               JPanel eastPanel = new JPanel();
+               eastPanel.add(selectAllButton);
+               mainPanel.add(eastPanel, BorderLayout.EAST);
+
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton okButton = new JButton(I18nManager.getText("button.ok"));
+               okButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               finish();
+                       }
+               });
+               buttonPanel.add(okButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                               _app.informNoDataLoaded();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+               mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               return mainPanel;
+       }
+
+       /** @return name key */
+       public String getNameKey() {
+               return "dialog.selecttracks";
+       }
+
+       /**
+        * Select all the tracks in the list
+        */
+       private void selectAll()
+       {
+               _trackList.setSelectionInterval(0, _trackNameList.getNumTracks()-1);
+       }
+
+       /**
+        * OK pressed, so finish the load
+        */
+       private void finish()
+       {
+               _dialog.dispose();
+               // Load track as it is, which is expensive but makes interrogating waypoints easier
+               Track loadedTrack = new Track();
+               loadedTrack.load(_fieldArray, _dataArray, _altFormat);
+               int[] tracks = _trackList.getSelectedIndices();
+               // Check if all tracks are selected, then don't have to filter at all
+               if (tracks.length == _trackNameList.getNumTracks()) {
+                       _app.informDataLoaded(loadedTrack, _sourceInfo);
+               }
+               else
+               {
+                       // build array of which tracks have been selected
+                       boolean[] selectedTracks = new boolean[_trackNameList.getNumTracks()];
+                       for (int i=0; i<tracks.length; i++) {
+                               selectedTracks[tracks[i]] = true;
+                       }
+                       // Loop over all points, counting points which survive filter and making flag array
+                       int numPointsSelected = 0;
+                       int currentTrack = -1;
+                       final int totalPoints = loadedTrack.getNumPoints();
+                       boolean[] selectedPoints = new boolean[totalPoints];
+                       for (int i=0; i<totalPoints; i++)
+                       {
+                               final int startOfNextTrack = _trackNameList.getStartIndex(currentTrack+1);
+                               if (i == startOfNextTrack) {currentTrack++;}
+                               if (currentTrack < 0 || selectedTracks[currentTrack] || loadedTrack.getPoint(i).isWaypoint()) {
+                                       selectedPoints[i] = true;
+                                       numPointsSelected++;
+                               }
+                       }
+                       // If none of the points have been selected, then load nothing
+                       if (numPointsSelected <= 0) {
+                               _app.informNoDataLoaded();
+                       }
+                       else {
+                               // Create new data array of required length
+                               Object[][] newDataArray = new String[numPointsSelected][];
+                               // Loop over all points again, copying surviving points
+                               int currPoint = 0;
+                               for (int i=0; i<totalPoints; i++)
+                               {
+                                       if (selectedPoints[i]) {
+                                               newDataArray[currPoint] = _dataArray[i];
+                                               currPoint++;
+                                       }
+                               }
+                               // Construct Track and call informDataLoaded
+                               Track filteredTrack = new Track();
+                               filteredTrack.load(_fieldArray, newDataArray, _altFormat);
+                               // Tell source info object which points were selected (pass selectedPoints array)
+                               _sourceInfo.setPointIndices(selectedPoints);
+                               _app.informDataLoaded(filteredTrack, _sourceInfo);
+                       }
+               }
+       }
+}
index 77fb091b3f939ccfbd78e7ef722b93cc5d476880..90bbc8fb4f598f15e491a4df5b76e3b68b057b82 100644 (file)
@@ -41,14 +41,14 @@ public class SetLanguage extends GenericFunction
        private int _startIndex = 0;
 
        /** Names of languages for display in dropdown (not translated) */
-       private static final String[] LANGUAGE_NAMES = {"deutsch", "english", "espa\u00F1ol", "fran\u00E7ais",
-               "italiano", "polski", "\u4e2d\u6587 (chinese)", "\u65E5\u672C\u8A9E (japanese)",
-               "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e", "portugu\u00EAs",
-               "afrikaans", "bahasa indonesia", "rom\u00E2n\u0103"
+       private static final String[] LANGUAGE_NAMES = {"\u010de\u0161tina", "deutsch", "english", "espa\u00F1ol",
+               "fran\u00E7ais", "italiano", "nederlands", "polski", "portugu\u00EAs", "\u4e2d\u6587 (chinese)",
+               "\u65E5\u672C\u8A9E (japanese)", "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e", "rom\u00E2n\u0103",
+               "afrikaans", "bahasa indonesia", "farsi"
        };
        /** Associated language codes (must be in same order as names!) */
-       private static final String[] LANGUAGE_CODES = {"de", "en", "es", "fr", "it", "pl", "zh", "ja",
-               "de_ch", "tr", "pt", "af", "in", "ro"
+       private static final String[] LANGUAGE_CODES = {"cz", "de", "en", "es", "fr", "it", "nl", "pl", "pt", "zh",
+               "ja", "de_ch", "tr", "ro", "af", "in", "fa"
        };
 
 
index 98a9fdd04428e42ff65cd85155ae060810fcda13..0946cf3c61534f0890ee95876942a08a17dcfd5e 100644 (file)
@@ -74,7 +74,16 @@ public class ShowKeysScreen extends GenericFunction
                introLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 10));
                mainPanel.add(introLabel, BorderLayout.NORTH);
 
-               JEditorPane kp = new JEditorPane("text/html", I18nManager.getText("dialog.keys.keylist"));
+               String keyText = I18nManager.getText("dialog.keys.keylist");
+               // If running on Mac, do global replace on "Ctrl" (or "Strg") for "Command" (or lang-specific text)
+               if (System.getProperty("mrj.version") != null) {
+                       String mod = I18nManager.getText("dialog.keys.normalmodifier");
+                       String macmod = I18nManager.getText("dialog.keys.macmodifier");
+                       if (mod != null && macmod != null && mod.length() > 1 && macmod.length() > 1) {
+                               keyText = keyText.replaceAll(mod, macmod);
+                       }
+               }
+               JEditorPane kp = new JEditorPane("text/html", keyText);
                kp.setEditable(false);
                kp.setOpaque(false);
                kp.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
index ecfd83f9b7bea07814dbcff564dcb78b63c2af09..d23a2f72cee7920c3173ddebcab15401fceaae35 100644 (file)
@@ -1,10 +1,12 @@
 package tim.prune.function.browser;
 
+import java.net.URI;
 import javax.swing.JOptionPane;
 
 
 /**
  * Class to launch a browser window to show an external map
+ * Some code and ideas taken from BareBonesBrowserLaunch at centerkey.com
  */
 public abstract class BrowserLauncher
 {
@@ -18,11 +20,13 @@ public abstract class BrowserLauncher
         */
        private static void init()
        {
+               _browserCommand = null;
                // First check if "which" command is available
                if (commandExists("which"))
                {
                        // which exists, so try browsers in turn
-                       String[] browsersToTry = {"firefox", "iceweasel", "konqueror", "opera", "epiphany", "mozilla", "safari", "lynx"};
+                       String[] browsersToTry = {"firefox", "iceweasel", "konqueror", "opera", "epiphany",
+                               "mozilla", "safari", "google-chrome", "lynx"};
                        String browserFound = null;
                        for (int i=0; i<browsersToTry.length && browserFound == null; i++)
                        {
@@ -33,12 +37,12 @@ public abstract class BrowserLauncher
                                _browserCommand = new String[] {browserFound, null};
                        }
                }
-               else
+
+               if (_browserCommand == null)
                {
-                       // no which command, so check if os name looks like a mac
+                       // no which command (or none of the browsers found), so check if os name looks like a mac
                        String osName = System.getProperty("os.name").toLowerCase();
-                       boolean isMacOsx = osName.indexOf("mac os") >= 0
-                        || osName.indexOf("darwin") >= 0;
+                       boolean isMacOsx = osName.indexOf("mac os") >= 0 || osName.indexOf("darwin") >= 0;
                        if (isMacOsx) {
                                // for Mac Osx just use "open" command
                                _browserCommand = new String[] {"open", null};
@@ -79,24 +83,35 @@ public abstract class BrowserLauncher
         */
        public static void launchBrowser(String inUrl)
        {
-               if (!_initialised) {init();}
-               if (_browserCommand == null) {
-                       JOptionPane.showMessageDialog(null, "Cannot show url: " + inUrl);
+               // First choice is to try the Desktop library from java 6, if available
+               try {
+                       Class<?> d = Class.forName("java.awt.Desktop");
+                       d.getDeclaredMethod("browse", new Class[] {URI.class}).invoke(
+                               d.getDeclaredMethod("getDesktop").invoke(null), new Object[] {URI.create(inUrl)});
+                       //above code mimics: Desktop.getDesktop().browse(URI.create(inUrl));
                }
-               else
+               catch (Exception ignore)
                {
-                       try
-                       {
-                               // enclose url in quotes if necessary
-                               String url = inUrl;
-                               if (_urlNeedsQuotes) {url = "\"" + url + "\"";}
-                               // Fill in url in last element of coommand array
-                               _browserCommand[_browserCommand.length - 1] = url;
-                               // execute command to launch browser
-                               Runtime.getRuntime().exec(_browserCommand);
+                       // The Desktop call failed, need to try backup methods
+                       if (!_initialised) {init();}
+                       if (_browserCommand == null) {
+                               JOptionPane.showMessageDialog(null, "Cannot show url: " + inUrl);
                        }
-                       catch (Exception e) {
-                               JOptionPane.showMessageDialog(null, "Failed to show url: " + inUrl);
+                       else
+                       {
+                               try
+                               {
+                                       // enclose url in quotes if necessary
+                                       String url = inUrl;
+                                       if (_urlNeedsQuotes) {url = "\"" + url + "\"";}
+                                       // Fill in url in last element of command array
+                                       _browserCommand[_browserCommand.length - 1] = url;
+                                       // execute command to launch browser
+                                       Runtime.getRuntime().exec(_browserCommand);
+                               }
+                               catch (Exception e) {
+                                       JOptionPane.showMessageDialog(null, "Failed to show url: " + inUrl);
+                               }
                        }
                }
        }
index c5ea99174894de82c675b8b27ca4d9dd0f172f5c..fa673c20df65e37797b520d9af4d0daa911b81af 100644 (file)
@@ -48,7 +48,7 @@ public class DuplicatePointAlgorithm extends CompressionAlgorithm
                                        for (int j=i-NUM_POINTS_TO_BACKTRACK; j<i; j++)
                                        {
                                                if (j<0) {j=0;} // only look at last few points, but not before 0
-                                               if (currPoint.isDuplicate(_track.getPoint(j)))
+                                               if (!inFlags[j] && currPoint.isDuplicate(_track.getPoint(j)))
                                                {
                                                        inFlags[i] = true;
                                                        numDeleted++;
index 9e02a1e69691d35dbbbeaff051f7f751f71258f3..2321ece0bda861184eaecca8879eb134624a87ec 100644 (file)
@@ -7,6 +7,7 @@ import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 
+import javax.swing.BorderFactory;
 import javax.swing.JButton;
 import javax.swing.JDialog;
 import javax.swing.JFrame;
@@ -102,7 +103,10 @@ public class PointEditor
                });
                _table.setPreferredScrollableViewportSize(new Dimension(_table.getWidth(), _table.getRowHeight() * 6));
                panel.add(new JScrollPane(_table), BorderLayout.CENTER);
-               panel.add(new JLabel(I18nManager.getText("dialog.pointedit.text")), BorderLayout.NORTH);
+               // Label at top
+               JLabel topLabel = new JLabel(I18nManager.getText("dialog.pointedit.text"));
+               topLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 3, 6));
+               panel.add(topLabel, BorderLayout.NORTH);
                _editButton = new JButton(I18nManager.getText("button.edit"));
                _editButton.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
index 9f9edc4b7057bf052a8379fe7d59557f137b2478..5939e63ef54656102f74f16d6d1f75139843571c 100644 (file)
 package tim.prune.function.gpsies;\r
 \r
+import java.net.HttpURLConnection;\r
 import java.net.URLConnection;\r
 import java.net.URL;\r
 import java.io.IOException;\r
-import java.io.File;\r
 import java.io.InputStream;\r
 import java.util.Random;\r
 import java.io.OutputStream;\r
-import java.io.FileInputStream;\r
 \r
 /**\r
- * Taken from Client HTTP Request class\r
+ * Taken from the Client HTTP Request class at com.myjavatools.web\r
+ * and subsequently simplified and modified\r
  * @author Vlad Patryshev\r
  */\r
 public class FormPoster\r
 {\r
-       private URLConnection _connection;\r
+       private URLConnection _connection = null;\r
        private OutputStream _os = null;\r
-       private static final Random random = new Random();\r
-       private static final String boundary = "---------------------------"\r
+       private static final Random RANDOM_GEN = new Random();\r
+       private static final String BOUNDARY = "---------------------------"\r
                + randomString() + randomString() + randomString();\r
 \r
 \r
+       /** Connect (if not already connected) */\r
        protected void connect() throws IOException {\r
                if (_os == null) _os = _connection.getOutputStream();\r
        }\r
 \r
+       /** Write a single character */\r
        protected void write(char c) throws IOException {\r
                connect();\r
                _os.write(c);\r
        }\r
 \r
+       /** Write a string */\r
        protected void write(String s) throws IOException {\r
                connect();\r
                _os.write(s.getBytes());\r
        }\r
 \r
+       /** Write a -r-n newline sequence */\r
        protected void newline() throws IOException {\r
-               connect();\r
                write("\r\n");\r
        }\r
 \r
+       /** Write a string followed by a newline */\r
        protected void writeln(String s) throws IOException {\r
-               connect();\r
                write(s);\r
                newline();\r
        }\r
 \r
-       protected static String randomString() {\r
-               return Long.toString(random.nextLong(), 36);\r
+       /** Generate a random alphanumeric string */\r
+       private static String randomString() {\r
+               return Long.toString(RANDOM_GEN.nextLong(), 36);\r
        }\r
 \r
+       /** Write a boundary marker */\r
        private void boundary() throws IOException {\r
                write("--");\r
-               write(boundary);\r
+               write(BOUNDARY);\r
        }\r
 \r
-       /**\r
-        * Creates a new multipart POST HTTP request on a freshly opened URLConnection\r
-        *\r
-        * @param inConnection an already open URL connection\r
-        * @throws IOException\r
-        */\r
-       public FormPoster(URLConnection inConnection) throws IOException\r
-       {\r
-               _connection = inConnection;\r
-               _connection.setDoOutput(true);\r
-               _connection.setRequestProperty("Content-Type",\r
-                               "multipart/form-data; boundary=" + boundary);\r
-       }\r
 \r
        /**\r
         * Creates a new multipart POST HTTP request for a specified URL\r
-        *\r
         * @param url the URL to send request to\r
         * @throws IOException\r
         */\r
-       public FormPoster(URL url) throws IOException {\r
-               this(url.openConnection());\r
-       }\r
-\r
-       /**\r
-        * Creates a new multipart POST HTTP request for a specified URL string\r
-        *\r
-        * @param urlString the string representation of the URL to send request to\r
-        * @throws IOException\r
-        */\r
-       public FormPoster(String urlString) throws IOException {\r
-               this(new URL(urlString));\r
+       public FormPoster(URL inUrl) throws IOException\r
+       {\r
+               _connection = inUrl.openConnection();\r
+               _connection.setDoOutput(true);\r
+               _connection.setRequestProperty("Content-Type",\r
+                       "multipart/form-data; boundary=" + BOUNDARY);\r
        }\r
 \r
-\r
-       private void writeName(String name) throws IOException\r
+       /** Write a header with the given name */\r
+       private void writeName(String inName) throws IOException\r
        {\r
                newline();\r
                write("Content-Disposition: form-data; name=\"");\r
-               write(name);\r
+               write(inName);\r
                write('"');\r
        }\r
 \r
@@ -105,23 +90,22 @@ public class FormPoster
         * @param value parameter value\r
         * @throws IOException\r
         */\r
-       public void setParameter(String name, String value) throws IOException\r
+       public void setParameter(String inName, String inValue) throws IOException\r
        {\r
                boundary();\r
-               writeName(name);\r
+               writeName(inName);\r
                newline(); newline();\r
-               writeln(value);\r
+               writeln(inValue);\r
        }\r
 \r
+       /** Pipe the contents of the input stream to the output stream */\r
        private static void pipe(InputStream in, OutputStream out) throws IOException\r
        {\r
                byte[] buf = new byte[500000];\r
                int nread;\r
-               int total = 0;\r
                synchronized (in) {\r
                        while((nread = in.read(buf, 0, buf.length)) >= 0) {\r
                                out.write(buf, 0, nread);\r
-                               total += nread;\r
                        }\r
                }\r
                out.flush();\r
@@ -130,60 +114,35 @@ public class FormPoster
 \r
        /**\r
         * adds a file parameter to the request\r
-        * @param name parameter name\r
-        * @param filename the name of the file\r
-        * @param is input stream to read the contents of the file from\r
+        * @param inName parameter name\r
+        * @param inFilename the name of the file\r
+        * @param inStream input stream to read the contents of the file from\r
         * @throws IOException\r
         */\r
-       public void setParameter(String name, String filename, InputStream is) throws IOException\r
+       public void setParameter(String inName, String inFilename, InputStream inStream) throws IOException\r
        {\r
                boundary();\r
-               writeName(name);\r
+               writeName(inName);\r
                write("; filename=\"");\r
-               write(filename);\r
+               write(inFilename);\r
                write('"');\r
                newline();\r
                write("Content-Type: ");\r
-               String type = URLConnection.guessContentTypeFromName(filename);\r
-               if (type == null) type = "application/octet-stream";\r
+               String type = URLConnection.guessContentTypeFromName(inFilename);\r
+               if (type == null) {type = "application/octet-stream";}\r
                writeln(type);\r
                newline();\r
-               pipe(is, _os);\r
+               pipe(inStream, _os);\r
                newline();\r
        }\r
 \r
-       /**\r
-        * adds a file parameter to the request\r
-        * @param name parameter name\r
-        * @param file the file to upload\r
-        * @throws IOException\r
-        */\r
-       public void setParameter(String name, File file) throws IOException {\r
-               setParameter(name, file.getPath(), new FileInputStream(file));\r
-       }\r
-\r
-       /**\r
-        * adds a parameter to the request; if the parameter is a File, the file is uploaded,\r
-        * otherwise the string value of the parameter is passed in the request\r
-        * @param name parameter name\r
-        * @param object parameter value, a File or anything else that can be stringified\r
-        * @throws IOException\r
-        */\r
-       public void setParameter(String name, Object object) throws IOException\r
-       {\r
-               if (object instanceof File) {\r
-                       setParameter(name, (File) object);\r
-               } else {\r
-                       setParameter(name, object.toString());\r
-               }\r
-       }\r
-\r
        /**\r
         * posts the requests to the server\r
         * @return input stream with the server response\r
         * @throws IOException\r
         */\r
-       public InputStream post() throws IOException {\r
+       public InputStream post() throws IOException\r
+       {\r
                boundary();\r
                writeln("--");\r
                _os.close();\r
@@ -191,136 +150,13 @@ public class FormPoster
        }\r
 \r
        /**\r
-        * post the POST request to the server, with the specified parameter\r
-        * @param name parameter name\r
-        * @param value parameter value\r
-        * @return input stream with the server response\r
-        * @throws IOException\r
-        * @see setParameter\r
-        */\r
-       public InputStream post(String name, Object value) throws IOException {\r
-               setParameter(name, value);\r
-               return post();\r
-       }\r
-\r
-       /**\r
-        * post the POST request to the server, with the specified parameters\r
-        * @param name1 first parameter name\r
-        * @param value1 first parameter value\r
-        * @param name2 second parameter name\r
-        * @param value2 second parameter value\r
-        * @return input stream with the server response\r
-        * @throws IOException\r
-        * @see setParameter\r
-        */\r
-       public InputStream post(String name1, Object value1, String name2, Object value2) throws IOException {\r
-               setParameter(name1, value1);\r
-               return post(name2, value2);\r
-       }\r
-\r
-       /**\r
-        * post the POST request to the server, with the specified parameters\r
-        * @param name1 first parameter name\r
-        * @param value1 first parameter value\r
-        * @param name2 second parameter name\r
-        * @param value2 second parameter value\r
-        * @param name3 third parameter name\r
-        * @param value3 third parameter value\r
-        * @return input stream with the server response\r
-        * @throws IOException\r
-        * @see setParameter\r
-        */\r
-       public InputStream post(String name1, Object value1, String name2, Object value2,\r
-               String name3, Object value3) throws IOException\r
-       {\r
-               setParameter(name1, value1);\r
-               return post(name2, value2, name3, value3);\r
-       }\r
-\r
-       /**\r
-        * post the POST request to the server, with the specified parameters\r
-        * @param name1 first parameter name\r
-        * @param value1 first parameter value\r
-        * @param name2 second parameter name\r
-        * @param value2 second parameter value\r
-        * @param name3 third parameter name\r
-        * @param value3 third parameter value\r
-        * @param name4 fourth parameter name\r
-        * @param value4 fourth parameter value\r
-        * @return input stream with the server response\r
-        * @throws IOException\r
-        * @see setParameter\r
-        */\r
-       public InputStream post(String name1, Object value1, String name2, Object value2,\r
-               String name3, Object value3, String name4, Object value4) throws IOException\r
-       {\r
-               setParameter(name1, value1);\r
-               return post(name2, value2, name3, value3, name4, value4);\r
-       }\r
-\r
-       /**\r
-        * post the POST request specified URL, with the specified parameter\r
-        * @param name parameter name\r
-        * @param value parameter value\r
-        * @return input stream with the server response\r
-        * @throws IOException\r
-        * @see setParameter\r
-        */\r
-       public static InputStream post(URL url, String name1, Object value1) throws IOException {\r
-               return new FormPoster(url).post(name1, value1);\r
-       }\r
-\r
-       /**\r
-        * post the POST request to specified URL, with the specified parameters\r
-        * @param name1 first parameter name\r
-        * @param value1 first parameter value\r
-        * @param name2 second parameter name\r
-        * @param value2 second parameter value\r
-        * @return input stream with the server response\r
-        * @throws IOException\r
-        * @see setParameter\r
-        */\r
-       public static InputStream post(URL url, String name1, Object value1,\r
-               String name2, Object value2) throws IOException\r
-       {\r
-               return new FormPoster(url).post(name1, value1, name2, value2);\r
-       }\r
-\r
-       /**\r
-        * post the POST request to specified URL, with the specified parameters\r
-        * @param name1 first parameter name\r
-        * @param value1 first parameter value\r
-        * @param name2 second parameter name\r
-        * @param value2 second parameter value\r
-        * @param name3 third parameter name\r
-        * @param value3 third parameter value\r
-        * @return input stream with the server response\r
-        * @throws IOException\r
-        * @see setParameter\r
+        * @return the HTTP response code, 200 for success or -1 if not available\r
         */\r
-       public static InputStream post(URL url, String name1, Object value1, String name2, Object value2,\r
-               String name3, Object value3) throws IOException\r
+       public int getResponseCode() throws IOException\r
        {\r
-               return new FormPoster(url).post(name1, value1, name2, value2, name3, value3);\r
-       }\r
-\r
-       /**\r
-        * post the POST request to specified URL, with the specified parameters\r
-        * @param name1 first parameter name\r
-        * @param value1 first parameter value\r
-        * @param name2 second parameter name\r
-        * @param value2 second parameter value\r
-        * @param name3 third parameter name\r
-        * @param value3 third parameter value\r
-        * @param name4 fourth parameter name\r
-        * @param value4 fourth parameter value\r
-        * @return input stream with the server response\r
-        * @throws IOException\r
-        * @see setParameter\r
-        */\r
-       public static InputStream post(URL url, String name1, Object value1, String name2, Object value2,\r
-               String name3, Object value3, String name4, Object value4) throws IOException\r
-       {\r
-               return new FormPoster(url).post(name1, value1, name2, value2, name3, value3, name4, value4);\r
+               if (_connection != null && _connection instanceof HttpURLConnection) {\r
+                       return ((HttpURLConnection) _connection).getResponseCode();\r
+               }\r
+               return -1;\r
        }\r
 }\r
diff --git a/tim/prune/function/gpsies/UploadGpsiesFunction.java b/tim/prune/function/gpsies/UploadGpsiesFunction.java
new file mode 100644 (file)
index 0000000..6bd762f
--- /dev/null
@@ -0,0 +1,326 @@
+package tim.prune.function.gpsies;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.border.EtchedBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.function.browser.BrowserLauncher;
+import tim.prune.gui.GuiGridLayout;
+import tim.prune.save.GpxExporter;
+
+/**
+ * Function to upload track information up to Gpsies.com
+ */
+public class UploadGpsiesFunction extends GenericFunction
+{
+       /** Dialog object */
+       private JDialog _dialog = null;
+       /** Edit box for user name */
+       private JTextField _usernameField = null;
+       /** Edit box for password */
+       private JPasswordField _passwordField = null;
+       /** Name of track */
+       private JTextField _nameField = null;
+       /** Description */
+       private JTextArea _descField = null;
+       /** Private checkbox */
+       private JCheckBox _privateCheckbox = null;
+       /** Activity checkboxes */
+       private JCheckBox[] _activityCheckboxes = null;
+       /** Writer object for GPX export */
+       private OutputStreamWriter _writer = null;
+       /** upload button */
+       private JButton _uploadButton = null;
+
+       /** URL to post form to */
+       private static final String GPSIES_URL = "http://www.gpsies.com/upload.do";
+       /** Keys for describing activities */
+       private static final String[] ACTIVITY_KEYS = {"trekking", "walking", "jogging",
+               "biking", "motorbiking", "snowshoe", "sailing", "skating"};
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public UploadGpsiesFunction(App inApp) {
+               super(inApp);
+       }
+
+       /**
+        * @return name key
+        */
+       public String getNameKey() {
+               return "function.uploadgpsies";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               // Initialise dialog, show empty list
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
+               // Show dialog
+               _dialog.setVisible(true);
+       }
+
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout());
+
+               JPanel gridPanel = new JPanel();
+               GuiGridLayout grid = new GuiGridLayout(gridPanel);
+               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.username")));
+               _usernameField = new JTextField(15);
+               grid.add(_usernameField);
+               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.password")));
+               _passwordField = new JPasswordField(15);
+               grid.add(_passwordField);
+               // Track name and description
+               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.column.name")));
+               _nameField = new JTextField(15);
+               grid.add(_nameField);
+               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.description")));
+               _descField = new JTextArea(5, 15);
+               _descField.setLineWrap(true);
+               _descField.setWrapStyleWord(true);
+               grid.add(new JScrollPane(_descField));
+               // Listener on all these text fields to enable/disable the ok button
+               KeyAdapter keyListener = new KeyAdapter() {
+                       /** Key released */
+                       public void keyReleased(KeyEvent inE) {
+                               enableOK();
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
+                                       _dialog.dispose();
+                               }
+                       }
+               };
+               _usernameField.addKeyListener(keyListener);
+               _passwordField.addKeyListener(keyListener);
+               _nameField.addKeyListener(keyListener);
+               // Listen for tabs on description field, to change focus not enter tabs
+               _descField.addKeyListener(new KeyAdapter() {
+                       /** Key pressed */
+                       public void keyPressed(KeyEvent inE) {
+                               if (inE.getKeyCode() == KeyEvent.VK_TAB) {
+                                       inE.consume();
+                                       if (inE.isShiftDown()) {
+                                               _nameField.requestFocusInWindow();
+                                       }
+                                       else {
+                                               _privateCheckbox.requestFocusInWindow();
+                                       }
+                               }
+                       }
+               });
+               // Listen for Ctrl-backspace on password field (delete contents)
+               _passwordField.addKeyListener(new KeyAdapter() {
+                       /** Key released */
+                       public void keyReleased(KeyEvent inE) {
+                               if (inE.isControlDown() && (inE.getKeyCode() == KeyEvent.VK_BACK_SPACE
+                                       || inE.getKeyCode() == KeyEvent.VK_DELETE)) {
+                                       _passwordField.setText("");
+                               }
+                       }
+               });
+               // Checkbox for private / public
+               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.keepprivate")));
+               _privateCheckbox = new JCheckBox();
+               _privateCheckbox.setSelected(true);
+               grid.add(_privateCheckbox);
+
+               // panel for activity type checkboxes
+               JPanel activityPanel = new JPanel();
+               activityPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
+               ChangeListener checkListener = new ChangeListener() {
+                       public void stateChanged(ChangeEvent arg0) {
+                               enableOK();
+                       }
+               };
+               GuiGridLayout actGrid = new GuiGridLayout(activityPanel, true);
+               final int numActivities = ACTIVITY_KEYS.length;
+               _activityCheckboxes = new JCheckBox[numActivities];
+               for (int i=0; i<numActivities; i++)
+               {
+                       _activityCheckboxes[i] = new JCheckBox(I18nManager.getText("dialog.gpsies.activity." + ACTIVITY_KEYS[i]));
+                       _activityCheckboxes[i].addChangeListener(checkListener);
+                       actGrid.add(_activityCheckboxes[i]);
+               }
+               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.activities")));
+               grid.add(activityPanel);
+               JPanel midPanel = new JPanel();
+               midPanel.setLayout(new BoxLayout(midPanel, BoxLayout.Y_AXIS));
+               midPanel.add(gridPanel);
+               dialogPanel.add(midPanel, BorderLayout.CENTER);
+
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               _uploadButton = new JButton(I18nManager.getText("button.upload"));
+               _uploadButton.setEnabled(false);
+               _uploadButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               startUpload();
+                       }
+               });
+               buttonPanel.add(_uploadButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               return dialogPanel;
+       }
+
+
+       /**
+        * Check the inputs and enable or disable the upload button
+        */
+       private void enableOK()
+       {
+               // Check for lengths of input fields - only username, password and filename are required
+               boolean ok = (_usernameField.getText().length() > 0 && _nameField.getText().length() > 0);
+               if (ok) {
+                       // also check password field
+                       char[] pass = _passwordField.getPassword();
+                       ok = pass.length > 0;
+                       for (int i=0; i<pass.length; i++) {pass[i] = '0';} // recommended by javadoc
+                       if (ok) {
+                               ok = false;
+                               for (int i=0; i<_activityCheckboxes.length; i++) {
+                                       ok = ok || _activityCheckboxes[i].isSelected();
+                               }
+                       }
+               }
+               _uploadButton.setEnabled(ok);
+       }
+
+
+       /**
+        * Start the upload process (require separate thread?)
+        */
+       private void startUpload()
+       {
+               try
+               {
+                       FormPoster poster = new FormPoster(new URL(GPSIES_URL));
+                       poster.setParameter("device", "Prune");
+                       poster.setParameter("username", _usernameField.getText());
+                       poster.setParameter("password", new String(_passwordField.getPassword()));
+                       boolean hasActivity = false;
+                       for (int i=0; i<ACTIVITY_KEYS.length; i++)
+                       {
+                               if (_activityCheckboxes[i].isSelected()) {
+                                       hasActivity = true;
+                                       poster.setParameter("trackTypes", ACTIVITY_KEYS[i]);
+                               }
+                       }
+                       if (!hasActivity) {poster.setParameter("trackTypes", "walking");} // default if none given
+                       poster.setParameter("filename", _nameField.getText());
+                       poster.setParameter("fileDescription", _descField.getText());
+                       poster.setParameter("startpointCountry", "DE");
+                       poster.setParameter("endpointCountry", "DE"); // both those will be corrected by gpsies
+                       poster.setParameter("status", (_privateCheckbox.isSelected()?"3":"1"));
+                       poster.setParameter("submit", "speichern"); // required
+                       // Use Pipes to connect the GpxExporter's output with the FormPoster's input
+                       PipedInputStream iStream = new PipedInputStream();
+                       PipedOutputStream oStream = new PipedOutputStream(iStream);
+                       _writer = new OutputStreamWriter(oStream);
+                       new Thread(new Runnable() {
+                               public void run() {
+                                       boolean[] saveFlags = {true, true, true, false, true}; // export everything
+                                       try {
+                                               GpxExporter.exportData(_writer, _app.getTrackInfo(), _nameField.getText(), null, saveFlags, false);
+                                               _writer.close();
+                                       } catch (IOException e) {
+                                               e.printStackTrace();
+                                       }
+                               }
+                       }).start();
+                       poster.setParameter("formFile", "filename.gpx", iStream);
+
+                       BufferedInputStream answer = new BufferedInputStream(poster.post());
+                       int response = poster.getResponseCode();
+                       BufferedReader reader = new BufferedReader(new InputStreamReader(answer));
+                       String line = reader.readLine();
+                       // Try to extract gpsies page url from the returned message
+                       String pageUrl = null;
+                       if (response == 200 && line.substring(0, 2).toUpperCase().equals("OK"))
+                       {
+                               final int bracketPos = line.indexOf('[');
+                               if (bracketPos > 0 && line.endsWith("]")) {
+                                       pageUrl = line.substring(bracketPos + 1, line.length()-1);
+                               }
+                       }
+                       if (pageUrl != null)
+                       {
+                               // OK received and managed to extract a Url from the return message.
+                               int userChoice = JOptionPane.showConfirmDialog(_app.getFrame(),
+                                       I18nManager.getText("dialog.gpsies.confirmopenpage"),
+                                       I18nManager.getText(getNameKey()), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
+                               if (userChoice == JOptionPane.OK_OPTION) {
+                                       BrowserLauncher.launchBrowser(pageUrl);
+                               }
+                       }
+                       else {
+                               _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.gpsies.uploadnotok")
+                                       + ": " + line);
+                       }
+               }
+               catch (MalformedURLException e) {}
+               catch (IOException ioe) {
+                       _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.gpsies.uploadfailed") + ": "
+                               + ioe.getClass().getName() + " : " + ioe.getMessage());
+               }
+               _dialog.dispose();
+       }
+}
index 30183d2d6cd13c0e780a018bc4ca6dd1ba2444b5..1bf26cd8646c9b36ad8761f07b74c55f0636a978 100644 (file)
@@ -15,6 +15,7 @@ import java.util.zip.ZipInputStream;
 import javax.swing.JButton;
 import javax.swing.JDialog;
 import javax.swing.JLabel;
+import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JProgressBar;
 
@@ -48,6 +49,7 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
        /** Altitude below which is considered void */
        private static final int VOID_VAL = -32768;
 
+
        /**
         * Constructor
         * @param inApp App object
@@ -114,14 +116,40 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
         */
        public void run()
        {
-               _dialog.setVisible(true);
                // Compile list of tiles to get
                Track track = _app.getTrackInfo().getTrack();
                ArrayList<SrtmTile> tileList = new ArrayList<SrtmTile>();
+               boolean hasZeroAltitudePoints = false;
+               boolean hasNonZeroAltitudePoints = false;
+               // First, loop to see what kind of points we have
+               for (int i=0; i<track.getNumPoints(); i++)
+               {
+                       if (track.getPoint(i).hasAltitude())
+                       {
+                               if (track.getPoint(i).getAltitude().getValue() == 0) {
+                                       hasZeroAltitudePoints = true;
+                               }
+                               else {
+                                       hasNonZeroAltitudePoints = true;
+                               }
+                       }
+               }
+               // Should we overwrite the zero altitude values?
+               boolean overwriteZeros = hasZeroAltitudePoints && !hasNonZeroAltitudePoints;
+               // If non-zero values present as well, ask user whether to overwrite the zeros or not
+               if (hasNonZeroAltitudePoints && hasZeroAltitudePoints && JOptionPane.showConfirmDialog(_parentFrame,
+                       I18nManager.getText("dialog.lookupsrtm.overwritezeros"), I18nManager.getText(getNameKey()),
+                       JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
+               {
+                       overwriteZeros = true;
+               }
+
+               _dialog.setVisible(true);
+               // Now loop again to extract the required tiles
                for (int i=0; i<track.getNumPoints(); i++)
                {
-                       // Only consider points which don't have altitudes already
-                       if (!track.getPoint(i).hasAltitude())
+                       // Consider points which don't have altitudes or have zero values
+                       if (!track.getPoint(i).hasAltitude() || (overwriteZeros && track.getPoint(i).getAltitude().getValue() == 0))
                        {
                                SrtmTile tile = new SrtmTile(track.getPoint(i));
                                boolean alreadyGot = false;
@@ -133,19 +161,30 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
                                if (!alreadyGot) {tileList.add(tile);}
                        }
                }
+               lookupValues(tileList, overwriteZeros);
+       }
+
+       /**
+        * Lookup the values from SRTM data
+        * @param inTileList list of tiles to get
+        * @param inOverwriteZeros true to overwrite zero altitude values
+        */
+       private void lookupValues(ArrayList<SrtmTile> inTileList, boolean inOverwriteZeros)
+       {
+               Track track = _app.getTrackInfo().getTrack();
                UndoLookupSrtm undo = new UndoLookupSrtm(_app.getTrackInfo());
                int numAltitudesFound = 0;
                // Update progress bar
-               _progressBar.setMaximum(tileList.size());
-               _progressBar.setIndeterminate(tileList.size() <= 1);
+               _progressBar.setMaximum(inTileList.size());
+               _progressBar.setIndeterminate(inTileList.size() <= 1);
                _progressBar.setValue(0);
                // Get urls for each tile
-               URL[] urls = TileFinder.getUrls(tileList);
-               for (int t=0; t<tileList.size() && !_cancelled; t++)
+               URL[] urls = TileFinder.getUrls(inTileList);
+               for (int t=0; t<inTileList.size() && !_cancelled; t++)
                {
                        if (urls[t] != null)
                        {
-                               SrtmTile tile = tileList.get(t);
+                               SrtmTile tile = inTileList.get(t);
                                // System.out.println("tile " + t + " of " + tileList.size() + " = " + urls[t].toString());
                                try {
                                        _progressBar.setValue(t);
@@ -175,7 +214,7 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
                                                for (int p=0; p<track.getNumPoints(); p++)
                                                {
                                                        DataPoint point = track.getPoint(p);
-                                                       if (!point.hasAltitude() || point.getAltitude().getValue() == 0) {
+                                                       if (!point.hasAltitude() || (inOverwriteZeros && point.getAltitude().getValue() == 0)) {
                                                                if (new SrtmTile(point).equals(tile))
                                                                {
                                                                        double x = (point.getLongitude().getDouble() - tile.getLongitude()) * 1200;
@@ -221,8 +260,11 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
                        _app.completeFunction(undo, I18nManager.getText("confirm.lookupsrtm1") + " " + numAltitudesFound
                                + " " + I18nManager.getText("confirm.lookupsrtm2"));
                }
+               else if (inTileList.size() > 0) {
+                       _app.showErrorMessage(getNameKey(), "error.lookupsrtm.nonefound");
+               }
                else {
-                       _app.showErrorMessage(getNameKey(), "error.lookupsrtm.none");
+                       _app.showErrorMessage(getNameKey(), "error.lookupsrtm.nonerequired");
                }
        }
 
index cb3572b7b2dcdcfe14ead8c516af5b58d827a722..7b226b5f9af0afc48d1f18384f27d2b859bcd6f8 100644 (file)
@@ -16,6 +16,7 @@ public class GuiGridLayout
        private GridBagLayout _layout = null;
        private GridBagConstraints _constraints = null;
        private JPanel _panel = null;
+       private boolean _allLeft = false;
        private int _x = 0;
        private int _y = 0;
 
@@ -24,8 +25,19 @@ public class GuiGridLayout
         * @param inPanel panel using layout
         */
        public GuiGridLayout(JPanel inPanel)
+       {
+               this(inPanel, false);
+       }
+
+       /**
+        * Constructor
+        * @param inPanel panel using layout
+        * @param inAllLeft true to align all elements to left
+        */
+       public GuiGridLayout(JPanel inPanel, boolean inAllLeft)
        {
                _panel = inPanel;
+               _allLeft = inAllLeft;
                _layout = new GridBagLayout();
                _constraints = new GridBagConstraints();
                _constraints.weightx = 1.0;
@@ -47,7 +59,7 @@ public class GuiGridLayout
                _constraints.gridy = _y;
                _constraints.weightx = (_x==0?0.5:1.0);
                // set anchor
-               _constraints.anchor = (_x == 0?GridBagConstraints.LINE_END:GridBagConstraints.LINE_START);
+               _constraints.anchor = ((_x == 0 && !_allLeft)?GridBagConstraints.LINE_END:GridBagConstraints.LINE_START);
                _layout.setConstraints(inComponent, _constraints);
                // add to panel
                _panel.add(inComponent);
index 1cca456e2271b5979b9e9e59355857521191d3e4..1387fbc7e2b507ee17a9c4ad0be00d6a097f0b7c 100644 (file)
@@ -45,6 +45,7 @@ public class MenuManager implements DataSubscriber
        private JMenuItem _exportKmlItem = null;
        private JMenuItem _exportGpxItem = null;
        private JMenuItem _exportPovItem = null;
+       private JMenuItem _exportSvgItem = null;
        private JMenuItem _undoItem = null;
        private JMenuItem _clearUndoItem = null;
        private JMenuItem _editPointItem = null;
@@ -68,11 +69,13 @@ public class MenuManager implements DataSubscriber
        private JMenu     _rearrangeMenu = null;
        private JMenuItem _cutAndMoveItem = null;
        private JMenuItem _convertNamesToTimesItem = null;
+       private JMenuItem _deleteFieldValuesItem = null;
        private JCheckBoxMenuItem _mapCheckbox = null;
        private JMenuItem _show3dItem = null;
        private JMenu     _browserMapMenu = null;
        private JMenuItem _chartItem = null;
        private JMenuItem _getGpsiesItem = null;
+       private JMenuItem _uploadGpsiesItem = null;
        private JMenuItem _lookupSrtmItem = null;
        private JMenuItem _distanceItem = null;
        private JMenuItem _fullRangeDetailsItem = null;
@@ -196,6 +199,10 @@ public class MenuManager implements DataSubscriber
                _exportPovItem = makeMenuItem(FunctionLibrary.FUNCTION_POVEXPORT);
                _exportPovItem.setEnabled(false);
                fileMenu.add(_exportPovItem);
+               // Svg
+               _exportSvgItem = makeMenuItem(FunctionLibrary.FUNCTION_SVGEXPORT);
+               _exportSvgItem.setEnabled(false);
+               fileMenu.add(_exportSvgItem);
                fileMenu.addSeparator();
                JMenuItem exitMenuItem = new JMenuItem(I18nManager.getText("menu.file.exit"));
                exitMenuItem.addActionListener(new ActionListener() {
@@ -279,6 +286,10 @@ public class MenuManager implements DataSubscriber
                _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES);
                _getGpsiesItem.setEnabled(false);
                trackMenu.add(_getGpsiesItem);
+               // Upload to gpsies
+               _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES);
+               _uploadGpsiesItem.setEnabled(false);
+               trackMenu.add(_uploadGpsiesItem);
                _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM);
                _lookupSrtmItem.setEnabled(false);
                trackMenu.add(_lookupSrtmItem);
@@ -362,6 +373,9 @@ public class MenuManager implements DataSubscriber
                });
                _mergeSegmentsItem.setEnabled(false);
                rangeMenu.add(_mergeSegmentsItem);
+               _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES);
+               _deleteFieldValuesItem.setEnabled(false);
+               rangeMenu.add(_deleteFieldValuesItem);
                rangeMenu.addSeparator();
                _interpolateItem = new JMenuItem(I18nManager.getText("menu.range.interpolate"));
                _interpolateItem.addActionListener(new ActionListener() {
@@ -449,6 +463,16 @@ public class MenuManager implements DataSubscriber
                        }
                });
                viewMenu.add(_mapCheckbox);
+               // Turn off the sidebars
+               JCheckBoxMenuItem sidebarsCheckbox = new JCheckBoxMenuItem(I18nManager.getText("menu.view.showsidebars"));
+               sidebarsCheckbox.setSelected(true);
+               sidebarsCheckbox.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               _app.toggleSidebars();
+                       }
+               });
+               viewMenu.add(sidebarsCheckbox);
+               // 3d
                _show3dItem = makeMenuItem(FunctionLibrary.FUNCTION_3D);
                _show3dItem.setEnabled(false);
                viewMenu.add(_show3dItem);
@@ -771,6 +795,7 @@ public class MenuManager implements DataSubscriber
                _exportKmlItem.setEnabled(hasData);
                _exportGpxItem.setEnabled(hasData);
                _exportPovItem.setEnabled(hasData);
+               _exportSvgItem.setEnabled(hasData);
                _compressItem.setEnabled(hasData);
                _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints());
                _rearrangeMenu.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
@@ -781,6 +806,7 @@ public class MenuManager implements DataSubscriber
                _browserMapMenu.setEnabled(hasData);
                _distanceItem.setEnabled(hasData);
                _getGpsiesItem.setEnabled(hasData);
+               _uploadGpsiesItem.setEnabled(hasData && _track.hasTrackPoints());
                _lookupSrtmItem.setEnabled(hasData);
                _findWaypointItem.setEnabled(hasData && _track.hasWaypoints());
                // is undo available?
@@ -830,6 +856,7 @@ public class MenuManager implements DataSubscriber
                _addTimeOffsetItem.setEnabled(hasRange);
                _addAltitudeOffsetItem.setEnabled(hasRange);
                _convertNamesToTimesItem.setEnabled(hasRange && _track.hasWaypoints());
+               _deleteFieldValuesItem.setEnabled(hasRange);
                _fullRangeDetailsItem.setEnabled(hasRange);
                // Is the currently selected point outside the current range?
                _cutAndMoveItem.setEnabled(hasRange && hasPoint &&
index 48a5ac4967a95229f7cad5d94665022872636128..ed244d0bdbff57816492f5d23d7a6407e4151ce4 100644 (file)
@@ -16,6 +16,7 @@ import javax.swing.JList;
 import javax.swing.JPanel;
 import javax.swing.JScrollBar;
 import javax.swing.JScrollPane;
+import javax.swing.ListSelectionModel;
 import javax.swing.border.EtchedBorder;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
@@ -98,6 +99,7 @@ public class SelectorDisplay extends GenericDisplay
                _waypointListModel = new WaypointListModel(_trackInfo.getTrack());
                _waypointList = new JList(_waypointListModel);
                _waypointList.setVisibleRowCount(NUM_LIST_ENTRIES);
+               _waypointList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                _waypointList.addListSelectionListener(new ListSelectionListener() {
                        public void valueChanged(ListSelectionEvent e)
                        {
@@ -112,6 +114,7 @@ public class SelectorDisplay extends GenericDisplay
                _photoListModel = new PhotoListModel(_trackInfo.getPhotoList());
                _photoList = new JList(_photoListModel);
                _photoList.setVisibleRowCount(NUM_LIST_ENTRIES);
+               _photoList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                _photoList.addListSelectionListener(new ListSelectionListener() {
                        public void valueChanged(ListSelectionEvent e)
                        {
diff --git a/tim/prune/gui/SidebarController.java b/tim/prune/gui/SidebarController.java
new file mode 100644 (file)
index 0000000..252a359
--- /dev/null
@@ -0,0 +1,69 @@
+package tim.prune.gui;
+
+import java.awt.Component;
+
+import javax.swing.JSplitPane;
+import javax.swing.SwingUtilities;
+
+/**
+ * Class to control the showing and hiding of the sidebars
+ * (left panel, right panel and profile display)
+ */
+public class SidebarController
+{
+       /** array of hideable components */
+       private Component[] _components = null;
+       /** array of splitter panes */
+       private JSplitPane[] _splitters = null;
+       /** array of splitter positions */
+       private int[] _positions = null;
+
+
+       /**
+        * Constructor
+        * @param inComponents array of components to hide/show
+        * @param inSplitters array of splitter panes
+        */
+       public SidebarController(Component[] inComponents, JSplitPane[] inSplitters)
+       {
+               _components = inComponents;
+               _splitters = inSplitters;
+               _positions = new int[inSplitters.length];
+       }
+
+       /**
+        * Toggle full screen mode on or off
+        */
+       public void toggle()
+       {
+               if (_components != null && _components.length > 0)
+               {
+                       boolean visible = _components[0].isVisible();
+                       if (visible) {
+                               // Store divider locations
+                               for (int i=0; i<_components.length; i++) {
+                                       _positions[i] = _splitters[i].getDividerLocation();
+                               }
+                       }
+                       // Set visibility of components
+                       for (int i=0; i<_components.length; i++) {
+                               _components[i].setVisible(!visible);
+                       }
+                       // Restore divider locations
+                       for (int i=0; i<_components.length; i++) {
+                               if (!visible) {
+                                       _splitters[i].setDividerLocation(_positions[i]);
+                               }
+                       }
+                       // Hiding of panels has to occur in separate thread to update properly
+                       if (visible) SwingUtilities.invokeLater(new Runnable() {
+                               public void run() {
+                                       for (int i=0; i<_components.length; i++) {
+                                               _splitters[i].setDividerLocation(i==0?0.0:1.0);
+                                               _splitters[i].invalidate();
+                                       }
+                               }
+                       });
+               }
+       }
+}
index 2717fcb66952417dc90189a95b49e75a963bc437..97be7b5024e47124a92b940292052aa09277ec42 100644 (file)
@@ -1,11 +1,13 @@
 package tim.prune.gui.map;
 
+import java.awt.BasicStroke;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.Image;
 import java.awt.RenderingHints;
 import java.awt.event.ActionEvent;
@@ -377,7 +379,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        if ((_mapImage == null || _recalculate))
                        {
                                paintMapContents();
-                               _scaleBar.updateScale(_mapPosition.getZoom(), _mapPosition.getCentreTileY());
+                               _scaleBar.updateScale(_mapPosition.getZoom(), _mapPosition.getYFromPixels(0, 0));
                        }
                        // Draw the prepared image onto the panel
                        if (_mapImage != null) {
@@ -468,8 +470,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                }
 
                                // Make maps brighter / fainter
-                               float[] scaleFactors = {1.0f, 1.05f, 1.1f, 1.2f, 1.6f, 2.0f};
-                               float scaleFactor = scaleFactors[_transparencySlider.getValue()];
+                               final float[] scaleFactors = {1.0f, 1.05f, 1.1f, 1.2f, 1.6f, 2.2f};
+                               final float scaleFactor = scaleFactors[_transparencySlider.getValue()];
                                if (scaleFactor > 1.0f)
                                {
                                        RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
@@ -518,6 +520,10 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                final Color secondColour = Config.getColourScheme().getColour(ColourScheme.IDX_SECONDARY);
                final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT);
 
+               // try to set double line width for painting
+               if (inG instanceof Graphics2D) {
+                       ((Graphics2D) inG).setStroke(new BasicStroke(2.0f));
+               }
                int pointsPainted = 0;
                // draw track points
                inG.setColor(pointColour);
@@ -797,16 +803,25 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                         // select point if it's a left-click
                        if (!inE.isMetaDown())
                        {
-                               int 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);
+                               if (inE.getClickCount() == 1)
+                               {
+                                       // single click
+                                       int 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);
+                                       }
+                                       else {
+                                               _trackInfo.selectPoint(pointIndex);
+                                       }
                                }
-                               else {
-                                       _trackInfo.selectPoint(pointIndex);
+                               else if (inE.getClickCount() == 2) {
+                                       // double click
+                                       panMap(inE.getX() - getWidth()/2, inE.getY() - getHeight()/2);
+                                       zoomIn();
                                }
                        }
                        else
index 1daa19ebd926dc05c852290fb2e653760532c00d..fdd2949446c6b387c8c1fd7f33bedc4209ac9918 100644 (file)
@@ -16,8 +16,8 @@ public class ScaleBar extends JPanel
 {
        /** zoom level */
        private int _zoomLevel = -1;
-       /** y tile number */
-       private int _yTile = -1;
+       /** y position */
+       private double _yPos = 0.0;
 
        // Dimensions
        /** Offset from left side in pixels */
@@ -29,18 +29,14 @@ public class ScaleBar extends JPanel
        /** Margin between bar and end text in pixels */
        private static final int MARGIN_WIDTH = 8;
 
-       /** metric scales for each zoom level */
-       private static final int[] _metricScales = {10000, 5000, 2000, 2000, 1000, 500, 200, 100,
-               50, 20, 10, 5, 2, 2, 1, -2, -5, -10, -20, -50, -100, -200};
+       /** scales for each zoom level */
+       private static final int[] _scales = {10000, 5000, 2000, 2000, 1000, 500, 200, 100,
+               50, 20, 10, 5, 2, 2, 1,
+               -2, -5, -10, -20, -50, -100, -200};
        /** pixel counts for each zoom level (metric) */
-       private static final int[] _metricPixels = {64, 64, 51, 102, 102, 102, 81, 81,
-               81, 65, 65, 65, 52, 105, 105, 105, 83, 83, 83, 67, 67, 67};
-       /** imperial scales for each zoom level (num miles) */
-       private static final int[] _mileScales = {10000, 10000, 5000, 2000, 2000, 1000, 500, 200,
-               100, 50, 20, 10, 5, 2, 1, -2, -2, -5, -10, -20, -50, -100};
-       /** pixel counts for each zoom level (miles) */
-       private static final int[] _milePixels = {79, 79, 79, 64, 127, 127, 127, 102,
-               102, 102, 81, 81, 81, 65, 65, 65, 130, 104, 104, 104, 104, 83, 83};
+       private static final double[] _metricPixels = {64, 64, 51, 102, 102, 102, 81, 81,
+               81, 65, 65, 65, 52, 105, 105,
+               105, 83, 83, 83, 67, 67, 67};
 
 
        /**
@@ -64,15 +60,14 @@ public class ScaleBar extends JPanel
                {
                        try {
                                boolean useMetric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
-                               int rightSide = LEFT_OFFSET + (useMetric?_metricPixels[_zoomLevel]:_milePixels[_zoomLevel]);
-                               int scale = (useMetric?_metricScales[_zoomLevel]:_mileScales[_zoomLevel]);
+                               double drightSide = LEFT_OFFSET + _metricPixels[_zoomLevel] * (useMetric?1:1.609);
+                               int scale = _scales[_zoomLevel];
 
-                               // work out cos(latitude) from y tile and zoom, and apply to scale
-                               final double n = Math.pow(2, _zoomLevel);
-                               final double angle = Math.PI * (1 - 2.0*_yTile/n);
+                               // work out cos(latitude) from y position, and apply to scale
+                               final double angle = Math.PI * (1 - 2*_yPos);
                                final double lat = Math.atan(Math.sinh(angle));
                                final double cosLat = Math.cos(lat);
-                               rightSide = (int) (rightSide / cosLat);
+                               int rightSide = (int) (drightSide / cosLat);
                                // Adjust if scale is too large
                                while (rightSide > 300)
                                {
@@ -81,6 +76,8 @@ public class ScaleBar extends JPanel
                                        // Abort if scale is now less than 1 unit (shouldn't ever be)
                                        if (scale < 1) {return;}
                                }
+                               // Abort if scale is negative (around poles)
+                               if (rightSide < 1) {return;}
 
                                // Determine colours to use
                                Color barColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT);
@@ -105,8 +102,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 = (scale>0?(""+scale):("1/"+(-scale))) + " "
-                                       + I18nManager.getText(useMetric?"units.kilometres.short":"units.miles.short");
+                               String text = getScaleText(scale, useMetric);
                                inG.setColor(blankColour);
                                inG.drawString(text, rightSide+MARGIN_WIDTH-1, Y_OFFSET);
                                inG.drawString(text, rightSide+MARGIN_WIDTH+1, Y_OFFSET);
@@ -119,13 +115,35 @@ 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
+        * @return scale text as string
+        */
+       private static String getScaleText(int inScale, boolean inUseMetric)
+       {
+               if (inScale > 0) {
+                       // Positive scale means km or miles
+                       return "" + inScale     + " " +
+                               I18nManager.getText(inUseMetric?"units.kilometres.short":"units.miles.short");
+               }
+               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");
+       }
+
        /**
         * Update the scale level
         * @param inZoom new zoom level
+        * @param inYPos y position, where 0 is north pole, 1 is south pole
         */
-       public void updateScale(int inZoom, int inYtile)
+       public void updateScale(int inZoom, double inYPos)
        {
                _zoomLevel = inZoom;
-               _yTile = inYtile;
+               _yPos = inYPos;
        }
 }
index 90130cc19a1f0985e2655da2c55f36ac34cc7305..7f2e10f2e1ab80f01de61feba9681b6333c984b4 100644 (file)
@@ -51,6 +51,7 @@ public class AltitudeData extends ProfileData
                                        _hasData = true;
                                        _pointHasData[i] = true;
                                }
+                               else _pointHasData[i] = false;
                        }
                }
        }
index d989ebfdfb99f889ec853a3c38d040304fe02183..17ea32b2a4c004328b1ce1afcaf592b97423780c 100644 (file)
@@ -103,7 +103,7 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                        // Find minimum and maximum values to plot
                        double minValue = _data.getMinValue();
                        double maxValue = _data.getMaxValue();
-                       if (maxValue <= minValue) {maxValue = minValue + 1;}
+                       if (maxValue <= minValue) {maxValue = minValue + 1; minValue--;}
 
                        final int numPoints = _track.getNumPoints();
                        _xScaleFactor = 1.0 * (width - 2 * BORDER_WIDTH - 1) / numPoints;
@@ -153,15 +153,18 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                                        }
                                }
                                // current point (make sure it's drawn last)
-                               if (_data.hasData(selectedPoint))
+                               if (selectedPoint >= 0)
                                {
                                        x = (int) (_xScaleFactor * selectedPoint) + 1;
                                        g.setColor(secondColour);
                                        g.fillRect(BORDER_WIDTH + x, height-usableHeight-BORDER_WIDTH+1, barWidth, usableHeight-2);
-                                       g.setColor(currentColour);
-                                       value = _data.getData(selectedPoint);
-                                       y = (int) (yScaleFactor * (value - minValue));
-                                       g.fillRect(BORDER_WIDTH + x, height-BORDER_WIDTH - y, barWidth, y);
+                                       if (_data.hasData(selectedPoint))
+                                       {
+                                               g.setColor(currentColour);
+                                               value = _data.getData(selectedPoint);
+                                               y = (int) (yScaleFactor * (value - minValue));
+                                               g.fillRect(BORDER_WIDTH + x, height-BORDER_WIDTH - y, barWidth, y);
+                                       }
                                }
                        }
                        catch (NullPointerException npe) { // ignore, probably due to data being changed
index 8b036a914856d9cad42de3a041d3ef147e8e9fe5..b72c43a03b7afd3b5cbb42e4519fd9239471a111 100644 (file)
@@ -87,7 +87,7 @@ public abstract class ProfileData
        public abstract String getNoDataKey();
 
        /**
-        * Initialise the data arrays
+        * Initialise the data arrays to the correct size
         */
        protected void initArrays()
        {
index 5420ea6762fe9d0c534e2a7aa72a8f7dc4c43561..c60afb299822c623f7a4abe77a78e485e50bdc00 100644 (file)
@@ -19,12 +19,6 @@ import tim.prune.I18nManager;
  */
 public abstract class ExifGateway
 {
-       // *********************************************************
-       // TODO: Check this exif library flag before releasing!
-       /** Flag to specify internal or external library */
-       private static final boolean USE_INTERNAL_LIBRARY = true;
-       // *********************************************************
-
        /** Library object to call */
        private static ExifLibrary _exifLibrary = null;
        /** Flag to set whether failure warning has already been shown */
@@ -33,7 +27,7 @@ public abstract class ExifGateway
        /** Static block to initialise library */
        static
        {
-               String libraryClass = USE_INTERNAL_LIBRARY?"InternalExifLibrary":"ExternalExifLibrary";
+               String libraryClass = ExifLibrarySwitch.USE_INTERNAL_LIBRARY?"InternalExifLibrary":"ExternalExifLibrary";
                try
                {
                        _exifLibrary = (ExifLibrary) Class.forName("tim.prune.jpeg." + libraryClass).newInstance();
@@ -73,7 +67,7 @@ public abstract class ExifGateway
         */
        public static String getDescriptionKey()
        {
-               String key = USE_INTERNAL_LIBRARY?"internal":"external";
+               String key = ExifLibrarySwitch.USE_INTERNAL_LIBRARY?"internal":"external";
                if (_exifLibrary == null || !_exifLibrary.looksOK()) {key = key + ".failed";}
                return key;
        }
diff --git a/tim/prune/jpeg/ExifLibrarySwitch.java b/tim/prune/jpeg/ExifLibrarySwitch.java
new file mode 100644 (file)
index 0000000..01adce1
--- /dev/null
@@ -0,0 +1,10 @@
+package tim.prune.jpeg;
+
+/**
+ * Empty class just to provide a switch between internal and external libraries
+ */
+public abstract class ExifLibrarySwitch
+{
+       /** Flag to specify internal or external library */
+       static final boolean USE_INTERNAL_LIBRARY = true;
+}
index b9cd1fdadf33bb739868d98c716c8d8b4d44fc16..1c64b4e247fcd83ba4632050667a61fe09c1c5c3 100644 (file)
@@ -66,7 +66,9 @@ public class ExternalExifLibrary implements ExifLibrary
                                        data.setGpsTimestamp(new int[] {times[0].intValue(), times[1].intValue(),
                                                times[2].intValue()});
                                        Rational[] dates = gpsdir.getRationalArray(TAG_GPS_DATESTAMP);
-                                       data.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()});
+                                       if (dates != null) {
+                                               data.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()});
+                                       }
                                }
                        }
 
@@ -75,10 +77,14 @@ public class ExternalExifLibrary implements ExifLibrary
                        {
                                Directory exifdir = metadata.getDirectory(ExifDirectory.class);
 
-                               // Take time and date from exif tags if haven't got it already from GPS
-                               if (data.getGpsDatestamp() == null && exifdir.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) {
+                               // Take time and date from exif tags
+                               if (exifdir.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) {
                                        data.setOriginalTimestamp(exifdir.getString(ExifDirectory.TAG_DATETIME_ORIGINAL));
                                }
+                               // Also take "digitized" timestamp
+                               if (exifdir.containsTag(ExifDirectory.TAG_DATETIME_DIGITIZED)) {
+                                       data.setDigitizedTimestamp(exifdir.getString(ExifDirectory.TAG_DATETIME_DIGITIZED));
+                               }
 
                                // Photo rotation code
                                if (exifdir.containsTag(ExifDirectory.TAG_ORIENTATION)) {
index e1ed4c4753aa12c100d06f70925b57b7465f07d2..62e54aff2f03846412d231d951429146995f6827 100644 (file)
@@ -18,6 +18,7 @@ public class JpegData
        private int[] _gpsTimestamp = null;
        private int[] _gpsDatestamp = null;
        private String _originalTimestamp = null;
+       private String _digitizedTimestamp = null;
        private int _orientationCode = -1;
        private byte[] _thumbnail = null;
        private ArrayList<String> _errors = null;
@@ -124,6 +125,15 @@ public class JpegData
                _originalTimestamp = inStamp;
        }
 
+       /**
+        * Set the digitized timestamp
+        * @param inStamp digitized (creation) timestamp of photo
+        */
+       public void setDigitizedTimestamp(String inStamp)
+       {
+               _digitizedTimestamp = inStamp;
+       }
+
        /**
         * Set the orientation code
         * @param inCode code from exif (1 to 8)
@@ -157,6 +167,8 @@ public class JpegData
        public int getOrientationCode() { return _orientationCode; }
        /** @return original timestamp as string */
        public String getOriginalTimestamp() { return _originalTimestamp; }
+       /** @return digitized timestamp as string */
+       public String getDigitizedTimestamp() { return _digitizedTimestamp; }
 
        /**
         * Set the thumbnail
index 50a72d0caffd37c987f9897b9ec2ff2860a83994..d4df892770e395569272099de5dcaa9279aba687 100644 (file)
@@ -74,8 +74,10 @@ public class ExifReader
        public static final int TAG_GPS_TIMESTAMP = 0x0007;\r
        /** GPS date (atomic clock) GPSDateStamp 23 1d RATIONAL 3 */\r
        public static final int TAG_GPS_DATESTAMP = 0x001d;\r
-       /** Exif timestamp */\r
+       /** "Original" Exif timestamp */\r
        public static final int TAG_DATETIME_ORIGINAL = 0x9003;\r
+       /** "Creation" or "Digitized" timestamp */\r
+    public static final int TAG_DATETIME_DIGITIZED = 0x9004;\r
        /** Thumbnail offset */\r
        private static final int TAG_THUMBNAIL_OFFSET = 0x0201;\r
        /** Thumbnail length */\r
@@ -331,39 +333,53 @@ public class ExifReader
        private void processGpsTag(JpegData inMetadata, int inTagType, int inTagValueOffset,\r
                int inComponentCount, int inFormatCode)\r
        {\r
-               // Only interested in tags latref, lat, longref, lon, altref, alt and gps timestamp\r
-               switch (inTagType)\r
+               try\r
                {\r
-                       case TAG_GPS_LATITUDE_REF:\r
-                               inMetadata.setLatitudeRef(readString(inTagValueOffset, inFormatCode, inComponentCount));\r
-                               break;\r
-                       case TAG_GPS_LATITUDE:\r
-                               Rational[] latitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
-                               inMetadata.setLatitude(new double[] {latitudes[0].doubleValue(), latitudes[1].doubleValue(), latitudes[2].doubleValue()});\r
-                               break;\r
-                       case TAG_GPS_LONGITUDE_REF:\r
-                               inMetadata.setLongitudeRef(readString(inTagValueOffset, inFormatCode, inComponentCount));\r
-                               break;\r
-                       case TAG_GPS_LONGITUDE:\r
-                               Rational[] longitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
-                               inMetadata.setLongitude(new double[] {longitudes[0].doubleValue(), longitudes[1].doubleValue(), longitudes[2].doubleValue()});\r
-                               break;\r
-                       case TAG_GPS_ALTITUDE_REF:\r
-                               inMetadata.setAltitudeRef(_data[inTagValueOffset]);\r
-                               break;\r
-                       case TAG_GPS_ALTITUDE:\r
-                               inMetadata.setAltitude(readRational(inTagValueOffset, inFormatCode, inComponentCount).intValue());\r
-                               break;\r
-                       case TAG_GPS_TIMESTAMP:\r
-                               Rational[] times = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
-                               inMetadata.setGpsTimestamp(new int[] {times[0].intValue(), times[1].intValue(), times[2].intValue()});\r
-                               break;\r
-                       case TAG_GPS_DATESTAMP:\r
-                               Rational[] dates = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
-                               inMetadata.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()});\r
-                               break;\r
-                       default: // ignore all other tags\r
+                       // Only interested in tags latref, lat, longref, lon, altref, alt and gps timestamp\r
+                       switch (inTagType)\r
+                       {\r
+                               case TAG_GPS_LATITUDE_REF:\r
+                                       inMetadata.setLatitudeRef(readString(inTagValueOffset, inFormatCode, inComponentCount));\r
+                                       break;\r
+                               case TAG_GPS_LATITUDE:\r
+                                       Rational[] latitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
+                                       inMetadata.setLatitude(new double[] {latitudes[0].doubleValue(), latitudes[1].doubleValue(), latitudes[2].doubleValue()});\r
+                                       break;\r
+                               case TAG_GPS_LONGITUDE_REF:\r
+                                       inMetadata.setLongitudeRef(readString(inTagValueOffset, inFormatCode, inComponentCount));\r
+                                       break;\r
+                               case TAG_GPS_LONGITUDE:\r
+                                       Rational[] longitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
+                                       inMetadata.setLongitude(new double[] {longitudes[0].doubleValue(), longitudes[1].doubleValue(), longitudes[2].doubleValue()});\r
+                                       break;\r
+                               case TAG_GPS_ALTITUDE_REF:\r
+                                       inMetadata.setAltitudeRef(_data[inTagValueOffset]);\r
+                                       break;\r
+                               case TAG_GPS_ALTITUDE:\r
+                                       inMetadata.setAltitude(readRational(inTagValueOffset, inFormatCode, inComponentCount).intValue());\r
+                                       break;\r
+                               case TAG_GPS_TIMESTAMP:\r
+                                       Rational[] times = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
+                                       inMetadata.setGpsTimestamp(new int[] {times[0].intValue(), times[1].intValue(), times[2].intValue()});\r
+                                       break;\r
+                               case TAG_GPS_DATESTAMP:\r
+                                       Rational[] dates = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
+                                       if (dates != null) {\r
+                                               inMetadata.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()});\r
+                                       }\r
+                                       else {\r
+                                               // Not in rational array format, but maybe as String?\r
+                                               String date = readString(inTagValueOffset, inFormatCode, inComponentCount);\r
+                                               if (date != null && date.length() == 10) {\r
+                                                       inMetadata.setGpsDatestamp(new int[] {Integer.parseInt(date.substring(0, 4)),\r
+                                                               Integer.parseInt(date.substring(5, 7)), Integer.parseInt(date.substring(8))});\r
+                                               }\r
+                                       }\r
+                                       break;\r
+                               default: // ignore all other tags\r
+                       }\r
                }\r
+               catch (Exception e) {} // ignore and continue\r
        }\r
 \r
 \r
@@ -379,10 +395,12 @@ public class ExifReader
                int inComponentCount, int inFormatCode)\r
        {\r
                // Only interested in original timestamp, thumbnail offset and thumbnail length\r
-               if (inTagType == TAG_DATETIME_ORIGINAL)\r
-               {\r
+               if (inTagType == TAG_DATETIME_ORIGINAL) {\r
                        inMetadata.setOriginalTimestamp(readString(inTagValueOffset, inFormatCode, inComponentCount));\r
                }\r
+               else if (inTagType == TAG_DATETIME_DIGITIZED) {\r
+                       inMetadata.setDigitizedTimestamp(readString(inTagValueOffset, inFormatCode, inComponentCount));\r
+               }\r
                else if (inTagType == TAG_THUMBNAIL_OFFSET) {\r
                        _thumbnailOffset = TIFF_HEADER_START_OFFSET + get16Bits(inTagValueOffset);\r
                        extractThumbnail(inMetadata);\r
diff --git a/tim/prune/lang/prune-texts_cz.properties b/tim/prune/lang/prune-texts_cz.properties
new file mode 100644 (file)
index 0000000..93f4494
--- /dev/null
@@ -0,0 +1,646 @@
+# Text entries for the Prune application
+# Czech entries as extra
+
+# Menu entries
+menu.file=Soubor
+menu.file.addphotos=P\u0159idat fotografie
+menu.file.save=Ulo\u017eit jako text
+menu.file.exit=Konec
+menu.track=Trasa
+menu.track.undo=Undo
+menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo
+menu.track.deletemarked=Smazat ozna\u010den\u00e9 body
+menu.track.rearrange=P\u0159euspo\u0159\u00e1dat body
+menu.track.rearrange.start=V\u0161e na po\u010d\u00e1tek
+menu.track.rearrange.end=V\u0161e na konec
+menu.track.rearrange.nearest=Zarovnat body na trasu
+menu.range=Rozmez\u00ed
+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
+menu.range.cutandmove=P\u0159en\u00e9st v\u00fdb\u011br
+menu.point=Bod
+menu.point.editpoint=Upravit bod
+menu.point.deletepoint=Smazat bod
+menu.photo=Fotografie
+menu.photo.saveexif=Ulo\u017eit do Exif
+menu.photo.connect=P\u0159ipojit do bodu
+menu.photo.disconnect=Odpojit od bodu
+menu.photo.delete=Odebrat fotografii
+menu.view=Zobrazen\u00ed
+menu.view.showsidebars=Zobrazit panely
+menu.view.browser=Mapa v internetov\u00e9m prohl\u00ed\u017ee\u010di
+menu.view.browser.google=Mapy Google
+menu.view.browser.openstreetmap=Openstreetmap
+menu.view.browser.mapquest=Mapquest
+menu.view.browser.yahoo=Mapy Yahoo
+menu.view.browser.bing=Mapy Bing
+menu.settings=Nastaven\u00ed
+menu.settings.onlinemode=Na\u010d\u00edtat mapy z internetu
+menu.help=Pomoc
+# Popup menu for map
+menu.map.zoomin=P\u0159ibl\u00ed\u017eit
+menu.map.zoomout=Odd\u00e1lit
+menu.map.zoomfull=\u00dapln\u011b odd\u00e1lit
+menu.map.newpoint=Vytvo\u0159it nov\u00fd bod
+menu.map.connect=Propojit body
+menu.map.autopan=Automatika zorn\u00e9ho pole
+menu.map.showmap=Zobrazit mapu
+menu.map.showscalebar=Zobrazit m\u011b\u0159\u00edtko
+
+# Alt keys for menus
+altkey.menu.file=S
+altkey.menu.track=T
+altkey.menu.range=R
+altkey.menu.point=B
+altkey.menu.view=Z
+altkey.menu.photo=F
+altkey.menu.settings=N
+altkey.menu.help=P
+
+# Ctrl shortcuts for menu items
+shortcut.menu.file.open=O
+shortcut.menu.file.load=N
+shortcut.menu.file.save=U
+shortcut.menu.track.undo=U
+shortcut.menu.edit.compress=K
+shortcut.menu.range.all=V
+shortcut.menu.help.help=P
+
+# Functions
+function.open=Otev\u0159\u00edt soubor
+function.loadfromgps=Na\u010d\u00edst data z GPS
+function.sendtogps=Poslat data do GPS
+function.exportkml=Export KML
+function.exportgpx=Export GPX
+function.exportpov=Export POV
+function.exportsvg=Export SVG
+function.editwaypointname=Nastavit n\u00e1zev bodu
+function.compress=Komprimovat trasu
+function.addtimeoffset=P\u0159idat \u010dasov\u00fd posun
+function.addaltitudeoffset=P\u0159idat v\u00fd\u0161kov\u00fd posun
+function.convertnamestotimes=P\u0159ev\u00e9st n\u00e1zvy bod\u016f na \u010dasov\u00e9 zna\u010dky
+function.deletefieldvalues=Smazat hodnoty pole
+function.findwaypoint=Hledat bod
+function.pastecoordinates=Zadat sou\u0159adnice
+function.charts=Grafy
+function.show3d=Trojrozm\u011brn\u011b
+function.distances=Vzd\u00e1lenosti
+function.fullrangedetails=Detaily rozmez\u00ed
+function.setmapbg=Nastavit pozad\u00ed
+function.setkmzimagesize=Nastavit velikost exportu KMZ
+function.setpaths=Nastavit cestu k program\u016fm
+function.getgpsies=St\u00e1hnout trasy z Gpsies
+function.uploadgpsies=Nahr\u00e1t trasu na Gpsies
+function.lookupsrtm=Na\u010d\u00edst nadm. v\u00fd\u0161ku ze SRTM
+function.duplicatepoint=Zdvojit bod
+function.setcolours=Nastavit barvy
+function.setlanguage=Nastavit jazyk
+function.correlatephotos=Sladit fotografie podle \u010dasu
+function.rearrangephotos=Uspo\u0159\u00e1dat fotografie
+function.rotatephotoleft=Oto\u010dit fotografii doleva
+function.rotatephotoright=Oto\u010dit fotografii doprava
+function.ignoreexifthumb=Ignorovat n\u00e1hled v Exif
+function.help=Pomoc
+function.showkeys=Zobrazit kl\u00e1vesov\u00e9 zkratky
+function.about=O programu
+function.checkversion=Zkontrolovat existenci nov\u00e9 verze
+function.saveconfig=Ulo\u017eit nastaven\u00ed
+function.diskcache=Ulo\u017eit mapy na disk
+
+# Dialogs
+dialog.exit.confirm.title=Ukon\u010dit Prune
+dialog.exit.confirm.text=Data nejsou ulo\u017eena. Opravdu chcete ukon\u010dit program?
+dialog.openappend.title=P\u0159ipojit k na\u010dten\u00fdm dat\u016fm
+dialog.openappend.text=P\u0159ipojit tato data k ji\u017e na\u010dten\u00fdm dat\u016fm?
+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.openoptions.title=Volby p\u0159i na\u010dten\u00ed
+dialog.openoptions.filesnippet=N\u00e1hled souboru
+dialog.load.table.field=Pole
+dialog.load.table.datatype=Typ dat
+dialog.load.table.description=Popis
+dialog.delimiter.label=Odd\u011blova\u010d pol\u00ed
+dialog.delimiter.comma=\u010c\u00e1rka ,
+dialog.delimiter.tab=Tabul\u00e1tor
+dialog.delimiter.space=Mezera
+dialog.delimiter.semicolon=St\u0159edn\u00edk ;
+dialog.delimiter.other=Jin\u00e9
+dialog.openoptions.deliminfo.records=z\u00e1znam\u016f, s
+dialog.openoptions.deliminfo.fields=poli
+dialog.openoptions.deliminfo.norecords=\u017d\u00e1dn\u00e9 z\u00e1znamy
+dialog.openoptions.altitudeunits=Jednotky v\u00fd\u0161ky
+dialog.open.contentsdoubled=Tento soubor obsahuje dv\u011b kopie ka\u017ed\u00e9ho bodu,\nv\u017edy jednou jako body trasy a jednou jako v\u00fdzna\u010dn\u00e9 body.
+dialog.selecttracks.intro=Vyberte trasu nebo trasy k na\u010dten\u00ed
+dialog.selecttracks.noname=Bez n\u00e1zvu
+dialog.jpegload.subdirectories=V\u010detn\u011b podadres\u00e1\u0159\u016f
+dialog.jpegload.loadjpegswithoutcoords=V\u010detn\u011b fotografi\u00ed bez sou\u0159adnic
+dialog.jpegload.loadjpegsoutsidearea=V\u010detn\u011b fotografi\u00ed mimo aktu\u00e1ln\u00ed oblast
+dialog.jpegload.progress.title=Na\u010d\u00edt\u00e1m fotografie
+dialog.jpegload.progress=Pros\u00edm chvilku strpen\u00ed p\u0159i vyhled\u00e1v\u00e1n\u00ed fotografi\u00ed
+dialog.gpsload.nogpsbabel=Nenalezen program gpsbabel. Pokra\u010dovat?
+dialog.gpsload.device=Ozna\u010den\u00ed za\u0159\u00edzen\u00ed
+dialog.gpsload.format=Form\u00e1t
+dialog.gpsload.getwaypoints=Na\u010d\u00edst body
+dialog.gpsload.gettracks=Na\u010d\u00edst trasy
+dialog.gpsload.save=Ulo\u017eit do souboru
+dialog.gpssend.sendwaypoints=Poslat bod
+dialog.gpssend.sendtracks=Poslat trasy
+dialog.gpssend.trackname=N\u00e1zev trasy
+dialog.saveoptions.title=Ulo\u017eit soubor
+dialog.save.fieldstosave=Ulo\u017eit pole
+dialog.save.table.field=Pole
+dialog.save.table.hasdata=Nepr\u00e1zdn\u00e9
+dialog.save.table.save=Ulo\u017eit
+dialog.save.headerrow=Ulo\u017eit i z\u00e1hlav\u00ed
+dialog.save.coordinateunits=Jednotky sou\u0159adnic
+dialog.save.altitudeunits=Jednotky v\u00fd\u0161ky
+dialog.save.timestampformat=Form\u00e1t \u010dasov\u00e9 zna\u010dky
+dialog.save.overwrite.title=Soubor u\u017e existuje
+dialog.save.overwrite.text=Tento soubor u\u017e existuje. Opravdu chcete soubor p\u0159epsat?
+dialog.save.notypesselected=Nebyl vybr\u00e1n ani jeden typ bod\u016f
+dialog.exportkml.text=Nadpis dat
+dialog.exportkml.altitude=V\u00fd\u0161ka nad hladinou mo\u0159e (pro l\u00e9t\u00e1n\u00ed)
+dialog.exportkml.kmz=Komprimovat do souboru kmz
+dialog.exportkml.exportimages=Vlo\u017eit n\u00e1hledy fotografi\u00ed
+dialog.exportkml.trackcolour=Barva trasy
+dialog.exportgpx.name=N\u00e1zev
+dialog.exportgpx.desc=Popis
+dialog.exportgpx.includetimestamps=Ulo\u017eit \u010dasov\u00e9 zna\u010dky
+dialog.exportgpx.copysource=Zkop\u00edrovat zdrojov\u00e9 xml
+dialog.exportpov.text=Pros\u00edm vlo\u017ete paramerty exportu do POV
+dialog.exportpov.font=Font
+dialog.exportpov.camerax=Kamera X
+dialog.exportpov.cameray=Kamera Y
+dialog.exportpov.cameraz=Kamera Z
+dialog.exportpov.modelstyle=Model
+dialog.exportpov.ballsandsticks=Koule a ty\u010dky
+dialog.exportpov.tubesandwalls=Roury a st\u011bny
+dialog.exportpov.warningtracksize=Tato trasa sest\u00e1v\u00e1 z mnoha bod\u016f, kter\u00e9 mo\u017en\u00e1 Java3D nebude um\u011bt zobrazit.\nOpravdu chcete pokra\u010dovat?
+dialog.exportsvg.text=Zvolte parametry exportu do SVG
+dialog.exportsvg.phi=Azimut \u03d5
+dialog.exportsvg.theta=V\u00fd\u0161kov\u00fd \u00fahel \u03b8
+dialog.exportsvg.gradients=Vypl\u0148ovat body barevn\u00fdm p\u0159echodem
+dialog.pointtype.desc=Ulo\u017eit body n\u00e1sleduj\u00edc\u00edch typ\u016f:
+dialog.pointtype.track=Body trasy
+dialog.pointtype.waypoint=V\u00fdzna\u010dn\u00e9 body
+dialog.pointtype.photo=M\u00edsta s fotografiemi
+dialog.pointtype.selection=Jen v\u00fdb\u011br
+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.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
+dialog.undo.none.text=Nenalezeny \u017e\u00e1dn\u00e9 akce k vr\u00e1cen\u00ed
+dialog.clearundo.title=Vypr\u00e1zdnit pam\u011b\u0165 undo
+dialog.clearundo.text=Opravdu chcete vypr\u00e1zdnit pam\u011b\u0165 undo?\nNebude u\u017e mo\u017en\u00e9 akce vracet do p\u016fvodn\u00edho stavu!
+dialog.pointedit.title=Upravit bod
+dialog.pointedit.text=Vyberte pole k editaci a stiskn\u011bte 'Upravit'
+dialog.pointedit.table.field=Pole
+dialog.pointedit.table.value=Hodnota
+dialog.pointedit.table.changed=Zm\u011bn\u011bno
+dialog.pointedit.changevalue.text=Zadejte novou hodnotu pole
+dialog.pointedit.changevalue.title=Upravit pole
+dialog.pointnameedit.name=N\u00e1zev v\u00fdzna\u010dn\u00e9ho bodu
+dialog.pointnameedit.uppercase=VELKÁ p\u00edsmena
+dialog.pointnameedit.lowercase=mal\u00e1 p\u00edsmena
+dialog.pointnameedit.sentencecase=Po\u010d\u00e1te\u010dn\u00ed P\u00edsmena Velk\u00e1
+dialog.addtimeoffset.add=P\u0159idat \u010das
+dialog.addtimeoffset.subtract=Odebrat \u010das
+dialog.addtimeoffset.days=Dny
+dialog.addtimeoffset.hours=Hodiny
+dialog.addtimeoffset.minutes=Minuty
+dialog.addtimeoffset.notimestamps=Nelze p\u0159idat \u010das, proto\u017ee v\u00fdb\u011br neobsahuje \u017e\u00e1dn\u00e9 \u010dasov\u00e9 zna\u010dky
+dialog.findwaypoint.intro=Vlo\u017ete \u010d\u00e1st n\u00e1zvu v\u00fdzna\u010dn\u00e9ho bodu
+dialog.findwaypoint.search=Hledat
+dialog.saveexif.title=Ulo\u017eit Exif
+dialog.saveexif.intro=Vyberte fotografie k ulo\u017een\u00ed
+dialog.saveexif.nothingtosave=Hodnoty sou\u0159adnic nebyly zm\u011bn\u011bny, nen\u00ed t\u0159eba nic ukl\u00e1dat
+dialog.saveexif.noexiftool=Nebyl nalezen program exiftool. Pokra\u010dovat?
+dialog.saveexif.table.photoname=N\u00e1zev fotografie
+dialog.saveexif.table.status=Status
+dialog.saveexif.table.save=Ulo\u017eit
+dialog.saveexif.photostatus.connected=P\u0159ipojeno
+dialog.saveexif.photostatus.disconnected=Odpojeno
+dialog.saveexif.photostatus.modified=Zm\u011bn\u011bno
+dialog.saveexif.overwrite=P\u0159epsat soubory
+dialog.saveexif.force=P\u0159epsat i p\u0159i nepodstatn\u00fdch chyb\u00e1ch
+dialog.charts.xaxis=Osa X
+dialog.charts.yaxis=Osa Y
+dialog.charts.output=V\u00fdstup
+dialog.charts.screen=V\u00fdstup na obrazovku
+dialog.charts.svg=V\u00fdstup do souboru SVG
+dialog.charts.svgwidth=\u0160\u00ed\u0159ka SVG
+dialog.charts.svgheight=V\u00fd\u0161ka SVG
+dialog.charts.needaltitudeortimes=Trasa mus\u00ed obsahovat bu\u010f informaci o nadmo\u0159sk\u00e9 v\u00fd\u0161ce nebo o \u010dase, aby bylo mo\u017en\u00e9 vytv\u00e1\u0159et grafy
+dialog.charts.gnuplotnotfound=Nepoda\u0159ilo se nal\u00e9zt gnuplot na dan\u00e9m um\u00edst\u011bn\u00ed
+dialog.distances.intro=P\u0159\u00edm\u00e9 vzd\u00e1lenosti mezi body
+dialog.distances.column.from=Z bodu
+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.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
+dialog.addmapsource.layer1url=URL prvn\u00ed vrstvy
+dialog.addmapsource.layer2url=Voliteln\u011b URL druh\u00e9 vrstvy
+dialog.addmapsource.maxzoom=Maxim\u00e1ln\u00ed zv\u011bt\u0161en\u00ed
+dialog.addmapsource.cloudstyle=\u010c\u00edslo stylu
+dialog.addmapsource.noname=Bez n\u00e1zvu
+dialog.gpsies.column.name=N\u00e1zev trasy
+dialog.gpsies.column.length=D\u00e9lka
+dialog.gpsies.description=Popis
+dialog.gpsies.nodescription=Bez popisu
+dialog.gpsies.nonefound=Nenalezeny \u017e\u00e1dn\u00e9 trasy
+dialog.gpsies.username=U\u017eiv. jm\u00e9no k gpsies
+dialog.gpsies.password=Heslo k gpsies
+dialog.gpsies.keepprivate=Trasu nezve\u0159ej\u0148ovat
+dialog.gpsies.confirmopenpage=Otev\u0159\u00edt nahranou trasu v internetov\u00e9m prohl\u00ed\u017ee\u010di?
+dialog.gpsies.activities=Aktivita
+dialog.gpsies.activity.trekking=Turistika
+dialog.gpsies.activity.walking=Ch\u016fze
+dialog.gpsies.activity.jogging=B\u011bh
+dialog.gpsies.activity.biking=Kolo
+dialog.gpsies.activity.motorbiking=Motorka
+dialog.gpsies.activity.snowshoe=Sn\u011b\u017enice
+dialog.gpsies.activity.sailing=Lo\u010f
+dialog.gpsies.activity.skating=Bruslen\u00ed
+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.photoselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fdch fotografi\u00ed pro ur\u010den\u00ed \u010dasov\u00e9ho posunu
+dialog.correlate.photoselect.photoname=N\u00e1zev fotografie
+dialog.correlate.photoselect.timediff=\u010casov\u00fd rozd\u00edl
+dialog.correlate.photoselect.photolater=Vyfoceno pozd\u011bji
+dialog.correlate.options.tip=Tip: kdy\u017e ru\u010dn\u011b slad\u00edte aspo\u0148 jednu fotografii, \u010dasov\u00fd posun bude vypo\u010d\u00edtat za v\u00e1s.
+dialog.correlate.options.intro=Upravte mo\u017enosti automatick\u00e9ho slad\u011bn\u00ed
+dialog.correlate.options.offsetpanel=\u010casov\u00fd posun
+dialog.correlate.options.offset=Posun
+dialog.correlate.options.offset.hours=hodin,
+dialog.correlate.options.offset.minutes=minut a
+dialog.correlate.options.offset.seconds=sekund
+dialog.correlate.options.photolater=Fotografie pozd\u011bj\u0161\u00ed ne\u017e bod
+dialog.correlate.options.pointlater=Bod pozd\u011bj\u0161\u00ed ne\u017e fotografie
+dialog.correlate.options.limitspanel=Limity slad\u011bn\u00ed
+dialog.correlate.options.notimelimit=Bez \u010dasov\u00e9ho limitu
+dialog.correlate.options.timelimit=\u010casov\u00fd limit
+dialog.correlate.options.nodistancelimit=Bez d\u00e9lkov\u00e9ho limitu
+dialog.correlate.options.distancelimit=D\u00e9lkov\u00fd limit
+dialog.correlate.options.correlate=Sladit
+dialog.correlate.alloutsiderange=V\u0161echny fotografie le\u017e\u00ed mimo \u010dasov\u00e9 rozmez\u00ed trasy, tak\u017ee nemohou b\u00fdt slad\u011bny.\nPokuste se zm\u011bnit \u010dasov\u00fd posun nebo ru\u010dn\u011b sla\u010fte aspo\u0148 jednu fotografii.
+dialog.rearrangephotos.desc=Vyberte um\u00edst\u011bn\u00ed a uspo\u0159\u00e1d\u00e1n\u00ed bod\u016f fotografi\u00ed
+dialog.rearrangephotos.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek
+dialog.rearrangephotos.toend=P\u0159en\u00e9st na konec
+dialog.rearrangephotos.nosort=Neuspo\u0159\u00e1d\u00e1vat
+dialog.rearrangephotos.sortbyfilename=Uspo\u0159\u00e1dat dle n\u00e1zvu souboru
+dialog.rearrangephotos.sortbytime=Uspo\u0159\u00e1dat dle \u010dasu
+dialog.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
+dialog.compress.wackypoints.paramdesc=Koeficient vzd\u00e1lnosti
+dialog.compress.singletons.title=Odstran\u011bn\u00ed osamocen\u00fdch bod\u016f
+dialog.compress.singletons.paramdesc=Koeficient vzd\u00e1lenosti
+dialog.compress.duplicates.title=Odstran\u011bn\u00ed zdvojen\u00fdch bod\u016f
+dialog.compress.summarylabel=Bod\u016f ke smaz\u00e1n\u00ed
+dialog.pastecoordinates.desc=Zadejte sou\u0159adnice
+dialog.pastecoordinates.coords=Sou\u0159adnice
+dialog.pastecoordinates.nothingfound=Pros\u00edm ov\u011b\u0159te sou\u0159adnice a zkuste znovu
+dialog.help.help=V\u00edce informac\u00ed v\u010detn\u011b manu\u00e1l\u016f (bohu\u017eel nikoli v \u010de\u0161tin\u011b) naleznete na adrese:\n http://activityworkshop.net/software/prune/
+dialog.about.version=Verze
+dialog.about.build=Build
+dialog.about.summarytext1=Prune je program k na\u010d\u00edt\u00e1n\u00ed, zobrazov\u00e1n\u00ed a editaci dat z navigac\u00ed GPS.
+dialog.about.summarytext2=Je vyd\u00e1n pod licenc\u00ed GNU GPL, tak\u017ee je zdarma a voln\u011b k u\u017e\u00edv\u00e1n\u00ed a vylep\u0161ov\u00e1n\u00ed.<br>Kop\u00edrov\u00e1n\u00ed, redistribuce a \u00fapravy jsou povoleny a podporov\u00e1ny<br>podle podm\u00ednek popsan\u00fdch v souboru <code>licence.txt</code>.
+dialog.about.summarytext3=V\u00edce informac\u00ed v\u010detn\u011b manu\u00e1l\u016f (bohu\u017eel nikoli v \u010de\u0161tin\u011b) naleznete<br> na adrese: <code style="font-weight:bold">http://activityworkshop.net/</code>.
+dialog.about.languages=Dostupn\u00e9 jazyky
+dialog.about.translatedby=\u010cesk\u00fd p\u0159eklad od prot_d.
+dialog.about.systeminfo=Syst\u00e9mov\u00e9 informace
+dialog.about.systeminfo.os=Opera\u010dn\u00ed syst\u00e9m
+dialog.about.systeminfo.java=Java Runtime
+dialog.about.systeminfo.java3d=Java3d nainstalov\u00e1n
+dialog.about.systeminfo.povray=Povray nainstalov\u00e1n
+dialog.about.systeminfo.exiftool=Exiftool nainstalov\u00e1n
+dialog.about.systeminfo.gpsbabel=Gpsbabel nainstalov\u00e1n
+dialog.about.systeminfo.gnuplot=Gnuplot nainstalov\u00e1n
+dialog.about.systeminfo.exiflib=Knihovna Exif
+dialog.about.systeminfo.exiflib.internal=Intern\u00ed
+dialog.about.systeminfo.exiflib.internal.failed=Intern\u00ed (nenalezeno)
+dialog.about.systeminfo.exiflib.external=Extern\u00ed
+dialog.about.systeminfo.exiflib.external.failed=Extern\u00ed (nenalezeno)
+dialog.about.yes=Ano
+dialog.about.no=Ne
+dialog.about.credits=Auto\u0159i a spolupracovn\u00edci
+dialog.about.credits.code=Programov\u00e1n\u00ed
+dialog.about.credits.exifcode=Knihovna Exif
+dialog.about.credits.icons=N\u011bkter\u00e9 ikony od
+dialog.about.credits.translators=P\u0159ekladatel\u00e9
+dialog.about.credits.translations=P\u0159eklady s pomoc\u00ed
+dialog.about.credits.devtools=V\u00fdvojov\u00e9 n\u00e1stroje
+dialog.about.credits.othertools=Dal\u0161\u00ed n\u00e1stroje
+dialog.about.credits.thanks=D\u011bkujeme
+dialog.about.readme=Readme
+dialog.checkversion.error=\u010c\u00edslo verze se nepoda\u0159ilo zkontrolovat.\nPros\u00edm ov\u011b\u0159te p\u0159ipojen\u00ed k internetu.
+dialog.checkversion.uptodate=Pou\u017e\u00edv\u00e1te posledn\u00ed verzi Prune.
+dialog.checkversion.newversion1=Nov\u00e1 verze Prune je u\u017e dostupn\u00e1! Posledn\u00ed verze m\u00e1 \u010d\u00edslo
+dialog.checkversion.newversion2=.
+dialog.checkversion.releasedate1=Tato verze byla vyd\u00e1na
+dialog.checkversion.releasedate2=.
+dialog.checkversion.download=Novou verzi m\u016f\u017eete st\u00e1hnout na adrese http://activityworkshop.net/software/prune/download.html.
+dialog.keys.intro=M\u00edsto my\u0161i m\u016f\u017eete pou\u017e\u00edvat n\u00e1sleduj\u00edc\u00ed kl\u00e1vesov\u00e9 zkratky
+dialog.keys.keylist=<table><tr><td>\u0160ipky</td><td>Posunout mapu vlevo, vpravo, nahoru, dol\u016f</td></tr><tr><td>Ctrl + \u0161ipka vlevo, vpravo</td><td>Vybrat p\u0159edchoz\u00ed nebo n\u00e1sleduj\u00edc\u00ed bod</td></tr><tr><td>Ctrl + \u0161ipka nahoru, dol\u016f</td><td>P\u0159ibl\u00ed\u017eit, odd\u00e1lit</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Vybrat p\u0159edchoz\u00ed, n\u00e1sleduj\u00edc\u00ed \u010d\u00e1st trasy</td></tr><tr><td>Ctrl + Home, End</td><td>Vybrat prvn\u00ed, posledn\u00ed bod</td></tr><tr><td>Del</td><td>Smazat aktu\u00e1ln\u00ed bod</td></tr></table>
+dialog.keys.normalmodifier=Ctrl
+dialog.keys.macmodifier=Command
+dialog.saveconfig.desc=N\u00e1sleduj\u00edc\u00ed volby mohou b\u00fdt ulo\u017eeny do konfigura\u010dn\u00edho souboru :
+dialog.saveconfig.prune.trackdirectory=Adres\u00e1\u0159 s trasami
+dialog.saveconfig.prune.photodirectory=Ad\u0159es\u00e1\u0159 s fotografiemi
+dialog.saveconfig.prune.languagecode=K\u00f3d jazyka
+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
+dialog.saveconfig.prune.mapsource=Vybran\u00fd zdroj map
+dialog.saveconfig.prune.mapsourcelist=Zdroje map
+dialog.saveconfig.prune.diskcache=Cache s mapami
+dialog.saveconfig.prune.kmzimagewidth=\u0160\u00ed\u0159ka bitmapy KMZ
+dialog.saveconfig.prune.kmzimageheight=V\u00fd\u0161ka bitmapy KMZ
+dialog.saveconfig.prune.colourscheme=Barevn\u00e9 sch\u00e9ma
+dialog.saveconfig.prune.kmltrackcolour=Barva trasy v KML
+dialog.setpaths.intro=Je-li to t\u0159eba, m\u016f\u017eete nastavit cesty k extern\u00edm aplikac\u00edm:
+dialog.setpaths.found=Cesta nalezena?
+dialog.addaltitude.noaltitudes=Vybran\u00e9 rozmez\u00ed neobsahuje nadmo\u0159skou v\u00fd\u0161ku
+dialog.addaltitude.desc=P\u0159idat v\u00fd\u0161kov\u00fd posun
+dialog.lookupsrtm.overwritezeros=P\u0159epsat hodnoty nadmo\u0159sk\u00e9 v\u00fd\u0161ky na nulu?
+dialog.setcolours.intro=Kliknut\u00edm zvolte barvu
+dialog.setcolours.background=Pozad\u00ed
+dialog.setcolours.borders=Okraje
+dialog.setcolours.lines=\u010c\u00e1ry
+dialog.setcolours.primary=Prim\u00e1rn\u00ed
+dialog.setcolours.secondary=Sekund\u00e1rn\u00ed
+dialog.setcolours.point=Body
+dialog.setcolours.selection=V\u00fdb\u011br
+dialog.setcolours.text=Text
+dialog.colourchooser.title=Zvolte barvu
+dialog.colourchooser.red=\u010cerven\u00e1
+dialog.colourchooser.green=Zelen\u00e1
+dialog.colourchooser.blue=Modr\u00e1
+dialog.setlanguage.firstintro=M\u016f\u017eete bu\u010f zvolit jeden z vypsan\u00fdch jazyk\u016f,<p>nebo vybrat textov\u00fd soubor
+dialog.setlanguage.secondintro=Aby do\u0161lo ke zm\u011bn\u011b jazyka, je t\u0159eba ulo\u017eit nastaven\u00ed<p> a potom restartovat program Prune.
+dialog.setlanguage.language=Jazyk
+dialog.setlanguage.languagefile=Jazykov\u00fd soubor
+dialog.setlanguage.endmessage=Aby do\u0161lo ke zm\u011bn\u011b jazyka, nyn\u00ed ulo\u017ete nastaven\u00ed\na spus\u0165te Prune nanovo.
+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.deletefieldvalues.intro=Vyberte pole, kter\u00e9 se m\u00e1 z aktu\u00e1ln\u00edho rozmez\u00ed odstranit
+
+# 3d window
+dialog.3d.title=Trojrozm\u011brn\u00e9 zobrazen\u00ed Prune
+dialog.3d.altitudefactor=Faktor zd\u016frazn\u011bn\u00ed v\u00fd\u0161ky
+dialog.3dlines.title=Linky m\u0159\u00ed\u017eky Prune
+dialog.3dlines.empty=Nejsou \u017e\u00e1dn\u00e9 linky k zobrazen\u00ed!
+dialog.3dlines.intro=Toto jsou linky m\u0159\u00ed\u017eky trojrozm\u011brn\u00e9ho zobrazen\u00ed
+
+# Confirm messages
+confirm.loadfile=Data na\u010dtena ze souboru
+confirm.save.ok1=\u00dasp\u011b\u0161n\u011b ulo\u017eeno
+confirm.save.ok2=bod\u016f do souboru
+confirm.deletepoint.single=bod byl odstran\u011bn
+confirm.deletepoint.multi=body byly odstran\u011bny
+confirm.point.edit=bod zm\u011bn\u011bn
+confirm.mergetracksegments=\u010c\u00e1sti trasy spojeny
+confirm.reverserange=Rozmez\u00ed obr\u00e1ceno
+confirm.addtimeoffset=\u010casov\u00fd posun zm\u011bn\u011bn
+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.convertnamestotimes=N\u00e1zvy bod\u016f p\u0159evedeny
+confirm.saveexif.ok1=Ulo\u017eeno
+confirm.saveexif.ok2=fotografi\u00ed
+confirm.undo.single=operace vr\u00e1cena
+confirm.undo.multi=operac\u00ed vr\u00e1ceno
+confirm.jpegload.single=fotografie p\u0159id\u00e1na
+confirm.jpegload.multi=fotografie p\u0159id\u00e1ny
+confirm.photo.connect=fotografie propojena
+confirm.photo.disconnect=fotografie odpojena
+confirm.correlate.single=fotografie slad\u011bna
+confirm.correlate.multi=fotografie slad\u011bny
+confirm.createpoint=bod vytvo\u0159en
+confirm.rotatephoto=fotografie oto\u010dena
+confirm.running=Prob\u00edh\u00e1 ...
+confirm.lookupsrtm1=Nalezeno
+confirm.lookupsrtm2=v\u00fd\u0161kov\u00fdch hodnot
+confirm.deletefieldvalues=Hodnoty pole smaz\u00e1ny
+
+# Buttons
+button.ok=OK
+button.back=Zp\u011bt
+button.next=Vp\u0159ed
+button.finish=Dokon\u010dit
+button.cancel=Zru\u0161it
+button.overwrite=P\u0159epsat
+button.moveup=Posunout nahoru
+button.movedown=Posunout dol\u016f
+button.showlines=Zobrazit linky m\u0159\u00ed\u017eky
+button.edit=Editovat
+button.exit=Konec
+button.close=Zav\u0159\u00edt
+button.continue=Pokra\u010dovat
+button.yes=Ano
+button.no=Ne
+button.yestoall=Ano u v\u0161eho
+button.notoall=Ne u v\u0161eho
+button.select=Vybrat
+button.selectall=Vybrat v\u0161e
+button.selectnone=Zru\u0161it v\u00fdb\u011br
+button.preview=N\u00e1hled
+button.load=Na\u010d\u00edst
+button.upload=Upload
+button.guessfields=Odhadnout pole
+button.showwebpage=Zobrazit str\u00e1nku
+button.check=Zkontrolovat
+button.resettodefaults=Resetovat
+button.browse=Proch\u00e1zet...
+button.addnew=P\u0159idat nov\u00e9
+button.delete=Smazat
+
+# File types
+filetype.txt=soubory TXT
+filetype.jpeg=soubory JPG
+filetype.kmlkmz=soubory KML, KMZ
+filetype.kml=soubory KML
+filetype.kmz=soubory KMZ
+filetype.gpx=soubory GPX
+filetype.pov=soubory POV
+filetype.svg=soubory SVG
+
+# Display components
+display.nodata=\u017d\u00e1dn\u00e1 data
+display.noaltitudes=Trasa neobsahuje informace o v\u00fd\u0161ce
+display.notimestamps=Trasa neobsahuje \u010dasov\u00e9 zna\u010dky
+details.trackdetails=Detaily trasy
+details.notrack=\u017d\u00e1dn\u00e1 trasa
+details.track.points=Body
+details.track.file=Soubor
+details.track.numfiles=Po\u010det soubor\u016f
+details.pointdetails=Detaily bodu
+details.index.selected=Bod \u010d.
+details.index.of=z
+details.nopointselection=Bod nevybr\u00e1n
+details.photofile=Soubor s foto
+details.norangeselection=Rozmez\u00ed nevybr\u00e1no
+details.rangedetails=Detaily rozmez\u00ed
+details.range.selected=Vybr\u00e1ny body
+details.range.to=a\u017e
+details.altitude.to=a\u017e
+details.range.climb=Nastoup\u00e1no
+details.range.descent=Nakles\u00e1no
+details.coordformat=Form\u00e1t sou\u0159adnic
+details.distanceunits=Jednotky d\u00e9lky
+display.range.time.secs=s
+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
+details.range.gradient=Sp\u00e1d
+details.waypointsphotos.waypoints=V\u00fdzna\u010dn\u00e9 body
+details.waypointsphotos.photos=Fotografie
+details.photodetails=Detaily fotografie
+details.nophoto=Fotografie nevybr\u00e1na
+details.photo.loading=Na\u010d\u00edt\u00e1m
+details.photo.connected=P\u0159ipojeno
+map.overzoom=P\u0159i tomto p\u0159ibl\u00ed\u017een\u00ed mapa nen\u00ed k dispozici
+
+# Field names
+fieldname.latitude=Zem. \u0161\u00ed\u0159ka
+fieldname.longitude=Zem. d\u00e9lka
+fieldname.altitude=Nadm. v\u00fd\u0161ka
+fieldname.timestamp=\u010cas
+fieldname.time=\u010cas
+fieldname.waypointname=N\u00e1zev
+fieldname.waypointtype=Typ
+fieldname.newsegment=Segment
+fieldname.custom=Vlastn\u00ed
+fieldname.prefix=Pole
+fieldname.distance=Vzd\u00e1lenost
+fieldname.movingdistance=Najeto
+fieldname.duration=Celkov\u00fd \u010das
+fieldname.speed=Rychlost
+fieldname.verticalspeed=Vertik. rychlost
+
+# Measurement units
+units.original=P\u016fvodn\u00ed
+units.default=Implicitn\u00ed
+units.metres=Metry
+units.metres.short=m
+units.feet=Stopy
+units.feet.short=stop
+units.kilometres=Kilometry
+units.kilometres.short=km
+units.kmh=km/h
+units.miles=M\u00edle
+units.miles.short=mil
+units.mph=mph
+units.metrespersec=m/s
+units.feetpersec=stop/s
+units.hours=hodin
+units.degminsec=Deg-min-sec
+units.degmin=Deg-min
+units.deg=Stupn\u011b
+units.iso8601=ISO 8601
+
+# External urls
+url.googlemaps=maps.google.cz
+
+# Cardinals for 3d plots
+cardinal.n=N
+cardinal.s=S
+cardinal.e=E
+cardinal.w=W
+
+# Undo operations
+undo.load=na\u010d\u00edst data
+undo.loadphotos=na\u010d\u00edst fotografie
+undo.editpoint=upravit bod
+undo.deletepoint=smazat bod
+undo.deletephoto=odebrat fotografii
+undo.deleterange=smazat rozmez\u00ed
+undo.compress=zkomprimovat trasu
+undo.insert=vlo\u017eit body
+undo.reverse=obr\u00e1tit rozmez\u00ed
+undo.mergetracksegments=slou\u010dit \u010d\u00e1sti trasy
+undo.addtimeoffset=p\u0159idat \u010dasov\u00fd posun
+undo.addaltitudeoffset=p\u0159idat v\u00fd\u0161kov\u00fd posun
+undo.rearrangewaypoints=p\u0159euspo\u0159\u00e1dat body
+undo.cutandmove=p\u0159esunout v\u00fdb\u011br
+undo.connectphoto=p\u0159ipojit fotografii
+undo.disconnectphoto=odpojit fotografii
+undo.correlate=sladit fotografie
+undo.rearrangephotos=uspo\u0159\u00e1dat fotografie
+undo.createpoint=vytvo\u0159it bod
+undo.rotatephoto=oto\u010dit fotografii
+undo.convertnamestotimes=p\u0159ev\u00e9st n\u00e1zvy na \u010dasy
+undo.lookupsrtm=na\u010d\u00edst nadm. v\u00fd\u0161ky ze SRTM
+undo.deletefieldvalues=smazat hodnoty pol\u00ed
+
+# Error messages
+error.save.dialogtitle=Chyba p\u0159i ukl\u00e1d\u00e1n\u00ed
+error.save.nodata=Nen\u00ed co ulo\u017eit
+error.save.failed=Chyba p\u0159i ukl\u00e1d\u00e1n\u00ed dat do souboru
+error.saveexif.filenotfound=Soubor s fotografi\u00ed nenalezen
+error.saveexif.cannotoverwrite1=Soubor
+error.saveexif.cannotoverwrite2=je jen ke \u010dten\u00ed a nelze ho p\u0159epsat. Ulo\u017eit do kopie?
+error.saveexif.failed1=Nepoda\u0159ilo se ulo\u017eit
+error.saveexif.failed2=fotografi\u00ed
+error.saveexif.forced1=P\u0159i ukl\u00e1d\u00e1n\u00ed
+error.saveexif.forced2=fotografi\u00ed do\u0161lo k nepodstatn\u00e9 chyb\u011b
+error.load.dialogtitle=Chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed
+error.load.noread=Nelze na\u010d\u00edst soubor
+error.load.nopoints=V souboru nenalezeny \u017e\u00e1dn\u00e9 informace o sou\u0159adnic\u00edch
+error.load.unknownxml=Nezn\u00e1m\u00fd form\u00e1t XML:
+error.load.noxmlinzip=Uvnit\u0159 archivu zip nenalezen soubor XML
+error.load.othererror=Chyba p\u0159i \u010dten\u00ed souboru:
+error.jpegload.dialogtitle=Chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed fotografi\u00ed
+error.jpegload.nofilesfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory
+error.jpegload.nojpegsfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory jpeg
+error.jpegload.noexiffound=Nenalezena informace EXIF
+error.jpegload.nogpsfound=Nenalezena informace GPS
+error.jpegload.exifreadfailed=Nepoda\u0159ilo se na\u010d\u00edst informaci EXIF. Tu nelze na\u010d\u00edst\nbez intern\u00ed nebo extern\u00ed knihovny.
+error.gpsload.unknown=Nezn\u00e1m\u00e1 chyba
+error.undofailed.title=Selhalo undo
+error.undofailed.text=Nepoda\u0159ilo se vr\u00e1tit operaci
+error.function.noop.title=Operace nic neprovedla
+error.rearrange.noop=P\u0159euspo\u0159\u00e1d\u00e1n\u00ed nic neprovedlo
+error.function.notavailable.title=Funkce nen\u00ed dostupn\u00e1
+error.function.nojava3d=Tato funkce vy\u017eaduje knihovnu Java3d,\ndostupnou na Sun.com.
+error.3d=P\u0159i trojrozm\u011brn\u00e9m zobrazen\u00ed do\u0161lo k chyb\u011b
+error.readme.notfound=Nenalezen soubor readme
+error.osmimage.dialogtitle=Chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed mapov\u00fdch podklad\u016f
+error.osmimage.failed=Selhalo na\u010dten\u00ed mapov\u00fdch podklad\u016f. Pros\u00edm zkontrolujte p\u0159ipojen\u00ed k internetu.
+error.language.wrongfile=Vybran\u00fd soubor nevypad\u00e1 jako jazykov\u00fd soubor pro Prune
+error.convertnamestotimes.nonames=N\u00e1zvy nemohou b\u00fdt p\u0159evedeny na \u010dasov\u00e9 zna\u010dky
+error.lookupsrtm.nonefound=Pro tyto body nen\u00ed k dispozici informace o nadmo\u0159sk\u00e9 v\u00fd\u0161ce
+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.
index 95af0aa1d584c4e610c48eb2941cad479a1aaf72..cc950599b6d7de41e762f627283b4b5bb0751655 100644 (file)
@@ -4,22 +4,22 @@
 # Menu entries
 menu.file=Datei
 menu.file.addphotos=Fotos laden
-menu.file.save=Speichern
+menu.file.save=Als Text Speichern
 menu.file.exit=Beenden
 menu.track=Track
-menu.track.undo=Rückgängig
-menu.track.clearundo=Liste der letzten Änderungen löschen
-menu.track.deletemarked=Komprimierte Punkte löschen
+menu.track.undo=R\u00fcckg\u00e4ngig
+menu.track.clearundo=Liste der letzten Änderungen l\u00f6schen
+menu.track.deletemarked=Komprimierte Punkte l\u00f6schen
 menu.track.rearrange=Wegpunkte reorganisieren
 menu.track.rearrange.start=Alle Wegpunkte zum Anfang
 menu.track.rearrange.end=Alle Wegpunkte ans Ende
-menu.track.rearrange.nearest=Jeden Wegpunkt zum nächsten Trackpunkt verschieben
+menu.track.rearrange.nearest=Jeden Wegpunkt zum n\u00e4chsten Trackpunkt verschieben
 menu.range=Bereich
 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öschen
+menu.range.deleterange=Bereich l\u00f6schen
 menu.range.interpolate=Interpolieren
 menu.range.average=Durchschnitt berechnen
 menu.range.reverse=Bereich umkehren
@@ -27,13 +27,14 @@ menu.range.mergetracksegments=Trackabschnitte verbinden
 menu.range.cutandmove=Schneiden und verschieben
 menu.point=Punkt
 menu.point.editpoint=Punkt bearbeiten
-menu.point.deletepoint=Punkt löschen
+menu.point.deletepoint=Punkt l\u00f6schen
 menu.photo=Foto
 menu.photo.saveexif=Exif Daten speichern
-menu.photo.connect=Mit Punkt verknüpfen
+menu.photo.connect=Mit Punkt verkn\u00fcpfen
 menu.photo.disconnect=Vom Punkt trennen
 menu.photo.delete=Foto entfernen
 menu.view=Ansicht
+menu.view.showsidebars=Seitenleisten anzeigen
 menu.view.browser=Karte in Browser
 menu.settings=Einstellungen
 menu.settings.onlinemode=Karten aus dem Internet laden
@@ -41,12 +42,12 @@ menu.help=Hilfe
 # Popup menu for map
 menu.map.zoomin=Hineinzoomen
 menu.map.zoomout=Herauszoomen
-menu.map.zoomfull=Auf Bildschirmgröße zoomen
+menu.map.zoomfull=Auf Bildschirmgr\u00f6\u00dfe zoomen
 menu.map.newpoint=Neuen Punkt erzeugen
 menu.map.connect=Trackpunkte mit Linie anzeigen
 menu.map.autopan=Autozentrierung
 menu.map.showmap=Karte zeigen
-menu.map.showscalebar=Maßstab anzeigen
+menu.map.showscalebar=Ma\u00dfstab anzeigen
 
 # Alt keys for menus
 altkey.menu.file=D
@@ -68,31 +69,34 @@ shortcut.menu.range.all=A
 shortcut.menu.help.help=H
 
 # Functions
-function.open=Datei öffnen
+function.open=Datei \u00f6ffnen
 function.loadfromgps=Vom GPS laden
 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.compress=Track komprimieren
 function.addtimeoffset=Zeitverschiebung aufrechnen
-function.addaltitudeoffset=Höhenverschiebung aufrechnen
+function.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
+function.convertnamestotimes=Wegpunktenamen in Zeitstempel umwandeln
+function.deletefieldvalues=Werte eines Feldes löschen
 function.findwaypoint=Wegpunkt finden
-function.convertnamestotimes=Wegpunktenamen in Zeitstempeln verwandeln
 function.pastecoordinates=Neue Koordinaten eingeben
 function.charts=Diagramme
 function.show3d=3D Ansicht
 function.distances=Entfernungen
-function.fullrangedetails=Zusätzliche Bereichdetails
+function.fullrangedetails=Zus\u00e4tzliche Bereichdetails
 function.setmapbg=Karte Hintergrund setzen
-function.setkmzimagesize=Bildgröße im KMZ setzen
+function.setkmzimagesize=Bildgr\u00f6\u00dfe im KMZ setzen
 function.setpaths=Programmpfade setzen
-function.setcolours=Farben einstellen
-function.setlanguage=Sprache einstellen
 function.getgpsies=Gpsies Tracks holen
-function.lookupsrtm=Höhendaten von SRTM holen
+function.uploadgpsies=Daten zum Gpsies hochladen
+function.lookupsrtm=H\u00f6hendaten von SRTM holen
 function.duplicatepoint=Punkt verdoppeln
+function.setcolours=Farben einstellen
+function.setlanguage=Sprache einstellen
 function.correlatephotos=Fotos korrelieren
 function.rearrangephotos=Fotos reorganisieren
 function.rotatephotoleft=Foto nach Links drehen
@@ -100,21 +104,21 @@ function.rotatephotoright=Foto nach Rechts drehen
 function.ignoreexifthumb=Exif Vorschaubild ignorieren
 function.help=Hilfe
 function.showkeys=Tastenkombinationen anzeigen
-function.about=Über Prune
+function.about=\u00dcber Prune
 function.checkversion=Nach neuen Versionen suchen
 function.saveconfig=Einstellungen speichern
-function.diskcache=Karten auf Disk speichern
+function.diskcache=Karten auf Festplatte speichern
 
 # Dialogs
 dialog.exit.confirm.title=Prune beenden
 dialog.exit.confirm.text=Ihre Daten wurden nicht gespeichert. Wollen Sie das Programm trotzdem beenden?
-dialog.openappend.title=Daten anhängen oder ersetzen
-dialog.openappend.text=Diese Daten an die aktuellen Daten anhängen?
-dialog.deletepoint.title=Punkt löschen
-dialog.deletepoint.deletephoto=Das zu diesem Punkt gehörende Foto ebenfalls löschen?
+dialog.openappend.title=Daten anh\u00e4ngen oder ersetzen
+dialog.openappend.text=Diese Daten an die aktuellen Daten anh\u00e4ngen?
+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örenden Punkt auch löschen?
-dialog.openoptions.title=Öffnen
+dialog.deletephoto.deletepoint=Den zu diesem Foto geh\u00f6renden Punkt auch l\u00f6schen?
+dialog.openoptions.title=\u00d6ffnen
 dialog.openoptions.filesnippet=Dateiausschnitt
 dialog.load.table.field=Feld
 dialog.load.table.datatype=Datentyp
@@ -128,36 +132,38 @@ dialog.delimiter.other=Andere
 dialog.openoptions.deliminfo.records=Aufnahmen, mit
 dialog.openoptions.deliminfo.fields=Feldern
 dialog.openoptions.deliminfo.norecords=Keine Rekords
-dialog.openoptions.altitudeunits=Höhe Maßeinheiten
-dialog.open.contentsdoubled=Diese Datei enthält zwei Kopien von jedem Punkt,\neinmal als Waypoint und einmal als Trackpunkt.
+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.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 vom Track laden
+dialog.jpegload.loadjpegsoutsidearea=Auch Fotos ausserhalb des Tracks laden
 dialog.jpegload.progress.title=Fotos werden geladen
-dialog.jpegload.progress=Bitte warten während die Fotos durchgesucht werden
+dialog.jpegload.progress=Bitte warten w\u00e4hrend die Fotos durchsucht werden
 dialog.gpsload.nogpsbabel=Programm gpsbabel wurde nicht gefunden. Weiter?
-dialog.gpsload.device=Gerätename
+dialog.gpsload.device=Ger\u00e4tename
 dialog.gpsload.format=Format
 dialog.gpsload.getwaypoints=Wegpunkte laden
 dialog.gpsload.gettracks=Tracks laden
-dialog.gpsload.save=nach Datei speichern
+dialog.gpsload.save=Als Datei speichern
 dialog.gpssend.sendwaypoints=Wegpunkte senden
 dialog.gpssend.sendtracks=Tracks senden
 dialog.gpssend.trackname=Track Name
 dialog.saveoptions.title=Datei speichern
 dialog.save.fieldstosave=Zu speichernde Felder
 dialog.save.table.field=Feld
-dialog.save.table.hasdata=Enthält Daten
+dialog.save.table.hasdata=Enth\u00e4lt Daten
 dialog.save.table.save=Speichern
 dialog.save.headerrow=Titelzeile speichern
-dialog.save.coordinateunits=Koordinaten Maßeinheiten
-dialog.save.altitudeunits=Höhe Maßeinheiten
+dialog.save.coordinateunits=Koordinaten Ma\u00dfeinheiten
+dialog.save.altitudeunits=H\u00f6he Ma\u00dfeinheiten
 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 überschreiben?
-dialog.save.notypesselected=Keine Punktetypen sind ausgewählt
-dialog.exportkml.text=Titel für die Daten
-dialog.exportkml.altitude=Absolute Höheninformation (für Luftfahrt)
+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.trackcolour=Trackfarbe
@@ -165,41 +171,45 @@ dialog.exportgpx.name=Name
 dialog.exportgpx.desc=Beschreibung
 dialog.exportgpx.includetimestamps=Zeitstempel mit exportieren
 dialog.exportgpx.copysource=Xml von Quelle kopieren
-dialog.exportpov.text=Geben Sie die Parameter für 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
 dialog.exportpov.cameraz=Kamera Z
 dialog.exportpov.modelstyle=Modellstil
-dialog.exportpov.ballsandsticks=Bälle und Stangen
-dialog.exportpov.tubesandwalls=Röhren und Wände
-dialog.exportpov.warningtracksize=Dieser Track hat sehr viele Punkte, die Java3D vielleicht nicht bearbeiten kann.\nMöchten Sie den Vorgang trotzdem fortsetzen?
+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ählen Sie die Parameter für den SVG Export aus
+dialog.exportsvg.phi=Richtungswinkel \u03D5
+dialog.exportsvg.theta=Neigungswinkel \u03B8
+dialog.exportsvg.gradients=Farbverläufe verwenden
 dialog.pointtype.desc=Folgende Punkttypen speichern:
 dialog.pointtype.track=Trackpunkte
 dialog.pointtype.waypoint=Wegpunkte
 dialog.pointtype.photo=Fotopunkte
 dialog.pointtype.selection=Nur aktuellen Bereich
-dialog.confirmreversetrack.title=Umkehrung bestätigen
-dialog.confirmreversetrack.text=Diese Daten enthalten Zeitangaben, die bei einer Umkehrung in falscher Reihenfolge erscheinen würden.\nSind Sie sicher, dass Sie diesen Bereich umkehren wollen?
-dialog.confirmcutandmove.title=Verschieben bestätigen
-dialog.confirmcutandmove.text=Diese Daten enthalten Zeitangaben, die nach dem Verschieben in falscher Reihenfolge erscheinen würden.\nSind Sie sicher, dass Sie diesen Bereich verschieben wollen?
+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ählten Punkten eingefügt werden sollen
-dialog.undo.title=Aktionen Rückgängig
-dialog.undo.pretext=Bitte die Operationen, die rückgängig gemacht werden sollen, auswählen.
-dialog.undo.none.title=Undo nicht möglich
-dialog.undo.none.text=Keine Operationen können rückgängig gemacht werden.
-dialog.clearundo.title=Undo-Liste löschen
-dialog.clearundo.text=Wollen Sie wirklich die Undo-Liste löschen?\nAlle Undo- Informationen werden verloren gehen!
+dialog.interpolate.parameter.text=Anzahl Punkte, die zwischen den gew\u00e4hlten Punkten eingef\u00fcgt werden sollen
+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
+dialog.undo.none.text=Keine Operationen k\u00f6nnen r\u00fcckg\u00e4ngig gemacht werden.
+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ählen Sie die Felder aus, die Sie bearbeiten möchten, und verwenden Sie den 'Bearbeiten' Knopf, um den Wert zu ändern
+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.table.field=Feld
 dialog.pointedit.table.value=Wert
-dialog.pointedit.table.changed=Geändert
-dialog.pointedit.changevalue.text=Geben Sie den neuen Wert für dieses Feld ein
+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ß geschrieben
+dialog.pointnameedit.uppercase=GRO\u00df geschrieben
 dialog.pointnameedit.lowercase=klein geschrieben
 dialog.pointnameedit.sentencecase=Gemischt geschrieben
 dialog.addtimeoffset.add=Zeit addieren
@@ -211,8 +221,8 @@ dialog.addtimeoffset.notimestamps=Zeitdifferenz kann nicht addiert werden, weil
 dialog.findwaypoint.intro=Geben Sie einen Teil des Namens ein
 dialog.findwaypoint.search=Suche
 dialog.saveexif.title=Exif speichern
-dialog.saveexif.intro=Wählen Sie die Fotos zum Speichern aus
-dialog.saveexif.nothingtosave=Koordinaten sind unverändert. Es gibt nichts zu 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.table.status=Status
@@ -220,58 +230,71 @@ dialog.saveexif.table.save=Speichern
 dialog.saveexif.photostatus.connected=Verbunden
 dialog.saveexif.photostatus.disconnected=Getrennt
 dialog.saveexif.photostatus.modified=Modifiziert
-dialog.saveexif.overwrite=Dateien überschreiben
-dialog.saveexif.force=Forzieren trotz Warnungen
+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.output=Ausgabe
-dialog.charts.screen=Ausgabe zum Bildschirm
+dialog.charts.screen=Ausgabe auf Bildschirm
 dialog.charts.svg=Ausgabe in SVG Datei
 dialog.charts.svgwidth=SVG Breite
-dialog.charts.svgheight=SVG Höhe
-dialog.charts.needaltitudeortimes=Ohne Daten über Höhe und Zeit kann kein Diagramm erzeugt werden.
+dialog.charts.svgheight=SVG H\u00f6he
+dialog.charts.needaltitudeortimes=Ohne Daten \u00fcber 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 vom markierten Bereich
-dialog.setmapbg.intro=Eine von den Quellen auswählen, oder eine neue hinzufügen
-dialog.addmapsource.title=Neue Kartequelle hinzufügen
-dialog.addmapsource.sourcename=Sourcename
-dialog.addmapsource.layer1url=URL für erste Ebene
-dialog.addmapsource.layer2url=URL für obere Ebene (falls nötig)
+dialog.fullrangedetails.intro=Hier sind die Details des markierten Bereichs
+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
+dialog.addmapsource.layer1url=URL f\u00fcr erste Ebene
+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.length=Länge
+dialog.gpsies.column.length=L\u00e4nge
 dialog.gpsies.description=Beschreibung
 dialog.gpsies.nodescription=Keine Beschreibung
 dialog.gpsies.nonefound=Keine Tracks gefunden
-dialog.correlate.notimestamps=Die Punkte enthalten keine Zeitangaben, deshalb können die Fotos nicht zugeordnet werden.
+dialog.gpsies.username=Gpsies Username
+dialog.gpsies.password=Gpsies Passwort
+dialog.gpsies.keepprivate=Track privat halten
+dialog.gpsies.confirmopenpage=Webseite für den hochgeladenen Track öffnen?
+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.sailing=Segeln
+dialog.gpsies.activity.skating=Inline-Skating
+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.photoselect.intro=Wählen Sie eines dieser Fotos aus, um die Zeitdifferenz zu berechnen
+dialog.correlate.photoselect.intro=W\u00e4hlen Sie eines dieser Fotos aus, um die Zeitdifferenz zu berechnen
 dialog.correlate.photoselect.photoname=Bezeichnung des Fotos
 dialog.correlate.photoselect.timediff=Zeitdifferenz
-dialog.correlate.photoselect.photolater=Foto später
-dialog.correlate.options.tip=Tipp: Mit mindestens einem korrelierten Foto kann die Zeitdifferenz automatisch berechnet werden.
-dialog.correlate.options.intro=Wählen Sie die Optionen für die Korrelation aus
+dialog.correlate.photoselect.photolater=Foto sp\u00e4ter
+dialog.correlate.options.tip=Tipp: Mit mindestens einem manuell korrelierten Foto kann die Zeitdifferenz automatisch berechnet werden.
+dialog.correlate.options.intro=W\u00e4hlen Sie die Optionen f\u00fcr die Korrelation aus
 dialog.correlate.options.offsetpanel=Zeitunterschied
 dialog.correlate.options.offset=Unterschied
 dialog.correlate.options.offset.hours=Stunden,
 dialog.correlate.options.offset.minutes=Minuten und
 dialog.correlate.options.offset.seconds=Sekunden
-dialog.correlate.options.photolater=Foto später als Punkt
-dialog.correlate.options.pointlater=Punkt später als Foto
+dialog.correlate.options.photolater=Foto sp\u00e4ter als Punkt
+dialog.correlate.options.pointlater=Punkt sp\u00e4ter als Foto
 dialog.correlate.options.limitspanel=Korrelation Grenzen
 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ßerhalb vom Track Zeitraum.  Sie können nicht korreliert werden.\nVersuchen Sie es mit einem anderen Offset oder binden Sie manuell mindestens ein Foto ein.
+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.rearrangephotos.desc=Setzen Sie das Ziel und die Reihenfolge der Fotopunkte
 dialog.rearrangephotos.tostart=Am Anfang
 dialog.rearrangephotos.toend=Am Ende
@@ -279,27 +302,27 @@ 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.duplicates.title=Duplikate entfernen
 dialog.compress.closepoints.title=Nahegelegene Punkte entfernen
 dialog.compress.closepoints.paramdesc=Span Faktor
-dialog.compress.wackypoints.title=Ungewöhnliche Punkte entfernen
+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.summarylabel=Punkte zu entfernen
 dialog.pastecoordinates.desc=Geben Sie die Koordinaten ein
 dialog.pastecoordinates.coords=Koordinaten
-dialog.pastecoordinates.nothingfound=Bitte prüfen Sie die Koordinaten und versuchen nochmals
+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/prune/
 dialog.about.version=Version
 dialog.about.build=Build
-dialog.about.summarytext1=Prune ist ein Programm zum Laden, Darstellen und Editieren von Daten von GPS Geräten.
-dialog.about.summarytext2=Es wird unter der Gnu GPL zur Verfügung gestellt, zum freien, kostenlosen und offenen Gebrauch und zur Weiterentwicklung.<br>Kopieren, Weiterverbreitung und Veränderungen sind erlaubt und willkommen<br>unter den in der <code>license.txt</code> Datei enthaltenen Bedingungen.
-dialog.about.summarytext3=Auf der Seite <code style="font-weight:bold">http://activityworkshop.net/</code> finden Sie weitere Information und Benutzeranleitungen.
-dialog.about.languages=Verfügbare Sprachen
-dialog.about.translatedby=Deutsche Übersetzung von activityworkshop.
-dialog.about.systeminfo=System Information
-dialog.about.systeminfo.os=Betriebsystem
+dialog.about.summarytext1=Prune 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.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.os=Betriebssystem
 dialog.about.systeminfo.java=Java Runtime
 dialog.about.systeminfo.java3d=Java3d installiert
 dialog.about.systeminfo.povray=Povray installiert
@@ -318,26 +341,28 @@ dialog.about.credits.code=Prune 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=Übersetzungen mit Hilfe von
+dialog.about.credits.translations=\u00dcbersetzungen mit Hilfe von
 dialog.about.credits.devtools=Entwicklungsprogramme
 dialog.about.credits.othertools=Andere Programme
-dialog.about.credits.thanks=Danke an
+dialog.about.credits.thanks=Dank an
 dialog.about.readme=Liesmich
-dialog.checkversion.error=Die Versionnummer konnte nicht ermittelt werden.\nBitte prüfen Sie die Internet Verbindung.
-dialog.checkversion.uptodate=Sie haben schon die letzte Version vom Prune.
-dialog.checkversion.newversion1=Eine neue Version vom Prune ist jetzt verfügbar!  Die neue Version heißt Version
+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 Prune.
+dialog.checkversion.newversion1=Eine neue Version vom Prune ist jetzt verf\u00fcgbar! Die neue Version ist Version
 dialog.checkversion.newversion2=.
 dialog.checkversion.releasedate1=Diese neue Version ist am
-dialog.checkversion.releasedate2=veröffentlicht worden.
+dialog.checkversion.releasedate2=ver\u00f6ffentlicht worden.
 dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://activityworkshop.net/software/prune/download.html.
-dialog.keys.intro=Anstatt die Maus können Sie diesen Taste-Kombinationen 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ächsten Punkt markieren</td></tr><tr><td>Strg + auf, abwärts Pfeil</td><td>Ein- oder Auszoomen</td></tr><tr><td>Strg + Bild auf, ab</td><td>Vorherigen oder nächsten 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.saveconfig.desc=Die folgende Einstellungen können gespeichert werden :
+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.normalmodifier=Strg
+dialog.keys.macmodifier=Kommando
+dialog.saveconfig.desc=Die folgende Einstellungen k\u00f6nnen gespeichert werden:
 dialog.saveconfig.prune.trackdirectory=Datenverzeichnis
 dialog.saveconfig.prune.photodirectory=Fotoverzeichnis
-dialog.saveconfig.prune.languagecode=Sprachecode (DE)
-dialog.saveconfig.prune.languagefile=Sprachedatei
-dialog.saveconfig.prune.gpsdevice=GPS Gerätename
+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?
@@ -345,48 +370,51 @@ 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=Kartenservers
+dialog.saveconfig.prune.mapsourcelist=Kartenserver
 dialog.saveconfig.prune.diskcache=Kartenordner
 dialog.saveconfig.prune.kmzimagewidth=Bildbreite in KMZ
-dialog.saveconfig.prune.kmzimageheight=Bildhöhe in KMZ
-dialog.saveconfig.prune.colourscheme=Farbenschema
+dialog.saveconfig.prune.kmzimageheight=Bildh\u00f6he in KMZ
+dialog.saveconfig.prune.colourscheme=Farbschema
 dialog.saveconfig.prune.kmltrackcolour=KML Trackfarbe
-dialog.setpaths.intro=Sie können hier die Pfade für externe Applikationen setzen:
+dialog.setpaths.intro=Sie k\u00f6nnen hier die Pfade f\u00fcr externe Applikationen setzen:
 dialog.setpaths.found=Pfad gefunden?
-dialog.addaltitude.noaltitudes=Der markierten Bereich enthält keine Höhenangaben
-dialog.addaltitude.desc=Höhenverschiebung aufzurechnen
-dialog.setcolours.intro=Klicken Sie auf einer Farbe um sie zu ändern
+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.background=Hintergrund
 dialog.setcolours.borders=Umrandungen
 dialog.setcolours.lines=Linien
-dialog.setcolours.primary=Primär
-dialog.setcolours.secondary=Secondär
+dialog.setcolours.primary=Prim\u00e4r
+dialog.setcolours.secondary=Second\u00e4r
 dialog.setcolours.point=Punkte
 dialog.setcolours.selection=Bereich
 dialog.setcolours.text=Texte
-dialog.colourchooser.title=Farbe auswählen
+dialog.colourchooser.title=Farbe ausw\u00e4hlen
 dialog.colourchooser.red=Rot
-dialog.colourchooser.green=Grün
+dialog.colourchooser.green=Gr\u00fcn
 dialog.colourchooser.blue=Blau
-dialog.setlanguage.firstintro=Sie können entweder eine von den eingebauten Sprachen<p>oder eine Text-Datei auswählen.
-dialog.setlanguage.secondintro=Sie müssen Ihre Einstellungen speichern und dann<p>Prune wieder neustarten um die Sprache zu ändern.
+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>Prune neu starten um die Sprache zu \u00e4ndern.
 dialog.setlanguage.language=Sprache
-dialog.setlanguage.languagefile=Sprache Datei
-dialog.setlanguage.endmessage=Nun speichern Sie Ihre Einstellungen und starten Sie Prune neu\num die neue Sprache zu verwenden.
-dialog.diskcache.save=Karten zum Disk speichern
+dialog.setlanguage.languagefile=Sprachdatei
+dialog.setlanguage.endmessage=Speichern Sie nun Ihre Einstellungen und starten Sie Prune neu\num die neue Sprache zu verwenden.
+dialog.diskcache.save=Karten auf Festplatte speichern
 dialog.diskcache.dir=Kartenordner
-dialog.diskcache.createdir=Ordner kreieren
-dialog.diskcache.nocreate=Ordner wurde nicht kreiert
+dialog.diskcache.createdir=Ordner anlegen
+dialog.diskcache.nocreate=Ordner wurde nicht angelegt
+dialog.deletefieldvalues.intro=W\u00e4hlen Sie das Feld aus, die Sie l\u00f6schen m\u00f6chten
 
 # 3d window
 dialog.3d.title=Prune 3D Ansicht
-dialog.3d.altitudecap=Minimum Höhenskala
+dialog.3d.altitudecap=Minimum H\u00f6henskala
+dialog.3d.altitudefactor=Vervielfachungsfaktor für Höhen
 dialog.3dlines.title=Prune Gitterlinien
 dialog.3dlines.empty=Keine Linien zum Anzeigen!
-dialog.3dlines.intro=Hier sind die Linien für die 3D Ansicht
+dialog.3dlines.intro=Hier sind die Linien f\u00fcr die 3D Ansicht
 
 # Confirm messages || These are displayed as confirmation in the status bar
-confirm.loadfile=Daten geladen vom
+confirm.loadfile=Daten aus Datei geladen
 confirm.save.ok1=Es wurden
 confirm.save.ok2=Punkte gespeichert nach
 confirm.deletepoint.single=Punkt wurde entfernt
@@ -395,56 +423,58 @@ confirm.point.edit=Punkt editiert
 confirm.mergetracksegments=Trackabschnitte verbunden
 confirm.reverserange=Bereich umgekehrt
 confirm.addtimeoffset=Zeitverschiebung aufgerechnet
-confirm.addaltitudeoffset=Höhenverschiebung aufgerechnet
+confirm.addaltitudeoffset=H\u00f6henverschiebung aufgerechnet
 confirm.rearrangewaypoints=Wegpunkte reorganisiert
 confirm.rearrangephotos=Fotos reorganisiert
 confirm.cutandmove=Bereich verschoben
-confirm.convertnamestotimes=Wegpunktnamen verwandelt
+confirm.convertnamestotimes=Wegpunktnamen umgewandelt
 confirm.saveexif.ok1=Es wurden
 confirm.saveexif.ok2=Fotodateien geschrieben
-confirm.undo.single=Operation rückgängig gemacht
-confirm.undo.multi=Operationen rückgängig gemacht
+confirm.undo.single=Operation r\u00fcckg\u00e4ngig gemacht
+confirm.undo.multi=Operationen r\u00fcckg\u00e4ngig gemacht
 confirm.jpegload.single=Foto wurde geladen
 confirm.jpegload.multi=Fotos wurden geladen
 confirm.photo.connect=Foto verbunden
 confirm.photo.disconnect=Foto getrennt
 confirm.correlate.single=Foto wurde korreliert
 confirm.correlate.multi=Fotos wurden korreliert
-confirm.createpoint=Punkt kreiert
-confirm.rotatephoto=Foto umgedreht
+confirm.createpoint=Punkt erzeugt
+confirm.rotatephoto=Foto gedreht
 confirm.running=In Bearbeitung ...
-confirm.lookupsrtm1=Es wurde
-confirm.lookupsrtm2=Höhenwerte gefunden
+confirm.lookupsrtm1=Es wurden
+confirm.lookupsrtm2=H\u00f6henwerte gefunden
+confirm.deletefieldvalues=Feldwerte gelöscht
 
-# Buttons
+# Buttons || These are all the texts for buttons
 button.ok=OK
-button.back=Zurück
-button.next=Vorwärts
+button.back=Zur\u00fcck
+button.next=Vorw\u00e4rts
 button.finish=Fertig
 button.cancel=Abbrechen
-button.overwrite=Überschreiben
+button.overwrite=\u00dcberschreiben
 button.moveup=Nach oben verschieben
 button.movedown=Nach unten verschieben
 button.showlines=Linien anzeigen
 button.edit=Bearbeiten
 button.exit=Beenden
-button.close=Schließen
+button.close=Schlie\u00dfen
 button.continue=Fortsetzen
 button.yes=Ja
 button.no=Nein
-button.yestoall=Ja für alle
-button.notoall=Nein für alle
-button.select=Auswählen
-button.selectall=Alle auswählen
-button.selectnone=Nichts auswählen
+button.yestoall=Ja f\u00fcr alle
+button.notoall=Nein f\u00fcr alle
+button.select=Ausw\u00e4hlen
+button.selectall=Alle ausw\u00e4hlen
+button.selectnone=Nichts ausw\u00e4hlen
 button.preview=Vorschau
 button.load=Laden
+button.upload=Hochladen
 button.guessfields=Felder erraten
 button.showwebpage=Webseite anzeigen
-button.check=Prüfen
-button.resettodefaults=Zurücksetzen
+button.check=Pr\u00fcfen
+button.resettodefaults=Zur\u00fccksetzen
 button.browse=Durchsuchen...
-button.addnew=Hinzufügen
+button.addnew=Hinzuf\u00fcgen
 button.delete=Entfernen
 
 # File types
@@ -459,19 +489,19 @@ filetype.svg=SVG Dateien
 
 # Display components
 display.nodata=Keine Daten geladen
-display.noaltitudes=Track enthält keine Höhenangaben
-display.notimestamps=Track enthält keine Zeitstempeln
-details.trackdetails=Details vom Track
+display.noaltitudes=Track enth\u00e4lt keine H\u00f6henangaben
+display.notimestamps=Track enth\u00e4lt keine Zeitstempel
+details.trackdetails=Details des Tracks
 details.notrack=Kein Track geladen
 details.track.points=Punkte
 details.track.file=Datei
 details.track.numfiles=Anzahl Dateien
-details.pointdetails=Details vom Punkt
+details.pointdetails=Details des Punkts
 details.index.selected=Index
 details.index.of=von
 details.nopointselection=Nichts selektiert
 details.photofile=Fotodatei
-details.norangeselection=Kein Bereich ausgewählt
+details.norangeselection=Kein Bereich ausgew\u00e4hlt
 details.rangedetails=Details der Auswahl
 details.range.selected=Markiert
 details.range.to=bis
@@ -479,29 +509,29 @@ details.altitude.to=bis
 details.range.climb=Aufstieg
 details.range.descent=Abstieg
 details.coordformat=Koordinatenformat
-details.distanceunits=Distanz Maßeinheiten
+details.distanceunits=Distanz Ma\u00dfeinheiten
 display.range.time.secs=s
 display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=T
-details.range.avespeed=Geschwindigkeit
-details.range.avemovingspeed=Geschwindigkeit unterwegs
-details.range.maxspeed=Höchstgeschwindigkeit
+details.range.avespeed=Durchschnittsgeschwindigkeit
+details.range.avemovingspeed=Durchschnittsgeschwindigkeit unterwegs
+details.range.maxspeed=H\u00f6chstgeschwindigkeit
 details.range.numsegments=Anzahl Abschnitte
 details.range.pace=Tempo
-details.range.gradient=Gefälle
+details.range.gradient=Gef\u00e4lle
 details.waypointsphotos.waypoints=Wegpunkte
 details.waypointsphotos.photos=Fotos
 details.photodetails=Fotodetails
-details.nophoto=Kein Foto ausgewählt
+details.nophoto=Kein Foto ausgew\u00e4hlt
 details.photo.loading=Laden
 details.photo.connected=Verbunden
-map.overzoom=Keine Karten für diesen Zoomfaktor verfügbar
+map.overzoom=Keine Karten f\u00fcr diesen Zoomfaktor verf\u00fcgbar
 
 # Field names
 fieldname.latitude=Breitengrad
-fieldname.longitude=Längengrad
-fieldname.altitude=Höhe
+fieldname.longitude=L\u00e4ngengrad
+fieldname.altitude=H\u00f6he
 fieldname.timestamp=Zeitstempel
 fieldname.time=Zeit
 fieldname.waypointname=Name
@@ -509,7 +539,7 @@ fieldname.waypointtype=Typ
 fieldname.newsegment=Segment
 fieldname.custom=Custom
 fieldname.prefix=Feld
-fieldname.distance=Länge
+fieldname.distance=L\u00e4nge
 fieldname.movingdistance=Wegstrecke
 fieldname.duration=Zeitdauer
 fieldname.speed=Geschwindigkeit
@@ -543,60 +573,64 @@ cardinal.w=W
 undo.load=Daten laden
 undo.loadphotos=Fotos laden
 undo.editpoint=Punkt bearbeiten
-undo.deletepoint=Punkt löschen
+undo.deletepoint=Punkt l\u00f6schen
 undo.deletephoto=Foto entfernen
-undo.deleterange=Bereich löschen
+undo.deleterange=Bereich l\u00f6schen
 undo.compress=Track komprimieren
-undo.insert=Punkte hinzufügen
+undo.insert=Punkte hinzuf\u00fcgen
 undo.reverse=Bereich umdrehen
 undo.mergetracksegments=Trackabschnitte verbinden
 undo.addtimeoffset=Zeitverschiebung aufrechnen
-undo.addaltitudeoffset=Höhenverschiebung aufrechnen
+undo.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
 undo.rearrangewaypoints=Wegpunkte reorganisieren
 undo.cutandmove=Bereich verschieben
 undo.connectphoto=Foto verbinden
 undo.disconnectphoto=Foto trennen
 undo.correlate=Fotos korrelieren
 undo.rearrangephotos=Fotos reorganisieren
-undo.rotatephoto=Foto umdrehen
 undo.createpoint=Punkt erzeugen
-undo.convertnamestotimes=Namen in Zeitstempeln verwandeln
-undo.lookupsrtm=Höhendaten von SRTM holen
+undo.rotatephoto=Foto umdrehen
+undo.convertnamestotimes=Namen in Zeitstempel umwandeln
+undo.lookupsrtm=H\u00f6hendaten von SRTM holen
+undo.deletefieldvalues=Feldwerte löschen
 
 # Error messages
 error.save.dialogtitle=Fehler beim Speichern
-error.save.nodata=Keine Daten zum Sichern vorhanden
+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.cannotoverwrite2=ist schreib-geschützt. Als Kopie speichern?
+error.saveexif.cannotoverwrite2=ist schreibgesch\u00fctzt. Als Kopie speichern?
 error.saveexif.failed1=
-error.saveexif.failed2=von den Bildern konnten nicht gespeichert werden
-error.saveexif.forced1=
-error.saveexif.forced2=von den Bildern mussten forziert werden
+error.saveexif.failed2=Bilder konnten nicht gespeichert werden
+error.saveexif.forced1=Bei
+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ültigen Daten in Datei gefunden
+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.othererror=Fehler beim Lesen von der Datei:
+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.noexiffound=Keine EXIF Information gefunden
 error.jpegload.nogpsfound=Keine GPS Information gefunden
-error.jpegload.exifreadfailed=EXIF Aufruf fehlgeschlagen. Keine EXIF Information können gelesen werden\nohne einen internen oder externen Bibliothek.
-error.gpsload.unknown=Unbekanntes Fehler
+error.jpegload.exifreadfailed=EXIF Aufruf fehlgeschlagen. Keine EXIF Information k\u00f6nnen gelesen werden\nohne einen internen oder externen Bibliothek.
+error.gpsload.unknown=Unbekannter Fehler
 error.undofailed.title=Undo fehlgeschlagen
-error.undofailed.text=Operation konnte nicht rückgängig gemacht werden
+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.function.notavailable.title=Funktion nicht verfügbar
-error.function.nojava3d=Diese Funktion benötigt die Java3d Library,\nvon Sun.com erhältlich.
+error.function.notavailable.title=Funktion nicht verf\u00fcgbar
+error.function.nojava3d=Diese Funktion ben\u00f6tigt die Java3d Library,\nvon Sun.com erh\u00e4ltlich.
 error.3d=Ein Fehler ist bei der 3D Darstellung aufgetreten
 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üfen Sie die Internetverbindung.
-error.language.wrongfile=Die ausgewählte Datei scheint keine Sprache-Datei für Prune zu sein
-error.convertnamestotimes.nonames=Keine Namen konnten verwandelt werden
-error.lookupsrtm.none=Keine Höhendaten gefunden
+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 Prune zu sein
+error.convertnamestotimes.nonames=Es konnten keine Namen umgewandelt werden
+error.lookupsrtm.nonefound=Keine H\u00f6hendaten verfügbar für diese Punkte
+error.lookupsrtm.nonerequired=Alle Punkte haben schon Höhendaten
+error.gpsies.uploadnotok=Der Gpsies Server hat geantwortet
+error.gpsies.uploadfailed=Das Hochladen ist fehlgeschlagen
index faf7db30b0f8b12f044a6fc1dd00be49a2ea828d..21a47c4d00a450c7f9c864503399ec9ab408535a 100644 (file)
@@ -4,7 +4,7 @@
 # Menu entries
 menu.file=File
 menu.file.addphotos=Fötelis innätue
-menu.file.save=Speichere
+menu.file.save=Als Text Speichere
 menu.file.exit=Beände
 menu.track=Track
 menu.track.undo=Undo
@@ -34,6 +34,7 @@ menu.photo.connect=Mitem Punkt verbind
 menu.photo.disconnect=Vonem Punkt trännä
 menu.photo.delete=Föteli entfernä
 menu.view=Aasicht
+menu.view.showsidebars=Seiteleischten aazeige
 menu.view.browser=Karte inem Browser
 menu.settings=Iistellige
 menu.settings.onlinemode=Karte uusem Internet lade
@@ -74,12 +75,14 @@ function.sendtogps=zum GPS schicke
 function.exportkml=KML exportierä
 function.exportgpx=GPX exportierä
 function.exportpov=POV exportierä
+function.exportsvg=SVG exportierä
 function.editwaypointname=Waypoint Name editiere
 function.compress=Track komprimierä
 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.pastecoordinates=Noii Koordinaten iigebe
 function.charts=Diagramme
 function.show3d=Drüü-D Aasicht
@@ -87,6 +90,7 @@ function.distances=Distanze
 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.duplicatepoint=Punkt verdopplä
 function.correlatephotos=Fötelis korrelierä
@@ -130,6 +134,8 @@ dialog.openoptions.deliminfo.fields=F
 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.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
@@ -174,6 +180,10 @@ 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.exportsvg.phi=Richtigswinkel \u03D5
+dialog.exportsvg.theta=Neigigswinkel \u03B8
+dialog.exportsvg.gradients=Farbeverläufe verwände
 dialog.pointtype.desc=Folgende Punkttype speichere:
 dialog.pointtype.track=Trackpunkte
 dialog.pointtype.waypoint=Waypoints
@@ -250,6 +260,19 @@ dialog.gpsies.column.length=L
 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.activities=Aktivit\u00e4ten
+dialog.gpsies.activity.trekking=Wandere
+dialog.gpsies.activity.walking=Z'Fuess gah
+dialog.gpsies.activity.jogging=Seggle
+dialog.gpsies.activity.biking=Velotour
+dialog.gpsies.activity.motorbiking=Motorrad
+dialog.gpsies.activity.snowshoe=Schneeschuh
+dialog.gpsies.activity.sailing=Segle
+dialog.gpsies.activity.skating=Inline-Skate
 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ä
@@ -332,6 +355,8 @@ dialog.checkversion.releasedate2=ussecho.
 dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://activityworkshop.net/software/prune/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, Ende</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt lösche</td></tr></table>
+dialog.keys.normalmodifier=Strg
+dialog.keys.macmodifier=Kommando
 dialog.saveconfig.desc=Die folgendi Iinstellige könne gspeicheret werde :
 dialog.saveconfig.prune.trackdirectory=Trackverzeichnis
 dialog.saveconfig.prune.photodirectory=Föteliverzeichnis
@@ -355,6 +380,7 @@ dialog.setpaths.intro=Sie k
 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.setcolours.background=Hintergrund
 dialog.setcolours.borders=Rande
@@ -377,15 +403,17 @@ 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.deletefieldvalues.intro=Wählet Sie s Fäld uus zum lösche
 
 # 3d window
 dialog.3d.title=Prune Drüü-d Aasicht
 dialog.3d.altitudecap=Minimum Höhenskala
+dialog.3d.altitudefactor=Höchivervilfachigsfaktor
 dialog.3dlines.title=Prune Gitterlinie
 dialog.3dlines.empty=Kei Linie zum aazeigä!
 dialog.3dlines.intro=Hier sin die Linie für die drüü-D Aasicht
 
-# Confirm messages || These are displayed as confirmation in the status bar
+# Confirm messages
 confirm.loadfile=Date glade vom
 confirm.save.ok1=Es sin
 confirm.save.ok2=Punkte gspeicheret worde na
@@ -415,6 +443,7 @@ confirm.rotatephoto=F
 confirm.running=Am Laufe ...
 confirm.lookupsrtm1=Es sin
 confirm.lookupsrtm2=Höhenwerte gfunde
+confirm.deletefieldvalues=Feldwärte glöscht
 
 # Buttons
 button.ok=OK
@@ -439,6 +468,7 @@ button.selectall=Alli uusw
 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
@@ -561,7 +591,8 @@ undo.rearrangephotos=F
 undo.createpoint=Punkt kreierä
 undo.rotatephoto=Föteli umadräya
 undo.convertnamestotimes=Name ins Ziitstämple verwondlä
-undo.lookupsrtm=Höhendate vonem SRTM hole
+undo.lookupsrtm=Höhendate vonem SRTM holä
+undo.deletefieldvalues=Feldwärte löschä
 
 # Error messages
 error.save.dialogtitle=Fähle bim Speichere
@@ -599,4 +630,7 @@ error.osmimage.dialogtitle=F
 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 Prune z'sii
 error.convertnamestotimes.nonames=Kei Namen han könnet verwondlet werde
-error.lookupsrtm.none=Kei Höhendate gfunde
+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.gpsies.uploadfailed=S Uufalade isch fehlgschlage
index d92a98a674282cf52415597a79dcfad224b4c0de..42b1d5ee6a2b5179064df4b55ec3cc4ea8283ce4 100644 (file)
@@ -4,7 +4,7 @@
 # Menu entries
 menu.file=File
 menu.file.addphotos=Add photos
-menu.file.save=Save
+menu.file.save=Save as text
 menu.file.exit=Exit
 menu.track=Track
 menu.track.undo=Undo
@@ -34,7 +34,8 @@ menu.photo.connect=Connect to point
 menu.photo.disconnect=Disconnect from point
 menu.photo.delete=Remove photo
 menu.view=View
-menu.view.browser=Map in browser
+menu.view.showsidebars=Show sidebars
+menu.view.browser=Map in a browser window
 menu.view.browser.google=Google maps
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
@@ -79,18 +80,21 @@ function.sendtogps=Send data to GPS
 function.exportkml=Export KML
 function.exportgpx=Export GPX
 function.exportpov=Export POV
+function.exportsvg=Export SVG
 function.editwaypointname=Edit waypoint name
 function.compress=Compress track
 function.addtimeoffset=Add time offset
 function.addaltitudeoffset=Add altitude offset
 function.findwaypoint=Find waypoint
 function.convertnamestotimes=Convert waypoint names to times
+function.deletefieldvalues=Delete field values
 function.pastecoordinates=Enter new coordinates
 function.charts=Charts
 function.show3d=Three-D view
 function.distances=Distances
 function.fullrangedetails=Full range details
 function.getgpsies=Get Gpsies tracks
+function.uploadgpsies=Upload track to Gpsies
 function.lookupsrtm=Get altitudes from SRTM
 function.duplicatepoint=Duplicate point
 function.correlatephotos=Correlate photos
@@ -135,6 +139,8 @@ dialog.openoptions.deliminfo.fields=fields
 dialog.openoptions.deliminfo.norecords=No records
 dialog.openoptions.altitudeunits=Altitude units
 dialog.open.contentsdoubled=This file contains two copies of each point,\nonce as waypoints and once as track points.
+dialog.selecttracks.intro=Select the track or tracks to load
+dialog.selecttracks.noname=Unnamed
 dialog.jpegload.subdirectories=Include subdirectories
 dialog.jpegload.loadjpegswithoutcoords=Include photos without coordinates
 dialog.jpegload.loadjpegsoutsidearea=Include photos outside current area
@@ -179,6 +185,10 @@ dialog.exportpov.modelstyle=Model style
 dialog.exportpov.ballsandsticks=Balls and sticks
 dialog.exportpov.tubesandwalls=Tubes and walls
 dialog.exportpov.warningtracksize=This track has a large number of points, which Java3D might not be able to display.\nAre you sure you want to continue?
+dialog.exportsvg.text=Select the parameters for the SVG export
+dialog.exportsvg.phi=Azimuth angle \u03D5
+dialog.exportsvg.theta=Elevation angle \u03B8
+dialog.exportsvg.gradients=Use gradients for shading
 dialog.pointtype.desc=Save the following point types:
 dialog.pointtype.track=Track points
 dialog.pointtype.waypoint=Waypoints
@@ -255,6 +265,19 @@ dialog.gpsies.column.length=Length
 dialog.gpsies.description=Description
 dialog.gpsies.nodescription=No description
 dialog.gpsies.nonefound=No tracks found
+dialog.gpsies.username=Gpsies username
+dialog.gpsies.password=Gpsies password
+dialog.gpsies.keepprivate=Keep track private
+dialog.gpsies.confirmopenpage=Open the web page for the uploaded track?
+dialog.gpsies.activities=Activity types
+dialog.gpsies.activity.trekking=Hiking
+dialog.gpsies.activity.walking=Walking
+dialog.gpsies.activity.jogging=Running
+dialog.gpsies.activity.biking=Cycling
+dialog.gpsies.activity.motorbiking=Motorbiking
+dialog.gpsies.activity.snowshoe=Snowshoeing
+dialog.gpsies.activity.sailing=Sailing
+dialog.gpsies.activity.skating=Skating
 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.photoselect.intro=Select one of these correlated photos to use as the time offset
@@ -337,6 +360,8 @@ dialog.checkversion.releasedate2=.
 dialog.checkversion.download=To download the new version, go to http://activityworkshop.net/software/prune/download.html.
 dialog.keys.intro=You can use the following shortcut keys instead of using the mouse
 dialog.keys.keylist=<table><tr><td>Arrow keys</td><td>Pan map left right, up, down</td></tr><tr><td>Ctrl + left, right arrow</td><td>Select previous or next point</td></tr><tr><td>Ctrl + up, down arrow</td><td>Zoom in or out</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Select previous, next segment</td></tr><tr><td>Ctrl + Home, End</td><td>Select first, last point</td></tr><tr><td>Del</td><td>Delete current point</td></tr></table>
+dialog.keys.normalmodifier=Ctrl
+dialog.keys.macmodifier=Command
 dialog.saveconfig.desc=The following settings can be saved to a configuration file :
 dialog.saveconfig.prune.trackdirectory=Track directory
 dialog.saveconfig.prune.photodirectory=Photo directory
@@ -360,6 +385,7 @@ dialog.setpaths.intro=If you need to, you can choose the paths to the external a
 dialog.setpaths.found=Path found?
 dialog.addaltitude.noaltitudes=The selected range does not contain altitudes
 dialog.addaltitude.desc=Altitude offset to add
+dialog.lookupsrtm.overwritezeros=Overwrite altitude values of zero?
 dialog.setcolours.intro=Click on a colour patch to change the colour
 dialog.setcolours.background=Background
 dialog.setcolours.borders=Borders
@@ -382,10 +408,12 @@ 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.deletefieldvalues.intro=Select the field to delete for the current range
 
 # 3d window
 dialog.3d.title=Prune Three-d view
-dialog.3d.altitudecap=Minimum altitude range
+dialog.3d.altitudecap=
+dialog.3d.altitudefactor=Altitude exaggeration factor
 dialog.3dlines.title=Prune gridlines
 dialog.3dlines.empty=No gridlines to display!
 dialog.3dlines.intro=These are the gridlines for the three-d view
@@ -420,6 +448,7 @@ confirm.createpoint=point created
 confirm.running=Running ...
 confirm.lookupsrtm1=Found
 confirm.lookupsrtm2=altitude values
+confirm.deletefieldvalues=Field values deleted
 
 # Buttons
 button.ok=OK
@@ -444,6 +473,7 @@ button.selectall=Select all
 button.selectnone=Select none
 button.preview=Preview
 button.load=Load
+button.upload=Upload
 button.guessfields=Guess fields
 button.showwebpage=Show webpage
 button.check=Check
@@ -573,6 +603,7 @@ undo.rotatephoto=rotate photo
 undo.createpoint=create point
 undo.convertnamestotimes=convert names to times
 undo.lookupsrtm=lookup altitudes from SRTM
+undo.deletefieldvalues=delete field values
 
 # Error messages
 error.save.dialogtitle=Error saving data
@@ -610,4 +641,7 @@ error.osmimage.dialogtitle=Error loading map images
 error.osmimage.failed=Failed to load map images. Please check internet connection.
 error.language.wrongfile=The selected file doesn't appear to be a language file for Prune
 error.convertnamestotimes.nonames=No names could be converted into times
-error.lookupsrtm.none=No altitude values found
+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
index a54ee1b2ecb4bad853899ec08635209d050574be..aa76d8f734b6800c43de0576f6911247a86e7f99 100644 (file)
@@ -34,19 +34,21 @@ menu.photo.connect=Conectar con punto
 menu.photo.disconnect=Desconectar de punto
 menu.photo.delete=Eliminar foto
 menu.view=Ver
-menu.view.browser=Mapa en un navegador
+menu.view.showsidebars=Mostrar barras laterales
+menu.view.browser=Mapa en una ventana del navegador
 menu.view.browser.google=Google maps
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Mapas Yahoo
 menu.view.browser.bing=Mapas Bing
 menu.settings=Preferencias
+menu.settings.onlinemode=Cargar mapas de Internet
 menu.help=Ayuda
 # Popup menu for map
 menu.map.zoomin=Ampliar zoom
 menu.map.zoomout=Reducir zoom
 menu.map.zoomfull=Mostrar todo
-menu.map.newpoint=Crear uno punto nuevo
+menu.map.newpoint=Crear un punto nuevo
 menu.map.connect=Conectar puntos de track
 menu.map.autopan=Posicionar autom\u00e1ticamente
 menu.map.showmap=Mostrar el mapa
@@ -78,11 +80,13 @@ function.sendtogps=Enviar datos al GPS
 function.exportkml=Exportar KML
 function.exportgpx=Exportar GPX
 function.exportpov=Exportar POV
+function.exportsvg=Exportar SVG
 function.editwaypointname=Editar nombre de waypoint
 function.compress=Comprimir track
 function.addtimeoffset=A\u00f1adir compensar tiempo
 function.addaltitudeoffset=A\u00f1adir compensar altitud
 function.convertnamestotimes=Convertir los nombres de los "waypoints" a tiempo
+function.deletefieldvalues=Borrar valores del campo
 function.findwaypoint=Buscar waypoint
 function.pastecoordinates=Insertar nuevas coordenadas
 function.charts=Diagramas
@@ -93,6 +97,8 @@ function.setmapbg=Configurar fondo de mapa
 function.setkmzimagesize=Configurar tama\u00f1os de las im\u00e1genes KMZ
 function.setpaths=Configurar rutas del programas
 function.getgpsies=Bajar ruta de Gpsies
+function.uploadgpsies=Subir recorrido a Gpsies
+function.lookupsrtm=Obtener altitudes de SRTM
 function.duplicatepoint=Duplicar punto
 function.setcolours=Establecer color
 function.setlanguage=Establecer lenguaje
@@ -100,22 +106,23 @@ function.correlatephotos=Correlacionar fotos
 function.rearrangephotos=Reacomodar fotos
 function.rotatephotoleft=Girar a la izquierda
 function.rotatephotoright=Girar a la derecha
-function.ignoreexifthumb=Ignorar "thumbnail" de exif
+function.ignoreexifthumb=Ignorar miniatura exif
 function.help=Ayuda
 function.showkeys=Mostrar teclas o combinaciones de atajo
 function.about=Acerca de Prune
 function.checkversion=Buscar una nueva versi\u00f3n
 function.saveconfig=Guardar preferencias
+function.diskcache=Guardar mapas en disco
 
 # Dialogs
 dialog.exit.confirm.title=Salir de Prune
-dialog.exit.confirm.text=Los datos han sido modificados. Desea salir de Prune?
-dialog.openappend.title=Agregar a datos existentes
-dialog.openappend.text=Agregar estos datos a los datos ya guardados?
+dialog.exit.confirm.text=¿Los datos han sido modificados. Desea salir de Prune?
+dialog.openappend.title=¿Agregar a datos existentes
+dialog.openappend.text=¿Agregar estos datos a los datos ya guardados?
 dialog.deletepoint.title=Borrar punto
-dialog.deletepoint.deletephoto=Borrar la foto tambien?
+dialog.deletepoint.deletephoto=¿Borrar la foto tambien?
 dialog.deletephoto.title=Borrar foto
-dialog.deletephoto.deletepoint=Borrar el punto tambien?
+dialog.deletephoto.deletepoint=¿Borrar el punto tambien?
 dialog.openoptions.title=Opciones de abrir
 dialog.openoptions.filesnippet=Extraer archivo
 dialog.load.table.field=Campo
@@ -131,19 +138,22 @@ dialog.openoptions.deliminfo.records=datos, con
 dialog.openoptions.deliminfo.fields=campos
 dialog.openoptions.deliminfo.norecords=Ningun dato
 dialog.openoptions.altitudeunits=Unidades altitud
+dialog.open.contentsdoubled=Este archivo contiene dos copias de cada punto,\nuna como "waypoints" y otra como puntos de recorrido.
+dialog.selecttracks.intro=Seleccionar recorrido/s a cargar
+dialog.selecttracks.noname=Innominados
 dialog.jpegload.subdirectories=Incluir subdirectorios
 dialog.jpegload.loadjpegswithoutcoords=Fotos sin coordenadas tambien
 dialog.jpegload.loadjpegsoutsidearea=Incluir fotos fuera del \u00e1rea
 dialog.jpegload.progress.title=Cargando fotos
 dialog.jpegload.progress=Por favor espere mientras se buscan las fotos
-dialog.gpsload.nogpsbabel=gpsbabel program no encontrado. Desea continuar?
+dialog.gpsload.nogpsbabel=No se ha encontrado el programa gpsbabel. ¿Desea continuar?
 dialog.gpsload.device=Dispositivo
 dialog.gpsload.format=Formato
 dialog.gpsload.getwaypoints=Cargar waypoints
 dialog.gpsload.gettracks=Cargar tracks
 dialog.gpsload.save=Salvar al archivo
-dialog.gpssend.sendwaypoints=enviar "waypoints"
-dialog.gpssend.sendtracks=enviar tracks
+dialog.gpssend.sendwaypoints=Enviar "waypoints"
+dialog.gpssend.sendtracks=Enviar tracks
 dialog.gpssend.trackname=Nombre del track
 dialog.saveoptions.title=Guardar archivo
 dialog.save.fieldstosave=Campos a guardar
@@ -153,9 +163,9 @@ dialog.save.table.save=Guardar
 dialog.save.headerrow=T\u00edtulo fila
 dialog.save.coordinateunits=Unidades de las coordenadas
 dialog.save.altitudeunits=Unidades de las altitudes
-dialog.save.timestampformat=Format del tiempo
+dialog.save.timestampformat=Formato del tiempo
 dialog.save.overwrite.title=El archivo ya existe
-dialog.save.overwrite.text=El archivo ya existe, desea sobreescribirlo?
+dialog.save.overwrite.text=El archivo ya existe, ¿desea sobreescribirlo?
 dialog.save.notypesselected=No se han seleccionado tipos de puntos
 dialog.exportkml.text=Descripci\u00f3n para los datos
 dialog.exportkml.altitude=Absoluta altitudes (para aviaci\u00f3n)
@@ -175,14 +185,18 @@ dialog.exportpov.modelstyle=Estilo
 dialog.exportpov.ballsandsticks=Balas en palos
 dialog.exportpov.tubesandwalls=Tubos y paredes
 dialog.exportpov.warningtracksize=Este track contiene un gran numero de puntos. Puede ser que Java3D no los pueda visualizar. Est\u00e1 seguro de que desea continuar?
+dialog.exportsvg.text=Seleccione los par\u00e1metros para exportar a SVG
+dialog.exportsvg.phi=Ángulo de azimuth \u03d5
+dialog.exportsvg.theta=Ángulo de elevaci\u00f3n
+dialog.exportsvg.gradients=Usar degradado para sombras
 dialog.pointtype.desc=Salvar los siguientes tipos de puntos:
 dialog.pointtype.track=Puntos de track
 dialog.pointtype.photo=Puntos de foto
 dialog.pointtype.selection=Solo selecci\u00f3n
 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. Esta seguro que desea invertir esta secci\u00f3n?
+dialog.confirmreversetrack.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de la inversi\u00f3n. ¿Est\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 la .... Esta seguro que desea ... esta secci\u00f3n?
+dialog.confirmcutandmove.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de mover.\n¿Esta 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
@@ -190,7 +204,7 @@ dialog.undo.pretext=Por favor, seleccione la operaci\u00f3n(es) a deshacer
 dialog.undo.none.title=No se puede deshacer
 dialog.undo.none.text=Ninguna operaci\u00f3n a deshacer
 dialog.clearundo.title=Despejar la lista de deshacer
-dialog.clearundo.text=Esta seguro que desea despejar la lista de deshacer?, se perder\u00e1 toda la informaci\u00f3n!
+dialog.clearundo.text=¿Esta seguro que desea despejar la lista de deshacer?, ¡se perder\u00e1 toda la informaci\u00f3n!
 dialog.pointedit.title=Editar punto
 dialog.pointedit.text=Seleccione cada campo a editar y use el bot\u00f3n 'Editar' para modificar el valor
 dialog.pointedit.table.field=Campo
@@ -202,25 +216,25 @@ dialog.pointnameedit.name=Nombre de waypoint
 dialog.pointnameedit.uppercase=May\u00fasculas
 dialog.pointnameedit.lowercase=min\u00fasculas
 dialog.pointnameedit.sentencecase=Mezcla
-dialog.addtimeoffset.add=a\u00f1adir tiempo
-dialog.addtimeoffset.subtract=sustraer tiempo
+dialog.addtimeoffset.add=A\u00f1adir tiempo
+dialog.addtimeoffset.subtract=Sustraer tiempo
 dialog.addtimeoffset.days=Dias
 dialog.addtimeoffset.hours=Horas
 dialog.addtimeoffset.minutes=Minutos
-dialog.addtimeoffset.notimestamps=No se puede a\u00f1adir tiempo de puesta a esta selecci\u00f3n si \u00e9sta no contiene ninguna informaci\u00f3n de "timestamp"
+dialog.addtimeoffset.notimestamps=No se puede a\u00f1adir un margen de tiempo a esta selecci\u00f3n ya que no contiene informaci\u00f3n de tiempo.
 dialog.findwaypoint.intro=Ingresar parte del nombre de "waypoint"
 dialog.findwaypoint.search=Buscar
 dialog.saveexif.title=Guardar Exif
 dialog.saveexif.intro=Seleccione fotos a guardar
 dialog.saveexif.nothingtosave=Coordenadas no modificadas, nada que guardar
-dialog.saveexif.noexiftool=exiftool program no encontrado. Desea continuar?
+dialog.saveexif.noexiftool=No se encuentra el programa exiftool. ¿Desea continuar?
 dialog.saveexif.table.photoname=Nombre de la foto
 dialog.saveexif.table.status=Estado
 dialog.saveexif.table.save=Guardar
 dialog.saveexif.photostatus.connected=Conectada
 dialog.saveexif.photostatus.disconnected=Desconectada
 dialog.saveexif.photostatus.modified=Modificada
-dialog.saveexif.overwrite=Sobreescribirlar archivos?
+dialog.saveexif.overwrite=Sobreescribir archivos
 dialog.saveexif.force=Fuerza despreciar errores menores
 dialog.charts.xaxis=Eje de abscisas
 dialog.charts.yaxis=Eje vertical
@@ -230,23 +244,41 @@ dialog.charts.svg=Salida a archivo SVG
 dialog.charts.svgwidth=Ancho de SVG
 dialog.charts.svgheight=Alto de SVG
 dialog.charts.needaltitudeortimes=La pista debe tener altitudes o informaci\u00f3n de tiempo en orden para crear las tablas
-dialog.charts.gnuplotnotfound=No pudo ser encontrado gnuplot con el camino o la direcci\u00f3n proporcionada
+dialog.charts.gnuplotnotfound=No pudo ser encontrado gnuplot con la ruta proporcionada
 dialog.distances.intro=L\u00edneas rectas entre puntos
 dialog.distances.column.from=De punto
 dialog.distances.column.to=Al punto
 dialog.distances.currentpoint=Punto actual
 dialog.distances.toofewpoints=Esta funcion necesita "waypoints" para poder calcular las distancias entre ellos
 dialog.fullrangedetails.intro=Aqui estan los detalles para la selecci\u00f3n de rangos
-dialog.addmapsource.sourcename=Nombre de la fuente
+dialog.setmapbg.intro=Seleccione un proveedor de mapas o a\u00f1ada uno nuevo
+dialog.addmapsource.title=A\u00f1adir un proveedor de mapas
+dialog.addmapsource.sourcename=Nombre del proveedor
+dialog.addmapsource.layer1url=URL de la primera capa
+dialog.addmapsource.layer2url=URL opcional de la segunda capa
+dialog.addmapsource.maxzoom=M\u00e1ximo nivel de zoom
 dialog.addmapsource.cloudstyle=N\u00famero del estilo
 dialog.addmapsource.noname=Innominada
 dialog.gpsies.column.name=Nombre del track
 dialog.gpsies.column.length=Distancia
 dialog.gpsies.description=Descripci\u00f3n
-dialog.gpsies.nodescription=Sin Descripci\u00f3n
+dialog.gpsies.nodescription=Sin descripci\u00f3n
 dialog.gpsies.nonefound=No se encontraron pistas
+dialog.gpsies.username=Nombre de usuario en Gpsies
+dialog.gpsies.password=Contrase\u00f1a de Gpsies
+dialog.gpsies.keepprivate=Mantener el recorrido como privado
+dialog.gpsies.confirmopenpage=Abrir la p\u00e1gina web del recorrido subido
+dialog.gpsies.activities=Apto para
+dialog.gpsies.activity.trekking=Excursi\u00f3n
+dialog.gpsies.activity.walking=Caminar
+dialog.gpsies.activity.jogging=Correr
+dialog.gpsies.activity.biking=En bicicleta
+dialog.gpsies.activity.motorbiking=En moto
+dialog.gpsies.activity.snowshoe=Raquetas de nieve
+dialog.gpsies.activity.sailing=Vela
+dialog.gpsies.activity.skating=Patinaje
 dialog.correlate.notimestamps=No hay informaci\u00f3n de tiempo para los puntos, as\u00ed que no hay nada que correlacionar con las fotos.
-dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\nEst\u00e1 seguro de que desea continuar?
+dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n¿Est\u00e1 seguro de que desea continuar?
 dialog.correlate.photoselect.intro=Seleccione una de estas fotos correlacionadas para usar como margen de tiempo
 dialog.correlate.photoselect.photoname=Nombre de la foto
 dialog.correlate.photoselect.timediff=Diferencia de tiempo
@@ -275,7 +307,10 @@ dialog.rearrangephotos.sortbyfilename=Sortear por nombre del archivo
 dialog.rearrangephotos.sortbytime=Sortear por tiempo
 dialog.compress.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
 dialog.compress.wackypoints.paramdesc=Factor distancia
+dialog.compress.singletons.title=Eliminar puntos aislados
 dialog.compress.singletons.paramdesc=Factor distancia
 dialog.compress.duplicates.title=Eliminar duplicados
 dialog.compress.summarylabel=Puntos para eliminar
@@ -289,7 +324,7 @@ dialog.about.summarytext1=Prune es un programa para cargar, mostrar y editar dat
 dialog.about.summarytext2=Distribuido bajo el GNU GPL para uso libre y gratuito.<br>Se permite (y se anima) la copia, redistribuci\u00f3n y modificaci\u00f3n de acuerdo<br>a las condiciones incluidas en el archivo <code>licence.txt</code>.
 dialog.about.summarytext3=Por favor, ver <code style="font-weight:bold">http://activityworkshop.net/</code> para m\u00e1s informaci\u00f3n y gu\u00edas del usuario.
 dialog.about.languages=Idiomas disponibles
-dialog.about.translatedby=Traducci\u00f3n al espa\u00f1ol realizada por activityworkshop y amigos muy amables!
+dialog.about.translatedby=Traducci\u00f3n al espa\u00f1ol realizada por Miguel, In\u00e9s, josatoc y Javier
 dialog.about.systeminfo=Informacion del sistema
 dialog.about.systeminfo.os=Sistema operativo
 dialog.about.systeminfo.java=Java Runtime
@@ -298,14 +333,14 @@ dialog.about.systeminfo.povray=Povray instalado
 dialog.about.systeminfo.exiftool=Exiftool instalado
 dialog.about.systeminfo.gpsbabel=Gpsbabel instalado
 dialog.about.systeminfo.gnuplot=Gnuplot instalado
-dialog.about.systeminfo.exiflib=Bibliotheca exif
+dialog.about.systeminfo.exiflib=Biblioteca exif
 dialog.about.systeminfo.exiflib.internal=Interna
 dialog.about.systeminfo.exiflib.internal.failed=Interna (no encontrada)
 dialog.about.systeminfo.exiflib.external=Externa
 dialog.about.systeminfo.exiflib.external.failed=Externa (no encontrada)
 dialog.about.yes=Si
 dialog.about.no=No
-dialog.about.credits=Creditos
+dialog.about.credits=Cr\u00e9ditos
 dialog.about.credits.code=El c\u00f3digo de Prune fue escrito por
 dialog.about.credits.exifcode=El c\u00f3digo Exif por
 dialog.about.credits.icons=Algunos iconos se tomaron de
@@ -315,13 +350,15 @@ dialog.about.credits.devtools=Herramientas de desarrollo
 dialog.about.credits.othertools=Otras herramientas
 dialog.about.credits.thanks=Gracias a
 dialog.about.readme=Readme
-dialog.checkversion.error=El numero de versi\u00f3n no pudo ser verificada.\n Por favor verificar la conexi\u00f3n de internet
+dialog.checkversion.error=El numero de versi\u00f3n no pudo ser verificada.\n Por favor verificar la conexi\u00f3n de Internet
 dialog.checkversion.uptodate=Esta usted utilizando la \u00faltima versi\u00f3n de Prune
-dialog.checkversion.newversion1=Una nueva versi\u00f3n de Prune est\u00e1 disponible! La \u00daltima versi\u00f3n es ahora versi\u00f3n
+dialog.checkversion.newversion1=¡Una nueva versi\u00f3n de Prune est\u00e1 disponible! La \u00faltima es ahora la versi\u00f3n
 dialog.checkversion.newversion2=.
 dialog.checkversion.releasedate1=La nueva versi\u00f3n fue lanzada en
 dialog.checkversion.releasedate2=.
+dialog.checkversion.download=Para descargar la nueva versi\u00f3n visite http://activityworkshop.net/software/prune/download.html.
 dialog.keys.intro=Usted puede usar el siguiente atajo en lugar de usar el rat\u00f3n
+dialog.keys.keylist=<table><tr><td>Teclas de cursor</td><td>Desplazar a la izquierde, derecha, arriba, abajo</td></tr><tr><td>Ctrl + cursor izquierda, derecha</td><td>Seleccionar punto siguiente o anterior</td></tr><tr><td>Ctrl + cursor arriba, abajo</td><td>Ampliar o reducir zoom</td></tr><tr><td>Ctrl + Av Pag, Re Pag</td><td>Seleccionar segmento siguiente, anterior</td></tr><tr><td>Ctrl + Inicio, Fin</td><td>Seleccionar primer, \u00faltimo punto</td></tr><tr><td>Supr</td><td>Eliminar punto actual</td></tr></table>
 dialog.saveconfig.desc=La siguiente configuraci\u00f3n puede ser salvada en un archivo de configuraci\u00f3n
 dialog.saveconfig.prune.trackdirectory=Directorio de pista
 dialog.saveconfig.prune.photodirectory=Directorio de foto
@@ -330,22 +367,23 @@ 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=Usar unidades m\u00e9tricas?
-dialog.saveconfig.prune.gnuplotpath=Camino a gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Camino a gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Camino a exiftool
-dialog.saveconfig.prune.mapserverindex=\u00cdndice de mapa del servidor
-dialog.saveconfig.prune.mapserverurl=Direcci\u00f3n URL de mapa del servidor
-dialog.saveconfig.prune.mapsourcelist=Fuentes de cartas
-dialog.saveconfig.prune.diskcache=Directorio de cartas
-dialog.saveconfig.prune.kmzimagewidth=Ancho de im\u00e1genes en kmz
-dialog.saveconfig.prune.kmzimageheight=Alto de im\u00e1genes en kmz
+dialog.saveconfig.prune.metricunits=¿Usar unidades m\u00e9tricas?
+dialog.saveconfig.prune.gnuplotpath=Ruta a gnuplot
+dialog.saveconfig.prune.gpsbabelpath=Ruta a gpsbabel
+dialog.saveconfig.prune.exiftoolpath=Ruta a exiftool
+dialog.saveconfig.prune.mapsource=Proveedor de mapas seleccionado
+dialog.saveconfig.prune.mapsourcelist=Proveedor de mapas
+dialog.saveconfig.prune.diskcache=Memoria intermedia de mapas
+dialog.saveconfig.prune.kmzimagewidth=Ancho de im\u00e1genes en KMZ
+dialog.saveconfig.prune.kmzimageheight=Alto de im\u00e1genes en KMZ
 dialog.saveconfig.prune.colourscheme=Color de esquema
 dialog.saveconfig.prune.kmltrackcolour=Color de pista de KML
 dialog.setpaths.intro=Si usted necesita, puede escoger las rutas a aplicaciones externas
+dialog.setpaths.found=¿Ruta encontrada?
 dialog.addaltitude.noaltitudes=Los rangos seleccionados no contienen altitudes
-dialog.addaltitude.desc=
-dialog.setcolours.intro=Clickear sobre una placa de color para cambiar el color
+dialog.addaltitude.desc=Desplazamiento de altitud a a\u00f1adir
+dialog.lookupsrtm.overwritezeros=¿Sobrescribir valores de altitud nulos?
+dialog.setcolours.intro=Haga clic sobre una placa de color para cambiar el color
 dialog.setcolours.background=Fondo
 dialog.setcolours.borders=Bordes
 dialog.setcolours.lines=L\u00edneas
@@ -359,48 +397,57 @@ dialog.colourchooser.red=Rojo
 dialog.colourchooser.green=Verde
 dialog.colourchooser.blue=Azul
 dialog.setlanguage.firstintro=Puede usted seleccionar algunos de los lenguajes incluidos,<p>o puede en lugar de esto seleccionar un archivo de texto
-dialog.setlanguage.secondintro=Usted necesita salvar su configuraci\u00f3n y luego<p>reiniciar Prune para cambiar el lenguaje
+dialog.setlanguage.secondintro=Usted necesita guardar sus preferencias y luego<p>reiniciar Prune para cambiar el lenguaje
 dialog.setlanguage.language=Lenguaje
 dialog.setlanguage.languagefile=Archivo de lenguaje
-dialog.setlanguage.endmessage=Ahora salve su configuraci\u00f3n y reinicie Prune\npara que los cambios tomen efecto.
-dialog.diskcache.save=Cargar cartas
-dialog.diskcache.dir=Directorio de cartas
+dialog.setlanguage.endmessage=Ahora guarde sus preferencias y reinicie Prune\npara que los cambios tomen efecto.
+dialog.diskcache.save=Guardar im\u00e1genes de mapa a disco
+dialog.diskcache.dir=Directorio de mapas
 dialog.diskcache.createdir=Crear directorio
+dialog.diskcache.nocreate=No se ha creado el directorio de mapas
+dialog.deletefieldvalues.intro=Seleccionar el campo a eliminar para el rango actual
 
 # 3d window
 dialog.3d.title=Prune vista 3-D
 dialog.3d.altitudecap=Escala de las altitudes
+dialog.3d.altitudefactor=Factor de exageraci\u00f3n de altura
 dialog.3dlines.title=Cuadr\u00edcula Prune
-dialog.3dlines.empty=No hay ninguna cuadr\u00edcula!
+dialog.3dlines.empty=¡No hay ninguna cuadr\u00edcula!
 dialog.3dlines.intro=Informaci\u00f3n de la cuadr\u00edcula
 
-# Confirm messages || These are displayed as confirmation in the status bar
+# Confirm messages
 confirm.loadfile=Dato cargado de
-confirm.save.ok1=Guardando
+confirm.save.ok1=Guardado correctamente
 confirm.save.ok2=puntos al archivo
 confirm.deletepoint.single=punto eliminado
 confirm.deletepoint.multi=puntos eliminados
 confirm.point.edit=Punto editado
 confirm.mergetracksegments=Segmentos unidos
 confirm.reverserange=Rango invertido
+confirm.addtimeoffset=A\u00f1adido margen de tiempo
+confirm.addaltitudeoffset=A\u00f1adido margen de altitud
 confirm.rearrangewaypoints=Waypoints reorganizados
 confirm.rearrangephotos=Fotos reacomodadas
 confirm.cutandmove=Mover Selecci\u00f3n
-confirm.saveexif.ok1=Guardando
+confirm.convertnamestotimes=Nombres de "waypoint" convertidos
+confirm.saveexif.ok1=Guardado
 confirm.saveexif.ok2=fotos
-confirm.undo.single=operaci\u00f3n no realizada
-confirm.undo.multi=operaci\u00f3n(es) no realizada(s)
+confirm.undo.single=operaci\u00f3n deshecha
+confirm.undo.multi=operaci\u00f3n(es) deshechas(s)
 confirm.jpegload.single=Foto incluida
 confirm.jpegload.multi=Fotos incluidas
-confirm.photo.connect=Foto conectado
-confirm.photo.disconnect=Foto desconectado
-confirm.correlate.single=foto fue correlada
-confirm.correlate.multi=fotos fueron correladas
+confirm.photo.connect=Foto conectada
+confirm.photo.disconnect=Foto desconectada
+confirm.correlate.single=foto fue correlacionada
+confirm.correlate.multi=fotos fueron correlacionadas
 confirm.createpoint=punto creado
-confirm.rotatephoto=Foto rotada
+confirm.rotatephoto=foto rotada
 confirm.running=Trabajando ...
+confirm.lookupsrtm1=Encontrados
+confirm.lookupsrtm2=valor de altitud para la funci\u00f3n de b\u00fasqueda SRTM
+confirm.deletefieldvalues=Valores del campo eliminados
 
-# Buttons || These are all the texts for buttons
+# Buttons
 button.ok=Aceptar
 button.back=Anterior
 button.next=Siguiente
@@ -423,10 +470,13 @@ button.selectall=Seleccionar todo
 button.selectnone=Seleccionar nada
 button.preview=Previsualizaci\u00f3n
 button.load=Cargar
+button.upload=Subir
 button.guessfields=Adivinar campos
 button.showwebpage=Mostrar p\u00e1gina web
 button.check=Verificar
 button.resettodefaults=Restablecer valores a los predeterminados
+button.browse=Navegar...
+button.addnew=A\u00f1adir nuevo
 button.delete=Eliminar
 
 # File types
@@ -439,10 +489,10 @@ filetype.gpx=Archivos GPX
 filetype.pov=Archivos POV
 filetype.svg=Archivos SVG
 
-# Display components || These are all for the side panels showing point/range details
+# Display components
 display.nodata=Ning\u00fan dato cargado
 display.noaltitudes=Los datos del track no incluyen altitudes
-display.notimestamps=Los datos del track no incluyen tiempos
+display.notimestamps=Los datos de recorrido no incluyen marcas de tiempo
 details.trackdetails=Detalles del track
 details.notrack=Ning\u00fan track cargado
 details.track.points=Puntos
@@ -470,10 +520,11 @@ 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
 details.range.gradient=Gradiente
 details.waypointsphotos.waypoints=Waypoints
 details.waypointsphotos.photos=Fotos
-details.photodetails=Detalles del Foto
+details.photodetails=Detalles de la foto
 details.nophoto=Ninguna foto seleccionada
 details.photo.loading=Cargando
 details.photo.connected=Conectada
@@ -526,7 +577,7 @@ cardinal.s=S
 cardinal.e=E
 cardinal.w=O
 
-# 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=cargar datos
 undo.loadphotos=cargar fotos
 undo.editpoint=editar punto
@@ -537,43 +588,57 @@ undo.compress=comprimir track
 undo.insert=insertar puntos
 undo.reverse=invertir rango
 undo.mergetracksegments=unir los segmentos de track
+undo.addtimeoffset=a\u00f1adir margen de tiempo
+undo.addaltitudeoffset=a\u00f1adir margen de altitud
 undo.rearrangewaypoints=reordenar waypoints
+undo.cutandmove=mover secci\u00f3n
 undo.connectphoto=conectar foto
 undo.disconnectphoto=desconectar foto
 undo.correlate=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
 
 # Error messages
 error.save.dialogtitle=Fallo al guardar datos
-error.save.nodata=Ning\u00fan dato salvado
+error.save.nodata=Ning\u00fan dato guardado
 error.save.failed=Fallo al guardar datos al archivo
 error.saveexif.filenotfound=Archivo no encontrado
 error.saveexif.cannotoverwrite1=No se puede guardar
-error.saveexif.cannotoverwrite2=. Guardar a una copia?
-error.saveexif.failed1=Fall\u00f3 al salvar
+error.saveexif.cannotoverwrite2=es s\u00f3lo-lectura y no se puede sobreescribir. Guardar a una copia?
+error.saveexif.failed1=Fall\u00f3 al guardar
 error.saveexif.failed2=de las im\u00e1genes
+error.saveexif.forced1=
+error.saveexif.forced2=de las im\u00e1genes requiere forzar
 error.load.dialogtitle=Fallo al cargar datos
 error.load.noread=No se puede leer el fichero
-error.load.nopoints=Ninguna informaci\u00f3n coordenadas encontrada
+error.load.nopoints=No se encuentra ninguna informaci\u00f3n de coordenadas en el archivo
 error.load.unknownxml=Formato xml no reconocido:
-error.load.noxmlinzip=Ning\u00fan xml archivo encontrado
+error.load.noxmlinzip=No se encuentra ning\u00fan archivo xml en el archivo zip
 error.load.othererror=Fallo al cargar datos:
 error.jpegload.dialogtitle=Error cargando fotos
-error.jpegload.nofilesfound=Ning\u00fan archivo encontrado
-error.jpegload.nojpegsfound=Ning\u00fan archivo jpeg encontrado
-error.jpegload.noexiffound=Ninguna informaci\u00f3n EXIF encontrada
-error.jpegload.nogpsfound=Ninguna informaci\u00f3n GPS encontrada
+error.jpegload.nofilesfound=No se encuentra ning\u00fan archivo
+error.jpegload.nojpegsfound=No se encuentra ning\u00fan archivo jpeg
+error.jpegload.noexiffound=No se encuentra informaci\u00f3n EXIF
+error.jpegload.nogpsfound=No se encuentra informaci\u00f3n GPS
+error.jpegload.exifreadfailed=Fallo al leer la informaci\u00f3n EXIF. No se puede leer ninguna informaci\u00f3n EXIF\ncon las librer\u00edas internas ni externas.
 error.gpsload.unknown=Error desconocido
 error.undofailed.title=Fallo al deshacer
 error.undofailed.text=No ha sido posible deshacer la operaci\u00f3n
 error.function.noop.title=La funci\u00f3n no se ha efectuado
 error.rearrange.noop=Reordenaci\u00f3n de puntos no se ha efectuado
 error.function.notavailable.title=Funci\u00f3n no disponible
-error.function.nojava3d=Esta funci\u00f3n requiere la librer\u00eda Java3d, disponible en Sun.com.
+error.function.nojava3d=Esta funci\u00f3n requiere la librer\u00eda Java3d,\ndisponible en Sun.com.
 error.3d=Ha ocurrido un error con la funci\u00f3n 3-D
 error.readme.notfound=Archivo readme no encontrado
 error.osmimage.dialogtitle=Error al cargar el mapa
 error.osmimage.failed=Imposible cargar el mapa. Por favor, compruebe la conexi\u00f3n a internet.
 error.language.wrongfile=El archivo seleccionado no parece ser un archivo de lenguaje para Prune
 error.convertnamestotimes.nonames=Los nombres no pudieron ser convertidos en tiempos
+error.lookupsrtm.nonefound=No se encontraron valores de altitud
+error.lookupsrtm.nonerequired=Todos los puntos tienen altitudes, as\u00ed que no hay nada que buscar.
+error.gpsies.uploadnotok=El servidor de gpsies ha devuelto el mensaje
+error.gpsies.uploadfailed=La carga ha fallado con el error
diff --git a/tim/prune/lang/prune-texts_fa.properties b/tim/prune/lang/prune-texts_fa.properties
new file mode 100644 (file)
index 0000000..b2aaefe
--- /dev/null
@@ -0,0 +1,73 @@
+# Text entries for the Prune application
+# Persian (Farsi) entries as extra
+
+# Menu entries
+menu.file=\u067e\u0648\u0634\u0647
+menu.file.addphotos=\u0627\u0636\u0627\u0641\u0647 \u06a9\u0631\u062f\u0646 \u0639\u06a9\u0633
+menu.file.save=\u0630\u062e\u064a\u0631\u0647
+menu.file.exit=\u062e\u0631\u0648\u062c
+menu.track=\u0645\u0633\u064a\u0631
+menu.track.undo=\u0628\u0627\u0637\u0644 \u06a9\u0631\u062f\u0646 \u06a9\u0627\u0631 \u0642\u0628\u0644\u064a
+menu.track.clearundo=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0644\u064a\u0633\u062a \u06a9\u0627\u0631\u0647\u0627\u06cc \u0627\u0646\u062c\u0627\u0645 \u0634\u062f\u0647
+menu.track.deletemarked=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0646\u0642\u0627\u0637 \u0627\u0646\u062a\u062e\u0627\u0628 \u0634\u062f\u0647
+menu.track.rearrange=\u0628\u0627\u0632 \u0686\u064a\u0646\u06cc \u0646\u0642\u0627\u0637 \u0645\u0633\u064a\u0631
+menu.track.rearrange.start=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0628\u062a\u062f\u0627\u06cc \u0645\u0633\u064a\u0631
+menu.track.rearrange.end=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0646\u062a\u0647\u0627\u06cc \u0645\u0633\u064a\u0631
+menu.track.rearrange.nearest=\u0647\u0631 \u064a\u06a9 \u0628\u0647 \u0646\u0632\u062f\u064a\u06a9 \u0646\u0642\u0637\u0647 \u0645\u0633\u064a\u0631
+menu.range=\u0686\u064a\u0646\u0634
+menu.range.all=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u0645\u0647 \u0646\u0642\u0627\u0637
+menu.range.none=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u064a\u0686 \u064a\u06a9 \u0627\u0632 \u0646\u0642\u0627\u0637
+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
+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
+menu.range.cutandmove=\u062c\u062f\u0627 \u06a9\u0631\u062f\u0646 \u0642\u0633\u0645\u062a \u0627\u0646\u062a\u062e\u0627\u0628 \u0634\u062f\u0647
+menu.point=\u0646\u0642\u0637\u0647
+menu.point.editpoint=\u062a\u0646\u0638\u064a\u0645\u0627\u062a \u0646\u0642\u0637\u0647
+menu.point.deletepoint=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0646\u0642\u0637\u0647
+menu.photo=\u0639\u06a9\u0633
+menu.photo.saveexif=\u0630\u062e\u064a\u0631\u0647 \u062f\u0631 \u0641\u0627\u064a\u0644 \u0636\u0645\u064a\u0645\u0647 \u0639\u06a9\u0633
+menu.photo.connect=\u0627\u062a\u0635\u0627\u0644 \u0628\u0647 \u0646\u0642\u0637\u0647
+menu.photo.disconnect=\u0642\u0637\u0639 \u0627\u062a\u0635\u0627\u0644 \u0627\u0632 \u0646\u0642\u0637\u0647
+menu.photo.delete=\u0628\u0631\u062f\u0627\u0634\u062a\u0646 \u0639\u06a9\u0633
+menu.view=\u062f\u064a\u062f
+menu.view.browser=\u0646\u0642\u0634\u0647 \u062f\u0631\u062c\u0633\u062a\u062c\u0648\u06af\u0631
+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=\u062a\u0646\u0638\u06cc\u0645\u0627\u062a
+menu.settings.onlinemode=\u062f\u0627\u0646\u0644\u0648\u062f \u0646\u0642\u0634\u0647 \u0627\u0632 \u0627\u064a\u0646\u062a\u0631\u0646\u062a
+menu.help=\u0631\u0627\u0647\u0646\u0645\u0627
+# Popup menu for map
+menu.map.zoomin=\u0628\u0632\u0631\u06af \u0646\u0645\u0627\u064a\u06cc
+menu.map.zoomout=\u06a9\u0648\u0686\u06a9 \u0646\u0645\u0627\u064a\u06cc
+menu.map.zoomfull=\u0646\u0645\u0627\u064a\u0634 \u062f\u0631 \u0627\u0646\u062f\u0627\u0632\u0647 \u06a9\u0627\u0645\u0644
+menu.map.newpoint=\u0627\u064a\u062c\u0627\u062f \u0646\u0642\u0637\u0647 \u062c\u062f\u064a\u062f
+menu.map.connect=\u0627\u062a\u0635\u0627\u0644 \u0646\u0642\u0627\u0637 \u0645\u0633\u064a\u0631
+menu.map.autopan=\u0646\u0645\u0627\u064a\u0634 \u062f\u0627\u0626\u0645 \u0646\u0642\u0637\u0647
+menu.map.showmap=\u0646\u0645\u0627\u064a\u0634 \u0646\u0642\u0637\u0647
+menu.map.showscalebar=\u0646\u0645\u0627\u064a\u0634 \u062a\u0646\u0638\u064a\u0645\u0627\u062a \u0645\u0642\u064a\u0627\u0633
+
+# Alt keys for menus
+altkey.menu.file=\u067e
+altkey.menu.track=\u0645
+altkey.menu.range=\u0686
+altkey.menu.point=\u0646
+altkey.menu.view=\u062f
+altkey.menu.photo=\u0639
+altkey.menu.settings=\u062a
+altkey.menu.help=\u0631
+
+# Ctrl shortcuts for menu items
+shortcut.menu.file.open=\u0628
+shortcut.menu.file.load=\u062f
+shortcut.menu.file.save=\u0630
+shortcut.menu.track.undo=\u0638
+shortcut.menu.edit.compress=\u062a
+shortcut.menu.range.all=\u0686
+shortcut.menu.help.help=\u0631
index dcbc3a7c65a6172c946f23026d24d0036e840895..e608c9d9f1c83eca4164ebf7706fc645b0dfd077 100644 (file)
@@ -254,6 +254,15 @@ dialog.gpsies.column.length=Distance
 dialog.gpsies.description=Description
 dialog.gpsies.nodescription=Aucune description
 dialog.gpsies.nonefound=Aucun trace trouv\u00e9
+dialog.gpsies.activities=Activit\u00e9
+dialog.gpsies.activity.trekking=Trekking
+dialog.gpsies.activity.walking=Randonn\u00e9e
+dialog.gpsies.activity.jogging=Jogging
+dialog.gpsies.activity.biking=V\u00e9lo
+dialog.gpsies.activity.motorbiking=Moto
+dialog.gpsies.activity.snowshoe=Raquette
+dialog.gpsies.activity.sailing=Volle
+dialog.gpsies.activity.skating=Skating
 dialog.correlate.notimestamps=Les points n'ont pas d'indication de temps, il n'est pas possible de les corr\u00e9ler.
 dialog.correlate.nouncorrelatedphotos=Il n'y a pas de photos non-corr\u00e9l\u00e9es.\nVoulez-vous continuer ?
 dialog.correlate.photoselect.intro=S\u00e9lectionner une de ces photos corr\u00e9l\u00e9es pour d\u00e9finir le d\u00e9calage de temps
@@ -611,4 +620,4 @@ error.osmimage.dialogtitle=Erreur au chargement des portions de cartes
 error.osmimage.failed=Erreur du chargement des portions de cartes. V\u00e9rifiez votre connexion internet.
 error.language.wrongfile=Le fichier s\u00e9lectionn\u00e9 n'est pas un fichier de langue pour Prune
 error.convertnamestotimes.nonames=Aucun nom n'a pu \u00eatre converti en horaire
-error.lookupsrtm.none=Aucune valeur d'altitude trouv\u00e9e
+error.lookupsrtm.nonefound=Aucune valeur d'altitude trouv\u00e9e pour les points
index 0801cafe20b75a63ddd8ff937110b307ca186ad7..6c07c73c323c8601d19f6d142115c7f8df68e9a8 100644 (file)
@@ -8,32 +8,33 @@ menu.file.save=Salva
 menu.file.exit=Esci
 menu.track=Traccia
 menu.track.undo=Annulla
-menu.track.clearundo=Cancella lista ultime modifiche
-menu.point.editpoint=Edita punto
-menu.point.deletepoint=Cancella punto
-menu.range.deleterange=Cancella la serie
+menu.track.clearundo=Cancella lista annulla
 menu.track.deletemarked=Cancella punti marcati
-menu.range.interpolate=Interpola
-menu.range.average=Crea punto medio della selezione
-menu.range.reverse=Inverti la serie
-menu.range.mergetracksegments=Unisci segmenti traccia
 menu.track.rearrange=Riorganizza waypoint
 menu.track.rearrange.start=Tutti all'inizio del file
 menu.track.rearrange.end=Tutti alla fine del file
 menu.track.rearrange.nearest=Sul punto pi\u00f9 vicino
-menu.range.cutandmove=Taglia e muovi la selezione
 menu.range=Serie
-menu.point=Punto
 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
+menu.range.cutandmove=Taglia e muovi la selezione
+menu.point=Punto
+menu.point.editpoint=Edita Punto
+menu.point.deletepoint=Cancella Punto
 menu.photo=Foto
 menu.photo.saveexif=Salva su Exif
 menu.photo.connect=Collega al punto
 menu.photo.disconnect=Scollega dal punto
 menu.photo.delete=Rimuovi foto
 menu.view=Visualizza
+menu.view.showsidebars=Mostra barre laterali
 menu.view.browser=Mappa sul browser
 menu.view.browser.google=Google maps
 menu.view.browser.openstreetmap=Openstreetmap
@@ -41,12 +42,13 @@ menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=mappe Yahoo
 menu.view.browser.bing=mappe Bing
 menu.settings=Preferenze
+menu.settings.onlinemode=Carica mappa da internet
 menu.help=Aiuto
 # Popup menu for map
 menu.map.zoomin=Zoom +
 menu.map.zoomout=Zoom -
 menu.map.zoomfull=Zoom tutto
-menu.map.newpoint=Crea punto nuovo
+menu.map.newpoint=Crea nuovo punto
 menu.map.connect=Aggancia ai punti
 menu.map.autopan=Autopan
 menu.map.showmap=Mostra sulla mappa
@@ -78,24 +80,39 @@ function.sendtogps=Invia dati al GPS
 function.exportkml=Esporta in KML
 function.exportgpx=Esporta in GPX
 function.exportpov=Esporta in POV
-function.editwaypointname=Edita nome waypoint
+function.exportsvg=Esporta in SVG
+function.editwaypointname=Modifica nome waypoint
 function.compress=Comprimi la traccia
 function.addtimeoffset=Aggiungi uno scarto temporale
 function.addaltitudeoffset=Aggiungi uno scarto di altitudine
+function.convertnamestotimes=Converti nomi dei waypoint in orari
+function.deletefieldvalues=Cancella i valori del campo
 function.findwaypoint=Trova waypoint
+function.pastecoordinates=Aggiungi coordinate
 function.charts=Diagrammi
 function.show3d=Mostra in 3D
 function.distances=Mostra distanze
+function.fullrangedetails=Mostra dettagli
 function.setmapbg=Configura sfondo mappa
 function.setkmzimagesize=Configura dimensione immagine KMZ
 function.setpaths=Configura percorsi programmi
 function.getgpsies=Ottieni traccie da Gpsies
+function.uploadgpsies=Carica traccia su Gpsies
+function.lookupsrtm=Ottieni quote da SRTM
+function.duplicatepoint=Duplica punto corrente in coda
+function.setcolours=Scegli colori
+function.setlanguage=Scegli la lingua
 function.correlatephotos=Correla le foto
+function.rearrangephotos=Riordina foto
+function.rotatephotoleft=Ruota foto a sinistra
+function.rotatephotoright=Ruota foto a destra
+function.ignoreexifthumb=Ignora anteprima foto exif
 function.help=Aiuto
 function.showkeys=Visualizza tasti scelta rapida
 function.about=Informazioni su Prune
 function.checkversion=Controlla gli aggiornamenti
 function.saveconfig=Salva configurazione
+function.diskcache=Salva mappe su disco
 
 # Dialogs
 dialog.exit.confirm.title=Esci da Prune
@@ -121,6 +138,9 @@ dialog.openoptions.deliminfo.records=registra, con
 dialog.openoptions.deliminfo.fields=campi
 dialog.openoptions.deliminfo.norecords=Nessun record
 dialog.openoptions.altitudeunits=Unit\u00e0 di misura altitudine
+dialog.open.contentsdoubled=Questo file contiene due copie di ogni punto,\nuno \u00e8 un waypoint e uno \u00e8 un punto traccia
+dialog.selecttracks.intro=Seleziona la traccia o le tracce da caricare
+dialog.selecttracks.noname=Senza nome
 dialog.jpegload.subdirectories=Includi sottocartelle
 dialog.jpegload.loadjpegswithoutcoords=Includi foto senza coordinate
 dialog.jpegload.loadjpegsoutsidearea=Includi foto fuori dall'area corrente
@@ -131,13 +151,14 @@ dialog.gpsload.device=Nome del Dispositivo
 dialog.gpsload.format=Formato
 dialog.gpsload.getwaypoints=Carica waypoint
 dialog.gpsload.gettracks=Carica tracce
-dialog.gpssend.sendwaypoints=Invia waypoints
+dialog.gpsload.save=Salva
+dialog.gpssend.sendwaypoints=Invia waypoint
 dialog.gpssend.sendtracks=Invia tracce
-dialog.gpssend.trackname=Nome del tracce
+dialog.gpssend.trackname=Nome della traccia
 dialog.saveoptions.title=Salva il file
 dialog.save.fieldstosave=Campi da salvare
 dialog.save.table.field=Campo
-dialog.save.table.hasdata=Ha dati
+dialog.save.table.hasdata=Contiene dati
 dialog.save.table.save=Salva
 dialog.save.headerrow=Regista l'intestazione delle righe
 dialog.save.coordinateunits=Unit\u00e0 di misura coordinate
@@ -145,13 +166,16 @@ dialog.save.altitudeunits=Unit\u00e0 di misura altitudine
 dialog.save.timestampformat=Formato della data
 dialog.save.overwrite.title=File gi\u00e0 esistente
 dialog.save.overwrite.text=Questo file esiste gi\u00e0. Sei sicuro di volerlo sovrascrivere?
+dialog.save.notypesselected=Nessun tipo di punto \u00e8 stato selezionato
 dialog.exportkml.text=Titolo dei dati
 dialog.exportkml.altitude=Includi altitudine (per aviazione)
 dialog.exportkml.kmz=Comprimi per file kmz
 dialog.exportkml.exportimages=Esporta le anteprime delle immagini per kmz
+dialog.exportkml.trackcolour=Colore della traccia
 dialog.exportgpx.name=Nome
 dialog.exportgpx.desc=Descrizione
 dialog.exportgpx.includetimestamps=Includi dati temporali
+dialog.exportgpx.copysource=Copia xml sorgente
 dialog.exportpov.text=Per favore inserisci i parametri per l'esportazione in POV
 dialog.exportpov.font=Font
 dialog.exportpov.camerax=Camera X
@@ -160,11 +184,16 @@ dialog.exportpov.cameraz=Camera Z
 dialog.exportpov.modelstyle=Stile del modello
 dialog.exportpov.ballsandsticks=Palle e bacchette
 dialog.exportpov.tubesandwalls=Tubi e pareti
-dialog.exportpov.warningtracksize=Questa traccia ha un elevato numero di punti, e Java3D pu\u00f2 non essere in grado di visualizzarli.\nSei sicuro di voler continuare?
+dialog.exportpov.warningtracksize=Questa traccia ha un elevato numero di punti, e Java3D potrebbe non essere in grado di visualizzarli.\nSei sicuro di voler continuare?
+dialog.exportsvg.text=Seleziona i parametri per esportare in SVG
+dialog.exportsvg.phi=Angolo orizzontale \u03d5
+dialog.exportsvg.theta=Angolo di elevazione \u03b8
+dialog.exportsvg.gradients=Usa il gradiente per le ombre
 dialog.pointtype.desc=Salva i tipi di punti seguenti:
 dialog.pointtype.track=Punti traccia
 dialog.pointtype.waypoint=Waypoints
 dialog.pointtype.photo=Punti foto
+dialog.pointtype.selection=Solo la selezione
 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
@@ -183,7 +212,7 @@ dialog.pointedit.table.field=Campo
 dialog.pointedit.table.value=Valore
 dialog.pointedit.table.changed=Cambiato
 dialog.pointedit.changevalue.text=Inserisci il nuovo valore di questo campo
-dialog.pointedit.changevalue.title=Edita il campo
+dialog.pointedit.changevalue.title=Modifica il campo
 dialog.pointnameedit.name=Nome del waypoint
 dialog.pointnameedit.uppercase=MAIUSCOLE
 dialog.pointnameedit.lowercase=minuscole
@@ -207,6 +236,7 @@ dialog.saveexif.photostatus.connected=Collegata
 dialog.saveexif.photostatus.disconnected=Scollegata
 dialog.saveexif.photostatus.modified=Modificata
 dialog.saveexif.overwrite=Sovrascrivi il file
+dialog.saveexif.force=Ignora errori minori
 dialog.charts.xaxis=Asse X
 dialog.charts.yaxis=Asse Y
 dialog.charts.output=Output
@@ -221,15 +251,33 @@ dialog.distances.column.from=Dal punto
 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.setmapbg.mapnik=Mapnik (default)
-dialog.setmapbg.osma=Osma
-dialog.setmapbg.cyclemap=Mappa ciclistica
-dialog.setmapbg.other=Altro
-dialog.setmapbg.server=URL server
+dialog.fullrangedetails.intro=Qui i dettagli della selezione
+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
+dialog.addmapsource.layer1url=URL del primo layer
+dialog.addmapsource.layer2url=URL opzionale del secondo layer
+dialog.addmapsource.maxzoom=Massimo livello di zoom
+dialog.addmapsource.cloudstyle=Stile numero
+dialog.addmapsource.noname=Senza nome
 dialog.gpsies.column.name=Nome traccia
 dialog.gpsies.column.length=Lunghezza
 dialog.gpsies.description=Descrizione
 dialog.gpsies.nodescription=Senza descrizione
+dialog.gpsies.nonefound=Nessuna traccia trovata
+dialog.gpsies.username=Gpsies username
+dialog.gpsies.password=Gpsies password
+dialog.gpsies.keepprivate=Rendi la traccia privata
+dialog.gpsies.confirmopenpage=Apri la pagina web per caricare la traccia?
+dialog.gpsies.activities=Attivit\u00e0
+dialog.gpsies.activity.trekking=Trekking
+dialog.gpsies.activity.walking=Camminare
+dialog.gpsies.activity.jogging=Jogging
+dialog.gpsies.activity.biking=Ciclismo
+dialog.gpsies.activity.motorbiking=Motocicletta
+dialog.gpsies.activity.snowshoe=Trekking sulla neve
+dialog.gpsies.activity.sailing=Navigazione
+dialog.gpsies.activity.skating=Pattinaggio
 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.photoselect.intro=Selezione una delle foto correlate da usare come scarto dell'orario
@@ -252,6 +300,12 @@ dialog.correlate.options.nodistancelimit=Nessun limite di distanza
 dialog.correlate.options.distancelimit=Distanza limite
 dialog.correlate.options.correlate=Correlate
 dialog.correlate.alloutsiderange=Tutte le foto sono fuori dall'orario della traccia, e nessuna pu\u00f2 essere correlata.\nProva a cambiare lo scarto o correla manualmente almeno una foto.
+dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei punti foto
+dialog.rearrangephotos.tostart=Sposta all'inizio
+dialog.rearrangephotos.toend=Sposta alla fine
+dialog.rearrangephotos.nosort=Non mettere in ordine
+dialog.rearrangephotos.sortbyfilename=Metti in ordine di nome del file
+dialog.rearrangephotos.sortbytime=Metti in ordine di tempo
 dialog.compress.nonefound=Nessun punto rimosso
 dialog.compress.closepoints.title=Cancella punti vicini
 dialog.compress.closepoints.paramdesc=Fattore vicinanza
@@ -261,6 +315,9 @@ dialog.compress.singletons.title=Cancella solitari
 dialog.compress.singletons.paramdesc=Fattore distanza
 dialog.compress.duplicates.title=Cancella duplicati
 dialog.compress.summarylabel=Punti da cancellare
+dialog.pastecoordinates.desc=Inserisci o incolla qui le coordinate
+dialog.pastecoordinates.coords=Coordinate
+dialog.pastecoordinates.nothingfound=Per favore, controlla le coordinate e riprova
 dialog.help.help=Per favore vedi\n http://activityworkshop.net/software/prune/\nper maggiori informazioni e per la guida utente.
 dialog.about.version=Versione
 dialog.about.build=Build
@@ -277,6 +334,11 @@ dialog.about.systeminfo.povray=Povray installato
 dialog.about.systeminfo.exiftool=Exiftool installato
 dialog.about.systeminfo.gpsbabel=Gpsbabel installato
 dialog.about.systeminfo.gnuplot=Gnuplot installato
+dialog.about.systeminfo.exiflib=Libreria exif
+dialog.about.systeminfo.exiflib.internal=Interna
+dialog.about.systeminfo.exiflib.internal.failed=Interna (non trovata)
+dialog.about.systeminfo.exiflib.external=Esterna
+dialog.about.systeminfo.exiflib.external.failed=Esterna (non trovata)
 dialog.about.yes=S\u00ec
 dialog.about.no=No
 dialog.about.credits=Crediti
@@ -298,10 +360,13 @@ dialog.checkversion.releasedate2=.
 dialog.checkversion.download=Per scaricare la nuova versione vai a http://activityworkshop.net/software/prune/download.html.
 dialog.keys.intro=Puoi utilizzare i seguenti tast di scelta rapida al posto del mouse
 dialog.keys.keylist=<table><tr><td>Tasti freccia</td><td>Muovi mappa destra, sinistra, su, giu'</td></tr><tr><td>Ctrl + freccia destra, sinistra</td><td>Selezione punto successivo o precedente</td></tr><tr><td>Ctrl + freccia su, giu'</td><td>Zoom in o out</td></tr><tr><td>Del</td><td>Cancella punto attuale</td></tr></table>
+dialog.keys.normalmodifier=Ctrl
+dialog.keys.macmodifier=Comando
 dialog.saveconfig.desc=Le configurazioni seguenti possono essere salvati in un file di configurazione:
 dialog.saveconfig.prune.trackdirectory=Cartella traccie
 dialog.saveconfig.prune.photodirectory=Cartella foto
-dialog.saveconfig.prune.languagecode=Codice linguaggio (EN)
+dialog.saveconfig.prune.languagecode=Codice lingua (IT)
+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
@@ -309,22 +374,51 @@ 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
-dialog.saveconfig.prune.mapserverindex=Indice server mappe
-dialog.saveconfig.prune.mapserverurl=URL del server mappe
+dialog.saveconfig.prune.mapsource=Selezionale la fonte delle mappe
+dialog.saveconfig.prune.mapsourcelist=Fonte delle mappe
+dialog.saveconfig.prune.diskcache=Cache delle mappe
 dialog.saveconfig.prune.kmzimagewidth=larghezza immagine KMZ
 dialog.saveconfig.prune.kmzimageheight=altezza immagine KMZ
+dialog.saveconfig.prune.colourscheme=Schema colori
+dialog.saveconfig.prune.kmltrackcolour=Colore della traccia KML
 dialog.setpaths.intro=Se necessario, puoi indicare il percorso delle applicazioni esterne:
+dialog.setpaths.found=trovato?
 dialog.addaltitude.noaltitudes=L'intervallo selezionato non contiene altitudini
 dialog.addaltitude.desc=Scarto altitudine da aggiungere
+dialog.lookupsrtm.overwritezeros=Sovrascrivere le quote pari a zero?
+dialog.setcolours.intro=Fai clic su un riquadro per cambiare il colore
+dialog.setcolours.background=Sfondo
+dialog.setcolours.borders=Bordi
+dialog.setcolours.lines=Linee
+dialog.setcolours.primary=Primario
+dialog.setcolours.secondary=Secondario
+dialog.setcolours.point=Punti
+dialog.setcolours.selection=Selezione
+dialog.setcolours.text=Testo
+dialog.colourchooser.title=Scegli colore
+dialog.colourchooser.red=Rosso
+dialog.colourchooser.green=Verde
+dialog.colourchooser.blue=Blu
+dialog.setlanguage.firstintro=Puoi selezionare una delle lingue incluse,<p>oppure selezionare un file di testo.
+dialog.setlanguage.secondintro=Devi salvare le impostazioni e<p> riavviare Prune per cambiare la lingua.
+dialog.setlanguage.language=Lingua
+dialog.setlanguage.languagefile=File della lingua
+dialog.setlanguage.endmessage=Ora salva le tue preferenze e riavvia Prune\n per rendere effettivo il cambio di lingua
+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.deletefieldvalues.intro=Selezione il campo da cancellare dall'intervallo corrente
 
 # 3d window
 dialog.3d.title=Visione Prune in 3D
 dialog.3d.altitudecap=Intervallo altitudine minimo
+dialog.3d.altitudefactor=Fattore di moltiplicazione della quota
 dialog.3dlines.title=Griglia di Prune
 dialog.3dlines.empty=Nessuna griglia mostrata!
 dialog.3dlines.intro=Queste sono le linee della griglia per la visione 3D
 
-# Confirm messages || These are displayed as confirmation in the status bar
+# Confirm messages
 confirm.loadfile=Dati caricati da file
 confirm.save.ok1=Salvati con successo
 confirm.save.ok2=punti nel file
@@ -336,7 +430,9 @@ confirm.reverserange=Intervallo invertito
 confirm.addtimeoffset=Scarto temporale aggiunto
 confirm.addaltitudeoffset=Scarto altitudine aggiunto
 confirm.rearrangewaypoints=Waypoint riorganizzati
+confirm.rearrangephotos=Foto riorganizzate
 confirm.cutandmove=Selezione spostata
+confirm.convertnamestotimes=Nome del waypoint convertito
 confirm.saveexif.ok1=Salvato
 confirm.saveexif.ok2=foto
 confirm.undo.single=operazione annullate
@@ -348,9 +444,13 @@ confirm.photo.disconnect=foto scollegata
 confirm.correlate.single=foto era correlata
 confirm.correlate.multi=foto erano correlate
 confirm.createpoint=punto creato
+confirm.rotatephoto=foto ruotata
 confirm.running=Operazione in corso...
+confirm.lookupsrtm1=Trovato
+confirm.lookupsrtm2=valori di quota
+confirm.deletefieldvalues=Valori del campo cancellati
 
-# Buttons || These are all the texts for buttons
+# Buttons
 button.ok=OK
 button.back=Precedente
 button.next=Prossimo
@@ -373,23 +473,29 @@ button.selectall=Seleziona tutto
 button.selectnone=Deseleziona tutto
 button.preview=Anteprima
 button.load=Carica
+button.upload=Caricato
 button.guessfields=Campi soluzione
 button.showwebpage=Mostra pagina
 button.check=Controlla
+button.resettodefaults=Ripristina predefinito
+button.browse=Sfoglia...
+button.addnew=Aggiungi nuovo
+button.delete=Cancella
 
 # File types
 filetype.txt=File TXT
 filetype.jpeg=File JPG
 filetype.kmlkmz=File KML, KMZ
 filetype.kml=File KML
-filetype.kmz=FIle KMZ
+filetype.kmz=File KMZ
 filetype.gpx=File GPX
 filetype.pov=File POV
 filetype.svg=File SVG
 
-# Display components || These are all for the side panels showing point/range details
+# Display components
 display.nodata=Nessun dato caricato
 display.noaltitudes=I dati della traccia non includono l'altitudine
+display.notimestamps=La traccia non include dati temporali
 details.trackdetails=Dettagli della traccia
 details.notrack=Nessuna traccia caricata
 details.track.points=Punti
@@ -415,7 +521,10 @@ 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
+details.range.gradient=Gradiente
 details.waypointsphotos.waypoints=Waypoint
 details.waypointsphotos.photos=Foto
 details.photodetails=Dettagli foto
@@ -471,7 +580,7 @@ cardinal.s=S
 cardinal.e=E
 cardinal.w=O
 
-# 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=carica dati
 undo.loadphotos=carica foto
 undo.editpoint=edita punto
@@ -489,7 +598,12 @@ undo.cutandmove=muovi selezione
 undo.connectphoto=collega foto
 undo.disconnectphoto=scollega foto
 undo.correlate=correla foto
+undo.rearrangephotos=riorganizza foto
 undo.createpoint=crea punto
+undo.rotatephoto=ruota foto
+undo.convertnamestotimes=converti nomi in orari
+undo.lookupsrtm=cerca quote in SRTM
+undo.deletefieldvalues=cancellare i valori del campo
 
 # Error messages
 error.save.dialogtitle=Errore nel salvataggio dati
@@ -498,6 +612,10 @@ error.save.failed=Fallito il tentativo di salvare i dati nel file
 error.saveexif.filenotfound=Non trovato un file di foto
 error.saveexif.cannotoverwrite1=File di foto
 error.saveexif.cannotoverwrite2=\u00e8 in sola lettura e non pu\u00f2 essere sovrascritto. Creo una copia?
+error.saveexif.failed1=Salvataggio fallito
+error.saveexif.failed2=delle immagini
+error.saveexif.forced1=
+error.saveexif.forced2=delle immagini richiede forzatura
 error.load.dialogtitle=Errore nel caricamento dati
 error.load.noread=Non posso leggere il file
 error.load.nopoints=Non ci sono coordinate nel file
@@ -509,13 +627,21 @@ error.jpegload.nofilesfound=File non trovato
 error.jpegload.nojpegsfound=File jpeg non trovato
 error.jpegload.noexiffound=Informazioni EXIF non trovate
 error.jpegload.nogpsfound=Informazioni GPS non trovate
+error.jpegload.exifreadfailed=Lettera dei dati EXIF fallita. I dati EXIF non possono\n essere letti senza una libreria interna o esterna.
+error.gpsload.unknown=Errore sconosciuto
 error.undofailed.title=Impossibile annullare
 error.undofailed.text=Impossibile annullare l'operazione
 error.function.noop.title=La funzione non ha avuto effetto
-error.rearrange.noop=La riorganizzazione dei waypoint non ha avuto effetto
+error.rearrange.noop=La riorganizzazione dei punto non ha avuto effetto
 error.function.notavailable.title=Funzione non disponibile
 error.function.nojava3d=Questa funzione richiede la libreria Java3d,\ndisponibile all'indirizzo Sun.com.
 error.3d=\u00c8 avvenuto un errore nella visualizzazione 3D
 error.readme.notfound=Non ho trovato il file Leggimi
 error.osmimage.dialogtitle=Errore nel caricamento nelle immagini della mappa
 error.osmimage.failed=Impossibile caricare le immagini della mappa. Per favore verifica la connessione a internet.
+error.language.wrongfile=Il file selezionato non sembra essere un file di lingua per Prune
+error.convertnamestotimes.nonames=Nomi non convertibili in orari
+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
index ee54e3dbd2aa592d6e9f49494b530790905be842..0c0d5eb4243b1e1fe94a638d5852279e2b67e491 100644 (file)
@@ -229,6 +229,14 @@ 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.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.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.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
diff --git a/tim/prune/lang/prune-texts_nl.properties b/tim/prune/lang/prune-texts_nl.properties
new file mode 100644 (file)
index 0000000..f7b7578
--- /dev/null
@@ -0,0 +1,646 @@
+# Text entries for the Prune application
+# Dutch entries as extra
+
+# Menu entries
+menu.file=Bestand
+menu.file.addphotos=Foto's toevoegen
+menu.file.save=Opslaan als tekst
+menu.file.exit=Afsluiten
+menu.track=Route
+menu.track.undo=Ongedaan maken
+menu.track.clearundo=Ongedaan-maken lijst wissen
+menu.track.deletemarked=Verwijderen gemarkeerde punten
+menu.track.rearrange=Rangschikken waypoints
+menu.track.rearrange.start=Alles naar begin route
+menu.track.rearrange.end=Alles naar einde route
+menu.track.rearrange.nearest=Alles naar dichtstbijzijnde routepunt
+menu.range=Reeks
+menu.range.all=Selecteer alles
+menu.range.none=Selecteer geen
+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
+menu.range.cutandmove=Knip en plak selectie
+menu.point=Punt
+menu.point.editpoint=Wijzig punt
+menu.point.deletepoint=Verwijder punt
+menu.photo=Foto
+menu.photo.saveexif=Opslaan naar Exif
+menu.photo.connect=Aan punt vastzetten
+menu.photo.disconnect=Van punt losmaken
+menu.photo.delete=Verwijder foto
+menu.view=Bekijken
+menu.view.showsidebars=Toon panelen
+menu.view.browser=Kaart in browser
+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=Instellingen
+menu.settings.onlinemode=Kaarten van internet ophalen
+menu.help=Help
+# Popup menu for map
+menu.map.zoomin=Zoom +
+menu.map.zoomout=Zoom -
+menu.map.zoomfull=Zoom alles
+menu.map.newpoint=Maak nieuw punt
+menu.map.connect=Verbind route punten
+menu.map.autopan=Autopan
+menu.map.showmap=Toon kaart
+menu.map.showscalebar=Toon schaal
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=R
+altkey.menu.range=E
+altkey.menu.point=P
+altkey.menu.view=B
+altkey.menu.photo=F
+altkey.menu.settings=I
+altkey.menu.help=H
+
+# Ctrl shortcuts for menu items
+shortcut.menu.file.open=O
+shortcut.menu.file.load=L
+shortcut.menu.file.save=S
+shortcut.menu.track.undo=Z
+shortcut.menu.edit.compress=C
+shortcut.menu.range.all=A
+shortcut.menu.help.help=H
+
+# Functions
+function.open=Open bestand
+function.loadfromgps=Gegevens lezen van GPS
+function.sendtogps=Gegevens verzenden naar GPS
+function.exportkml=Export KML
+function.exportgpx=Export GPX
+function.exportpov=Export POV
+function.exportsvg=Export SVG
+function.editwaypointname=Hernoem waypoint
+function.compress=Route comprimeren
+function.addtimeoffset=Tijdsverschil toevoegen
+function.addaltitudeoffset=Hoogteverschil toevoegen
+function.convertnamestotimes=Converteer waypointnamen naar tijden
+function.deletefieldvalues=Verwijder veldwaarden
+function.findwaypoint=Zoek waypoint
+function.pastecoordinates=Nieuwe co\u00f6rdinaten ingeven
+function.charts=Diagram
+function.show3d=3D beeld
+function.distances=Afstanden
+function.fullrangedetails=Reeks details
+function.setmapbg=Instellen kaart achtergrond
+function.setkmzimagesize=Instellen KMZ afbeelding grootte
+function.setpaths=Instellen programmapaden
+function.getgpsies=Routes van Gpsies ophalen
+function.uploadgpsies=Upload routes naar Gpsies
+function.lookupsrtm=Hoogtes van SRTM ophalen
+function.duplicatepoint=Dupliceer punt
+function.setcolours=Instellen kleuren
+function.setlanguage=Instellen taal
+function.correlatephotos=Correleer foto's
+function.rearrangephotos=Foto's herschikken
+function.rotatephotoleft=Roteer foto linksom
+function.rotatephotoright=Roteer foto rechtsom
+function.ignoreexifthumb=Negeer exif thumbnail
+function.help=Help
+function.showkeys=Toon sneltoetsen
+function.about=Over Prune
+function.checkversion=Controleer op nieuwe versie
+function.saveconfig=Instellingen opslaan
+function.diskcache=Kaart opslaan op schijf
+
+# Dialogs
+dialog.exit.confirm.title=Prune afsluiten
+dialog.exit.confirm.text=Uw data is niet opgeslagen. Weet u zeker dat u wilt afsluiten?
+dialog.openappend.title=Toevoegen aan bestaande data
+dialog.openappend.text=Wilt u deze data toevoegen aan de reeds geladen data?
+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.openoptions.title=Instellingen openen
+dialog.openoptions.filesnippet=Samenvatting van bestand
+dialog.load.table.field=Veld
+dialog.load.table.datatype=Datatype
+dialog.load.table.description=Omschrijving
+dialog.delimiter.label=Scheidingsteken
+dialog.delimiter.comma=Komma ,
+dialog.delimiter.tab=Tab
+dialog.delimiter.space=Spatie
+dialog.delimiter.semicolon=Puntkomma ;
+dialog.delimiter.other=Overig
+dialog.openoptions.deliminfo.records=bestanden, met
+dialog.openoptions.deliminfo.fields=velden
+dialog.openoptions.deliminfo.norecords=Geen bestanden
+dialog.openoptions.altitudeunits=Hoogte eenheden
+dialog.open.contentsdoubled=Dit bestand bevat twee kopie\u00ebn van ieder punt,\neenkeer als als waypoint en eenkeer als punt
+dialog.selecttracks.intro=Selecteer route of routes om te laden
+dialog.selecttracks.noname=Onbenoemd
+dialog.jpegload.subdirectories=Submappen meenemen
+dialog.jpegload.loadjpegswithoutcoords=Foto's zonder co\u00f6rdinaten meenemen
+dialog.jpegload.loadjpegsoutsidearea=Foto's buiten huidige gebied meenemen
+dialog.jpegload.progress.title=Foto's laden
+dialog.jpegload.progress=Foto's worden gezocht, even geduld aub.
+dialog.gpsload.nogpsbabel=GPSBabel niet gevonden. Doorgaan?
+dialog.gpsload.device=Naam van het apparaat
+dialog.gpsload.format=Formaat
+dialog.gpsload.getwaypoints=Waypoints laden
+dialog.gpsload.gettracks=Routes laden
+dialog.gpsload.save=Opslaan naar bestand
+dialog.gpssend.sendwaypoints=Verstuur waypoint
+dialog.gpssend.sendtracks=Verstuur routes
+dialog.gpssend.trackname=Routenaam
+dialog.saveoptions.title=Bestand opslaan
+dialog.save.fieldstosave=Velden op te slaan
+dialog.save.table.field=Veld
+dialog.save.table.hasdata=Bevat data
+dialog.save.table.save=Opslaan
+dialog.save.headerrow=Ook kopregel opslaan
+dialog.save.coordinateunits=Eenheid co\u00f6rdinaten
+dialog.save.altitudeunits=Eenheid hoogten
+dialog.save.timestampformat=Formaat tijdnotatie
+dialog.save.overwrite.title=Bestand bestaat reeds
+dialog.save.overwrite.text=Bestand bestaat reeds. Weet u zeker dat u wilt overschrijven?
+dialog.save.notypesselected=Geen punttypen geselecteerd
+dialog.exportkml.text=Titel voor de data
+dialog.exportkml.altitude=Absolute hoogten (voor luchtvaart)
+dialog.exportkml.kmz=Comprimeren voor kmz bestand
+dialog.exportkml.exportimages=Exporteer thumbnails naar kzm
+dialog.exportkml.trackcolour=Routekleur
+dialog.exportgpx.name=Naam
+dialog.exportgpx.desc=Omschrijving
+dialog.exportgpx.includetimestamps=Tijden meenemen
+dialog.exportgpx.copysource=Kopieer bron xml
+dialog.exportpov.text=Geef parameters voor POV export
+dialog.exportpov.font=Lettertype
+dialog.exportpov.camerax=Camera X
+dialog.exportpov.cameray=Camera Y
+dialog.exportpov.cameraz=Camera Z
+dialog.exportpov.modelstyle=Model stijl
+dialog.exportpov.ballsandsticks=Balletjes en stokjes
+dialog.exportpov.tubesandwalls=Tubes en muren
+dialog.exportpov.warningtracksize=Deze route heeft een groot aantal punten. Java3D kan deze mogelijk niet tonen.\nWeet u zeker dat u door wilt gaan?
+dialog.exportsvg.text=Selecteer de camera hoeken voor SVG export
+dialog.exportsvg.phi=Azimut hoek \u03d5
+dialog.exportsvg.theta=Stijgingshoek \u03b8
+dialog.exportsvg.gradients=Gebruik gradaties voor schaduw
+dialog.pointtype.desc=Sla de volgende punttypen op:
+dialog.pointtype.track=Routepunten
+dialog.pointtype.waypoint=Waypoints
+dialog.pointtype.photo=Fotopunten
+dialog.pointtype.selection=Alleen selectie
+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.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.
+dialog.undo.none.text=Geen acties om ongedaan te maken
+dialog.clearundo.title=Ongedaan-maken lijst wissen
+dialog.clearundo.text=Weet u zeker dat u de ongedaan-maken lijst wilt wissen?\nAlle informatie zal verloren gaan!
+dialog.pointedit.title=Wijzig punt
+dialog.pointedit.text=Selecteer ieder te wijzigen veld en gebruik de "Wijzigen" knop om de waarden aan te passen.
+dialog.pointedit.table.field=Veld
+dialog.pointedit.table.value=Waarde
+dialog.pointedit.table.changed=Gewijzigd
+dialog.pointedit.changevalue.text=Geef de nieuwe waarde voor dit veld
+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.addtimeoffset.add=Tijd toevoegen
+dialog.addtimeoffset.subtract=Tijd aftrekken
+dialog.addtimeoffset.days=Dagen
+dialog.addtimeoffset.hours=Uren
+dialog.addtimeoffset.minutes=Minuten
+dialog.addtimeoffset.notimestamps=Kan geen tijdsverschil aan deze selectie toevoegen, omdat deze geen tijdinformatie bevat
+dialog.findwaypoint.intro=Voer een deel van de waypoint naam in
+dialog.findwaypoint.search=Zoeken
+dialog.saveexif.title=Opslaan Exif
+dialog.saveexif.intro=Selecteer de foto's die u wilt opslaan door deze aan te vinken
+dialog.saveexif.nothingtosave=Co\u00f6rdinaten zijn ongewijzigd, niets om op te slaan
+dialog.saveexif.noexiftool=Exiftool niet gevonden. Doorgaan?
+dialog.saveexif.table.photoname=Fotonaam
+dialog.saveexif.table.status=Status
+dialog.saveexif.table.save=Opslaan
+dialog.saveexif.photostatus.connected=Verbonden
+dialog.saveexif.photostatus.disconnected=Ontkoppeld
+dialog.saveexif.photostatus.modified=Aangepast
+dialog.saveexif.overwrite=Bestanden overschrijven
+dialog.saveexif.force=Negeer kleine fouten
+dialog.charts.xaxis=X-as
+dialog.charts.yaxis=Y-as
+dialog.charts.output=Output
+dialog.charts.screen=Output naar scherm
+dialog.charts.svg=Output naar SVG bestand
+dialog.charts.svgwidth=SVG breedte
+dialog.charts.svgheight=SVG hoogte
+dialog.charts.needaltitudeortimes=De route moet of hoogte- of tijd-informatie bevatten om een diagram te kunnen maken
+dialog.charts.gnuplotnotfound=Kon gnuplot niet vinden op het aangegeven pad
+dialog.distances.intro=Hemelsbrede afstand tussen punten
+dialog.distances.column.from=Van punt
+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.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
+dialog.addmapsource.layer1url=URL van de eerste laag
+dialog.addmapsource.layer2url=URL van de tweede laag (optioneel)
+dialog.addmapsource.maxzoom=Maximaal zoom-niveau
+dialog.addmapsource.cloudstyle=Stijl nummer
+dialog.addmapsource.noname=Onbenoemd
+dialog.gpsies.column.name=Routenaam
+dialog.gpsies.column.length=Lengte
+dialog.gpsies.description=Omschrijving
+dialog.gpsies.nodescription=Geen omschrivjing
+dialog.gpsies.nonefound=Geen routes gevonden
+dialog.gpsies.username=Gpsies gebruikersnaam
+dialog.gpsies.password=Gpsies wachtwoord
+dialog.gpsies.keepprivate=Houd route priv\u00e9
+dialog.gpsies.confirmopenpage=Webpagina van de ge\u00fcploade route openen?
+dialog.gpsies.activities=Aktiviteit
+dialog.gpsies.activity.trekking=Trekking
+dialog.gpsies.activity.walking=Lopen
+dialog.gpsies.activity.jogging=Hardlopen
+dialog.gpsies.activity.biking=Fietsen
+dialog.gpsies.activity.motorbiking=Motor
+dialog.gpsies.activity.snowshoe=Sneeuwschoen
+dialog.gpsies.activity.sailing=Zeilen
+dialog.gpsies.activity.skating=Skating
+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.photoselect.intro=Selecteer \u00e9\u00e9n van deze gekoppelde foto's om het tijdsverschil te gebruiken
+dialog.correlate.photoselect.photoname=Fotonaam
+dialog.correlate.photoselect.timediff=Tijdsverschil
+dialog.correlate.photoselect.photolater=Foto later
+dialog.correlate.options.tip=Tip: Door handmatig een foto te koppelen kan het tijdsverschil voor u berekend worden.
+dialog.correlate.options.intro=Selecteer de opties voor automatisch koppelen
+dialog.correlate.options.offsetpanel=Tijdverschil
+dialog.correlate.options.offset=Verschil
+dialog.correlate.options.offset.hours=uren,
+dialog.correlate.options.offset.minutes=minuten en
+dialog.correlate.options.offset.seconds=seconden
+dialog.correlate.options.photolater=Foto later dan punt
+dialog.correlate.options.pointlater=Punt later dan foto
+dialog.correlate.options.limitspanel=Koppelingslimieten
+dialog.correlate.options.notimelimit=Geen tijdslimiet
+dialog.correlate.options.timelimit=Tijdslimiet
+dialog.correlate.options.nodistancelimit=Geen afstandlimiet
+dialog.correlate.options.distancelimit=Afstandlimeit
+dialog.correlate.options.correlate=Koppelen
+dialog.correlate.alloutsiderange=Alle foto's zijn buiten het bereik van de route en kunnen dus niet gekoppeld worden.\nPas het tijdsverschil aan, of maak minimaal \u00e9\u00e9n koppeling handmatig.
+dialog.rearrangephotos.desc=Selecteer doel en sorteervolgorde van de foto punten
+dialog.rearrangephotos.tostart=Naar begin
+dialog.rearrangephotos.toend=Naar einde
+dialog.rearrangephotos.nosort=Niet sorteren
+dialog.rearrangephotos.sortbyfilename=Sorteren op bestandsnaam
+dialog.rearrangephotos.sortbytime=Sorteren op tijd
+dialog.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
+dialog.compress.wackypoints.paramdesc=Afstandfactor
+dialog.compress.singletons.title=Singletons verwijderen
+dialog.compress.singletons.paramdesc=Afstandfactor
+dialog.compress.duplicates.title=Verwijderen duplicaten
+dialog.compress.summarylabel=Te verwijderen punten
+dialog.pastecoordinates.desc=Geef co\u00f6rdinaten in
+dialog.pastecoordinates.coords=Co\u00f6rdinaten
+dialog.pastecoordinates.nothingfound=Controleer de co\u00f6rdinaten en probeer het nogmaals
+dialog.help.help=Ga naar\n http://activityworkshop.net/software/prune/\nvoor meer informatie en handleidingen.
+dialog.about.version=Versie
+dialog.about.build=Build
+dialog.about.summarytext1=Prune is een programma voor het laden, tonen en wijzigen van data uit GPS ontvangers.
+dialog.about.summarytext2=Uitgebracht onder de Gnu GPL voor gratis, open, wereldwijd gebruik en verbetering<br>Kopieren, verspreiding en aanpassing is toegestaan en aangemoedigd<br>volgens de voorwaarden in het bestand <code>license.txt</code>.
+dialog.about.summarytext3=Ga naar <code style="font-weight:bold">http://activityworkshop.net/</code> voor meer informatie en handleidingen.
+dialog.about.languages=Beschikbare talen
+dialog.about.translatedby=Nederlandse vertaling door JFO
+dialog.about.systeminfo=Systeeminformatie
+dialog.about.systeminfo.os=Besturingssysteem
+dialog.about.systeminfo.java=Java Runtime
+dialog.about.systeminfo.java3d=Java3d ge\u00efnstalleerd
+dialog.about.systeminfo.povray=Povray ge\u00efnstalleerd
+dialog.about.systeminfo.exiftool=Exiftool ge\u00efnstalleer
+dialog.about.systeminfo.gpsbabel=Gpsbabel ge\u00efnstalleer
+dialog.about.systeminfo.gnuplot=Gnuplot ge\u00efnstalleer
+dialog.about.systeminfo.exiflib=Exif bibliotheek
+dialog.about.systeminfo.exiflib.internal=Intern
+dialog.about.systeminfo.exiflib.internal.failed=Intern (niet gevonden)
+dialog.about.systeminfo.exiflib.external=Extern
+dialog.about.systeminfo.exiflib.external.failed=Extern (niet gevonden)
+dialog.about.yes=Ja
+dialog.about.no=Nee
+dialog.about.credits=Credits
+dialog.about.credits.code=Prune code geschreven door
+dialog.about.credits.exifcode=Exif code door
+dialog.about.credits.icons=Sommige iconen overgenomen van
+dialog.about.credits.translators=Vertalers
+dialog.about.credits.translations=Vertaling geholpen door
+dialog.about.credits.devtools=Ontwikkeltool
+dialog.about.credits.othertools=Overige tools
+dialog.about.credits.thanks=Dank aan
+dialog.about.readme=Leesmij
+dialog.checkversion.error=Versienummer kon niet worden gecontroleerd.\nControleer de internetverbinding.
+dialog.checkversion.uptodate=U gebruikt de laatste versie van Prune.
+dialog.checkversion.newversion1=Een nieuwe versie van Prune is beschikbaar. De nieuwste versie is nu versie
+dialog.checkversion.newversion2=.
+dialog.checkversion.releasedate1=Deze nieuwe versie was vrijgegeven op
+dialog.checkversion.releasedate2=.
+dialog.checkversion.download=Om de nieuwst versie te downloaden, ga naar http://activityworkshop.net/software/prune/download.html.
+dialog.keys.intro=De volgende sneltoetsen vervangen muisacties
+dialog.keys.keylist=<table><tr><td>Pijltjestoetsen</td><td>verschuif de kaart links, rechts, omhoog, omlaag</td></tr><tr><td>Ctrl + pijltje naar links, rechts</td><td>Selecteer volgende, vorige punt</td></tr><tr><td>Ctrl + pijltje omhoog, omlaag</td><td>Zoom in of uit</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Selecteer vorig, volgend segment</td></tr><tr><td>Ctrl + Home, End</td><td>Select eerste, laatste punt</td></tr><tr><td>Del</td><td>Verwijder huidige punt</td></tr></table>
+dialog.keys.normalmodifier=Ctrl
+dialog.keys.macmodifier=Command
+dialog.saveconfig.desc=De volgende instellingen kunnen worden opgeslagen in een configuratiebestand:
+dialog.saveconfig.prune.trackdirectory=Route map
+dialog.saveconfig.prune.photodirectory=Foto map
+dialog.saveconfig.prune.languagecode=Taalcode (NL)
+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
+dialog.saveconfig.prune.mapsource=Geselecteerde kaartbron
+dialog.saveconfig.prune.mapsourcelist=Kaartbronnen
+dialog.saveconfig.prune.diskcache=Kaartcache
+dialog.saveconfig.prune.kmzimagewidth=KMZ afbeelding breedte
+dialog.saveconfig.prune.kmzimageheight=KMZ afbeelding hoogte
+dialog.saveconfig.prune.colourscheme=Kleurenschema
+dialog.saveconfig.prune.kmltrackcolour=KML routekleur
+dialog.setpaths.intro=Indien nodig kan je de paden naar externe applicaties kiezen:
+dialog.setpaths.found=Pad gevonden?
+dialog.addaltitude.noaltitudes=De geselecteerde reeks bevat geen hoogtes
+dialog.addaltitude.desc=Toe te voegen hoogteverschil
+dialog.lookupsrtm.overwritezeros=Hoogtewaarde 0 overschijven?
+dialog.setcolours.intro=Klik op een kleur om de kleur te wijzigen
+dialog.setcolours.background=Achtergrond
+dialog.setcolours.borders=Randen
+dialog.setcolours.lines=Lijnen
+dialog.setcolours.primary=Primair
+dialog.setcolours.secondary=Secondair
+dialog.setcolours.point=Punten
+dialog.setcolours.selection=Selectie
+dialog.setcolours.text=Tekst
+dialog.colourchooser.title=Selecteer een kleur
+dialog.colourchooser.red=Rood
+dialog.colourchooser.green=Groen
+dialog.colourchooser.blue=Blauw
+dialog.setlanguage.firstintro=U kunt kiezen uit \u00e9\u00e9n van de meegeleverde talen,<p>of selecteer een tekstbestand.
+dialog.setlanguage.secondintro=U dient uw instellingen op te slaan en<p>Prune te herstarten om de taal te kunnen wijzigen
+dialog.setlanguage.language=Taal
+dialog.setlanguage.languagefile=Taal bestand
+dialog.setlanguage.endmessage=Sla nu uw instellingen op en herstart Prune\nom de taalwijziging te activeren
+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.deletefieldvalues.intro=Selecteer het te verwijderen veld voor de huidige reeks
+
+# 3d window
+dialog.3d.title=Prune in 3D
+dialog.3d.altitudecap=Minimale hoogte
+dialog.3d.altitudefactor=Hoogte overdrijvingsfactor
+dialog.3dlines.title=Prune raster
+dialog.3dlines.empty=Geen raster om af te beelden
+dialog.3dlines.intro=Dit is het raster voor 3D
+
+# Confirm messages
+confirm.loadfile=Data van schijf geladen
+confirm.save.ok1=Succesvol opgeslagen
+confirm.save.ok2=Punten naar bestand
+confirm.deletepoint.single=punt was verwijderd
+confirm.deletepoint.multi=punten waren verwijderd
+confirm.point.edit=punt gewijzigd
+confirm.mergetracksegments=Route segmenten samengevoegd
+confirm.reverserange=Reeks ge\u00efnverteerd
+confirm.addtimeoffset=Tijdsverschil toegevoegd
+confirm.addaltitudeoffset=Hoogteverschil toegevoegd
+confirm.rearrangewaypoints=Waypoints herschikt
+confirm.rearrangephotos=Foto herschikt
+confirm.cutandmove=Selectie verplaatst
+confirm.convertnamestotimes=Namen waypoint geconverteerd
+confirm.saveexif.ok1=Opgeslagen
+confirm.saveexif.ok2=foto bestanden
+confirm.undo.single=Actie geannuleerd
+confirm.undo.multi=Acties geannuleerd
+confirm.jpegload.single=foto was toegevoegd
+confirm.jpegload.multi=foto's waren toegevoegd
+confirm.photo.connect=foto's gekoppeld
+confirm.photo.disconnect=foto's ontkoppeld
+confirm.correlate.single=Foto was gecorreleerd
+confirm.correlate.multi=Foto's waren gecorreleerd
+confirm.createpoint=punt aangemaakt
+confirm.rotatephoto=foto rgeroteerd
+confirm.running=Bezig...
+confirm.lookupsrtm1=Gevonden
+confirm.lookupsrtm2=hoote waarden
+confirm.deletefieldvalues=Veldwaarden gewist
+
+# Buttons
+button.ok=OK
+button.back=Terug
+button.next=Volgende
+button.finish=Einde
+button.cancel=Annuleren
+button.overwrite=Overschrijven
+button.moveup=Omhoog
+button.movedown=Omlaag
+button.showlines=Toon raster
+button.edit=Wijzigen
+button.exit=Afsluiten
+button.close=Sluiten
+button.continue=Doorgaan
+button.yes=Ja
+button.no=Nee
+button.yestoall=Ja op alles
+button.notoall=Nee op alles
+button.select=Selecteer
+button.selectall=Selecteer alles
+button.selectnone=Selecteer niets
+button.preview=Voorbeeld
+button.load=Laden
+button.guessfields=Raad velden
+button.showwebpage=Toon webpagina
+button.check=Controleren
+button.resettodefaults=Terug naar beginwaarden
+button.browse=Browse...
+button.addnew=Nieuwe toevoegen
+button.delete=Verwijderen
+
+# File types
+filetype.txt=TXT bestand
+filetype.jpeg=JPG bestand
+filetype.kmlkmz=KML, KMZ bestand
+filetype.kml=KML bestand
+filetype.kmz=KMZ bestand
+filetype.gpx=GPX bestand
+filetype.pov=POV bestand
+filetype.svg=SVG bestand
+
+# Display components
+display.nodata=Geen gegevens geladen
+display.noaltitudes=Route gegevens bevatten geen hoogte
+display.notimestamps=Route gegevens bevatten geen tijdinformatie
+details.trackdetails=Route details
+details.notrack=Geen route geladen
+details.track.points=Punten
+details.track.file=Bestand
+details.track.numfiles=Aantal bestanden
+details.pointdetails=Puntdetails
+details.index.selected=Index
+details.index.of=van
+details.nopointselection=Geen punt geselecteerd
+details.photofile=Fotobestand
+details.norangeselection=Geen reeks geselecteerd
+details.rangedetails=Reeksdetails
+details.range.selected=Geselecteerd
+details.range.to=tot
+details.altitude.to=tot
+details.range.climb=Stijgen
+details.range.descent=Dalen
+details.coordformat=Co\u00f6rdinatenformaat
+details.distanceunits=Lengte eenheid
+display.range.time.secs=s
+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
+details.range.gradient=Helling
+details.waypointsphotos.waypoints=Waypoints
+details.waypointsphotos.photos=Foto
+details.photodetails=Foto details
+details.nophoto=Geen foto geselecteerd
+details.photo.loading=Bezig met laden
+details.photo.connected=Geconnecteerd
+map.overzoom=Geen kaarten beschikbaar op dit zoom-niveau
+
+# Field names
+fieldname.latitude=Breedtegraad
+fieldname.longitude=Lengtegraad
+fieldname.altitude=Hoogte
+fieldname.timestamp=Tijd
+fieldname.time=Tijd
+fieldname.waypointname=Naam
+fieldname.waypointtype=Type
+fieldname.newsegment=Segment
+fieldname.custom=Custom
+fieldname.prefix=Veld
+fieldname.distance=Afstand
+fieldname.movingdistance=Snelheid in beweging
+fieldname.duration=Duur
+fieldname.speed=Snelheid
+fieldname.verticalspeed=Verticale snelheid
+
+# Measurement units
+units.original=Oorspronkelijke
+units.default=Default
+units.metres=Meters
+units.metres.short=m
+units.feet=Voet
+units.feet.short=ft
+units.kilometres=Kilometers
+units.kilometres.short=km
+units.kmh=km/u
+units.miles=Mijlen
+units.miles.short=mi
+units.mph=mph
+units.metrespersec=m/s
+units.feetpersec=ft/s
+units.hours=uren
+units.degminsec=Grd-min-sec
+units.degmin=Grd-min
+units.deg=Graden
+units.iso8601=ISO 8601
+
+# External urls
+url.googlemaps=maps.google.nl
+
+# Cardinals for 3d plots
+cardinal.n=N
+cardinal.s=Z
+cardinal.e=O
+cardinal.w=W
+
+# Undo operations
+undo.load=gegevens laden
+undo.loadphotos=foto's laden
+undo.editpoint=wijzigen punt
+undo.deletepoint=verwijderen punt
+undo.deletephoto=verwijderen foto
+undo.deleterange=verwijderen reeks
+undo.compress=comprimeren route
+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.connectphoto=Koppel foto
+undo.disconnectphoto=Loskoppelen foto
+undo.correlate=Correleer foto
+undo.rearrangephotos=Herschikken foto's
+undo.createpoint=Cre\u00eber punt
+undo.rotatephoto=Roteer foto
+undo.convertnamestotimes=Converteer namen naar tijden
+undo.lookupsrtm=Opzoeken hoogtes in SRTM
+undo.deletefieldvalues=Verwijder veldwaarden
+
+# Error messages
+error.save.dialogtitle=Fout bij opslaan gegevens
+error.save.nodata=Geen gegevens om op te slaan
+error.save.failed=Kon de gegevens niet naar bestand wegschrijven
+error.saveexif.filenotfound=Fotobestand niet gevonden
+error.saveexif.cannotoverwrite1=Fotobestand
+error.saveexif.cannotoverwrite2=is alleen-lezen en kan niet worden overschreven. Wegschrijven naar kopie?
+error.saveexif.failed1=Kon
+error.saveexif.failed2=van de foto's niet wegschrijven
+error.saveexif.forced1=
+error.saveexif.forced2=van de foto's hadden "forcing" nodig
+error.load.dialogtitle=Fout gegevens laden
+error.load.noread=Kan bestand niet lezen
+error.load.nopoints=Geen co\u00f6rdinaat informatie gevonden in bestand
+error.load.unknownxml=Onbekend xml-formaat:
+error.load.noxmlinzip=Geen xml-bestand gevonden in zipbestand
+error.load.othererror=Fout bij lezen bestand:
+error.jpegload.dialogtitle=Fout bij inlezen foto's
+error.jpegload.nofilesfound=Bestanden niet gevonden
+error.jpegload.nojpegsfound=Geen jpeg-bestanden gevonden
+error.jpegload.noexiffound=Geen EXIF informatie gevonden
+error.jpegload.nogpsfound=Geen GPS informatie gevonden
+error.jpegload.exifreadfailed=Kon geen EXIF informatie inlezen. EXIF informatie kan niet worden gelezen\n zonder interne of externe bibliotheek.
+error.gpsload.unknown=Onbekende fout
+error.undofailed.title=Terugdraaien mislukt
+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.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
+error.osmimage.failed=Fout bij inlezen kaart afbeeldingen. Controleer je internet verbinding
+error.language.wrongfile=Het geselecteerde bestand is vermoedelijk geen taalbestand voor Prune
+error.convertnamestotimes.nonames=Namen konden niet naar tijden worden geconverteerd
+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
index b0456302544d7f7697776dffa447f5f71a25a1c0..e218589a3083a85b0f8e74060551acde19efb854 100644 (file)
@@ -255,6 +255,15 @@ dialog.gpsies.column.length=D\u0142ugo\u015b\u0107
 dialog.gpsies.description=Opis
 dialog.gpsies.nodescription=Brak opisu
 dialog.gpsies.nonefound=Nie znalaz\u0142em \u015bcie\u017cek
+dialog.gpsies.activities=Aktywno\u015b\u0107
+dialog.gpsies.activity.trekking=W\u0119drowa\u0107
+dialog.gpsies.activity.walking=Walking
+dialog.gpsies.activity.jogging=Jogging
+dialog.gpsies.activity.biking=Wycieczka rowerowa
+dialog.gpsies.activity.motorbiking=Motocykl
+dialog.gpsies.activity.snowshoe=Snowshoeing
+dialog.gpsies.activity.sailing=Sailing
+dialog.gpsies.activity.skating=Skating
 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.photoselect.intro=Wybierz jedno z powi\u0105zanych zdj\u0119\u0107 i u\u017cyj go jako wzorca do przesuni\u0119cia czasu
@@ -612,4 +621,4 @@ error.osmimage.dialogtitle=B\u0142\u0105d przy \u0142adowaniu obraz\u00f3w map
 error.osmimage.failed=B\u0142\u0105d przy \u0142adowaniu obraz\u00f3w map. Sprawd\u017a po\u0142\u0105czenie z internetem.
 error.language.wrongfile=Wybrany plik nie jest plikiem z t\u0142umaczeniem dla Prune
 error.convertnamestotimes.nonames=\u017badne nazwy nie mog\u0142y zosta\u0107 zmienione na czas
-error.lookupsrtm.none=Nie znaleziono danych o wysoko\u015bci.
+error.lookupsrtm.nonefound=Nie znaleziono danych o wysoko\u015bci.
index 6010a38ed1541c1a93d835f416a6054ef0239900..0549635485eb11f1a067c7bf0b73a655c7f8f536 100644 (file)
@@ -9,25 +9,25 @@ menu.file.exit=Sair
 menu.track=Track
 menu.track.undo=Desfazer
 menu.track.clearundo=Limpar lista de desfazer
-menu.point.editpoint=Editar ponto
-menu.point.deletepoint=Remover ponto
-menu.range.deleterange=Remover intervalo
 menu.track.deletemarked=Remover pontos marcados
-menu.range.interpolate=Interpolar
-menu.range.average=Sele\u00e7\u00e3o m\u00e9dia
-menu.range.reverse=Reverter intervalo
-menu.range.mergetracksegments=Mesclar trechos da rota
 menu.track.rearrange=Rearrumar pontos
 menu.track.rearrange.start=Tudo para o in\u00edcio do arquivo
 menu.track.rearrange.end=Tudo para o fim do arquivo
 menu.track.rearrange.nearest=Cada um para o ponto da rota mais pr\u00f3ximo
-menu.range.cutandmove=Recortar e mover sele\u00e7\u00e3o
 menu.range=Intervalo
-menu.point=Ponto
 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
+menu.range.average=Sele\u00e7\u00e3o m\u00e9dia
+menu.range.reverse=Reverter intervalo
+menu.range.mergetracksegments=Mesclar trechos da rota
+menu.range.cutandmove=Recortar e mover sele\u00e7\u00e3o
+menu.point=Ponto
+menu.point.editpoint=Editar ponto
+menu.point.deletepoint=Remover ponto
 menu.photo=Foto
 menu.photo.saveexif=Salvar para Exif
 menu.photo.connect=Conectar ao ponto
@@ -41,6 +41,7 @@ menu.view.browser.mapquest=Mapas do Mapquest
 menu.view.browser.yahoo=Mapas do Yahoo
 menu.view.browser.bing=Mapas do Bing
 menu.settings=Configura\u00e7\u00f5es
+menu.settings.onlinemode=Carregar mapas da Internet
 menu.help=Ajuda
 # Popup menu for map
 menu.map.zoomin=Ampliar
@@ -93,6 +94,7 @@ function.setmapbg=Definir como fundo do mapa
 function.setkmzimagesize=Definir tamanho da imagem KMZ
 function.setpaths=Definir caminhos do programa
 function.getgpsies=Obter rotas Gpsies
+function.lookupsrtm=Obter altitudes a partir do SRTM
 function.duplicatepoint=Duplicar ponto
 function.setcolours=Definir cores
 function.setlanguage=Definir idioma
@@ -106,6 +108,7 @@ function.showkeys=Mostrar atalhos de teclado
 function.about=Sobre o Prune
 function.checkversion=Verificar novas vers\u00f5es
 function.saveconfig=Salvar configura\u00e7\u00f5es
+function.diskcache=Salvar mapas para o disco
 
 # Dialogs
 dialog.exit.confirm.title=Sair do Prune
@@ -131,6 +134,7 @@ dialog.openoptions.deliminfo.records=registros, com
 dialog.openoptions.deliminfo.fields=campos
 dialog.openoptions.deliminfo.norecords=Sem registros
 dialog.openoptions.altitudeunits=Unidades de altitude
+dialog.open.contentsdoubled=Este arquivo cont\u00e9m duas c\u00f3pias de cada ponto,\nsendo uma como ponto normal e uma como ponto de rota.
 dialog.jpegload.subdirectories=Incluir subpastas
 dialog.jpegload.loadjpegswithoutcoords=Incluir fotos sem coordenadas
 dialog.jpegload.loadjpegsoutsidearea=Incluir fotos fora da \u00e1rea atual
@@ -238,16 +242,27 @@ dialog.distances.column.to=Para o ponto
 dialog.distances.currentpoint=Ponto atual
 dialog.distances.toofewpoints=Esta fun\u00e7\u00e3o precisa de pontos para calcular a dist\u00e3ncia entre eles
 dialog.fullrangedetails.intro=Aqui est\u00e3o os detalhes para o intervalo selecionado
-dialog.setmapbg.mapnik=Mapnik (padr\u00e3o)
-dialog.setmapbg.osma=Osma
-dialog.setmapbg.cyclemap=Cyclemap
-dialog.setmapbg.other=Outro
-dialog.setmapbg.server=URL do Servidor
+dialog.setmapbg.intro=Selecione uma das fontes de mapas ou adicione uma nova
+dialog.addmapsource.title=Adicionar nova fonte de mapas
+dialog.addmapsource.sourcename=Nome da fonte
+dialog.addmapsource.layer1url=URL da primeira camada
+dialog.addmapsource.layer2url=URL opcional da segunda camada
+dialog.addmapsource.maxzoom=N\u00edvel de amplia\u00e7\u00e3o m\u00e1ximo
+dialog.addmapsource.cloudstyle=N\u00famero do estilo
+dialog.addmapsource.noname=Sem nome
 dialog.gpsies.column.name=Nome da rota
 dialog.gpsies.column.length=Extens\u00e3o
 dialog.gpsies.description=Descri\u00e7\u00e3o
 dialog.gpsies.nodescription=Sem descri\u00e7\u00e3o
 dialog.gpsies.nonefound=Nenhuma rota encontrada
+dialog.gpsies.activity.trekking=Trilha
+dialog.gpsies.activity.walking=Caminhada
+dialog.gpsies.activity.jogging=Corrida
+dialog.gpsies.activity.biking=Ciclismo
+dialog.gpsies.activity.motorbiking=Motocross
+dialog.gpsies.activity.snowshoe=Snowshoeing
+dialog.gpsies.activity.sailing=Sailing
+dialog.gpsies.activity.skating=Patina\u00e7\u00e3o
 dialog.correlate.notimestamps=N\u00e3o existem data-hora nos dados dos pontos, assim n\u00e3o h\u00e1 nada para correlacionar com as fotos
 dialog.correlate.nouncorrelatedphotos=Existem fotos n\u00e3o correlacionadas.\nVoc\u00ea tem certeza que deseja continuar?
 dialog.correlate.photoselect.intro=Selecione uma destas fotos correlacionadas para usar como diferen\u00e7a de tempo
@@ -295,7 +310,7 @@ dialog.about.summarytext1=Prune \u00e9 um programa para carregar, exibir e edita
 dialog.about.summarytext2=Isto est\u00e1 lan\u00e7ado sob a Gnu GPL para uso e melhoria livre, aberto e em todo o mundo.<br>A c\u00f3pia, redistribui\u00e7\u00e3o e modifica\u00e7\u00e3o s\u00e3o permitidas e encorajadas<br>de acordo coma as condi\u00e7\u00f5es no arquivo <code>license.txt</code>inclu\u00eddo.
 dialog.about.summarytext3=Por favor, veja <code style="font-weight:bold">http://activityworkshop.net/</code> para mais informa\u00e7\u00f5es e guia do usu\u00e1rio.
 dialog.about.languages=Idiomas dispon\u00edveis
-dialog.about.translatedby=Texto em portugu\u00eas por amigo anónimo.
+dialog.about.translatedby=Texto em portugu\u00eas por Marcus Gama.
 dialog.about.systeminfo=Informa\u00e7\u00f5es do sistema
 dialog.about.systeminfo.os=Sistema Operacional
 dialog.about.systeminfo.java=M\u00f3dulo Java
@@ -304,6 +319,11 @@ dialog.about.systeminfo.povray=Povray instalado
 dialog.about.systeminfo.exiftool=Exittool instalado
 dialog.about.systeminfo.gpsbabel=Gpsbabel instalado
 dialog.about.systeminfo.gnuplot=Gnuplot instalado
+dialog.about.systeminfo.exiflib=Biblioteca do Exif
+dialog.about.systeminfo.exiflib.internal=Interna
+dialog.about.systeminfo.exiflib.internal.failed=Interna (n\u00e3o encontrada)
+dialog.about.systeminfo.exiflib.external=Externa
+dialog.about.systeminfo.exiflib.external.failed=Externa (n\u00e3o encontrada)
 dialog.about.yes=Sim
 dialog.about.no=N\u00e3o
 dialog.about.credits=Cr\u00e9ditos
@@ -325,6 +345,8 @@ dialog.checkversion.releasedate2=.
 dialog.checkversion.download=Para baixar a nova vers\u00e3o, v\u00e1 para http://activityworkshop.net/software/prune/download.html.
 dialog.keys.intro=Voc\u00ea pode usar os seguintes atalhos de teclado ao inv\u00e9s de usar o mouse
 dialog.keys.keylist=<table><tr><td>Cursores</td><td>Move o mapa para esquerda, direita, acima e abaixo</td></tr><tr><td>Ctrl + cursores esquerdo e direito</td><td>Seleciona o pr\u00f3ximo ponto ou o anterior</td></tr><tr><td>Ctrl + cursores acima e abaixo</td><td>Amplia ou reduz</td></tr><tr><td>Del</td><td>Remove o ponto atual</td></tr></table>
+dialog.keys.normalmodifier=Ctrl
+dialog.keys.macmodifier=Command
 dialog.saveconfig.desc=As seguintes configura\u00e7\u00f5es podem ser salvas para um arquivo de configura\u00e7\u00e3o.
 dialog.saveconfig.prune.trackdirectory=Pasta de rotas
 dialog.saveconfig.prune.photodirectory=Pasta de fotos
@@ -337,13 +359,15 @@ 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
-dialog.saveconfig.prune.mapserverindex=\u00cdndice do servidor de mapas
-dialog.saveconfig.prune.mapserverurl=URL do servidor de mapas
+dialog.saveconfig.prune.mapsource=Selecionar fonte de mapas
+dialog.saveconfig.prune.mapsourcelist=Fontes de mapas
+dialog.saveconfig.prune.diskcache=Cache de mapas
 dialog.saveconfig.prune.kmzimagewidth=Largura da imagem KMZ
 dialog.saveconfig.prune.kmzimageheight=Altura da imagem KMZ
 dialog.saveconfig.prune.colourscheme=Esquema de cores
 dialog.saveconfig.prune.kmltrackcolour=Cor da rota KML
 dialog.setpaths.intro=Se voc\u00ea precisar, voc\u00ea pode escolher os caminhos para as aplica\u00e7\u00f5es externas:
+dialog.setpaths.found=Caminho encontrado?
 dialog.addaltitude.noaltitudes=O intervalo selecionado n\u00e3o cont\u00e9m altitudes
 dialog.addaltitude.desc=Diferen\u00e7a de altitude a adicionar
 dialog.setcolours.intro=Clique em um trecho de cor para mudar a cor
@@ -364,6 +388,10 @@ dialog.setlanguage.secondintro=Voc\u00ea precisa salvar suas configura\u00e7\u00
 dialog.setlanguage.language=Idioma
 dialog.setlanguage.languagefile=Arquivo de idioma
 dialog.setlanguage.endmessage=Agora salve suas configura\u00e7\u00f5es e reinicie o Prune\npara que a mudan\u00e7a de idioma ocorra.
+dialog.diskcache.save=Salvar imagens do mapa para o disco
+dialog.diskcache.dir=Diret\u00f3rio da cache
+dialog.diskcache.createdir=Criar diret\u00f3rio
+dialog.diskcache.nocreate=Diret\u00f3rio da cache n\u00e3o foi criado
 
 # 3d window
 dialog.3d.title=Vista 3D do Prune
@@ -400,6 +428,8 @@ confirm.correlate.multi=fotos foram correlacionadas
 confirm.createpoint=ponto criado
 confirm.rotatephoto=foto rotacionada
 confirm.running=Rodando...
+confirm.lookupsrtm1=Encontrado
+confirm.lookupsrtm2=valores de altitude
 
 # Buttons || These are all the texts for buttons
 button.ok=Ok
@@ -429,6 +459,8 @@ button.showwebpage=Mostrar p\u00e1gina Web
 button.check=Verificar
 button.resettodefaults=Restaurar para os padr\u00f5es
 button.browse=Navegar...
+button.addnew=Adicionar novo
+button.delete=Remover
 
 # File types
 filetype.txt=Arquivos TXT
@@ -443,6 +475,7 @@ filetype.svg=Arquivos SVG
 # Display components || These are all for the side panels showing point/range details
 display.nodata=Nenhum dado carregado
 display.noaltitudes=Dados da rota n\u00e3o incluem altitudes
+display.notimestamps=Dados da rota n\u00e3o incluem data-hora
 details.trackdetails=Detalhes da track
 details.notrack=Nenhuma rota carrgeada
 details.track.points=Pontos
@@ -468,6 +501,7 @@ 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
 details.range.gradient=Gradiente
@@ -518,7 +552,7 @@ units.deg=Graus
 units.iso8601=ISO 8601
 
 # External urls
-url.googlemaps=maps.google.pt
+url.googlemaps=maps.google.com.br
 
 # Cardinals for 3d plots
 cardinal.n=N
@@ -548,6 +582,7 @@ undo.rearrangephotos=rearrumar fotos
 undo.createpoint=criar ponto
 undo.rotatephoto=rotacionar foto
 undo.convertnamestotimes=converter nomes para tempos
+undo.lookupsrtm=procurar altitudes a partir do STRM
 
 # Error messages
 error.save.dialogtitle=Erro ao salvar dados
@@ -571,6 +606,7 @@ error.jpegload.nofilesfound=Nenhum arquivo encontrado
 error.jpegload.nojpegsfound=Nenhum arquivo jpeg encontrado
 error.jpegload.noexiffound=Nenhuma informa\u00e7\u00e3o EXIF encontrada
 error.jpegload.nogpsfound=Nenhuma informa\u00e7\u00e3o de GPS encontrada
+error.jpegload.exifreadfailed=Falha ao ler informa\u00e7\u00f5es do EXIF. Nenhuma informa\u00e7\u00e3o do EXIF pode ser lida\nseja na biblioteca interna, seja na externa.
 error.gpsload.unknown=Erro desconhecido
 error.undofailed.title=Falha ao desfazer
 error.undofailed.text=Falha para desfazer opera\u00e7\u00e3o
@@ -584,3 +620,4 @@ error.osmimage.dialogtitle=Erro ao carregar imagens do mapa
 error.osmimage.failed=Falha ao carregar imagens do mapa. Por favor, verifique a conex\u00e3o \u00e0 Internet.
 error.language.wrongfile=O arquivo selecionado n\u00e3o parece ser um arquivo de idioma do Prune
 error.convertnamestotimes.nonames=Nenhum nome pode ser convertido para tempo
+error.lookupsrtm.nonefound=Nenhum valor de altitude encontrado
index 59fa48f217cb05cd9d2dba4b73ba45dac704c28e..d000f5d68f1d3e13d7297dac90b4608c738ece74 100644 (file)
@@ -216,6 +216,13 @@ dialog.gpsies.column.length=Uzunlu\u011fu
 dialog.gpsies.description=A\u00e7\u0131klama
 dialog.gpsies.nodescription=A\u00e7\u0131klama yok
 dialog.gpsies.nonefound=Herhangi bir yol bulunmad\u0131
+dialog.gpsies.activities=Etkinlik
+dialog.gpsies.activity.trekking=Y\u00fcr\u00fcy\u00fc\u015f
+dialog.gpsies.activity.walking=Y\u00fcr\u00fcme
+dialog.gpsies.activity.jogging=Ko\u015fma
+dialog.gpsies.activity.biking=Bisiklet
+dialog.gpsies.activity.sailing=Yelken
+dialog.gpsies.activity.skating=Paten
 dialog.correlate.photoselect.photoname=Foto ad\u0131
 dialog.correlate.photoselect.photolater=Foto sonra
 dialog.correlate.options.offset.hours=saat,
index 5bf076411332602d99a8d0710afe569259dc11ba..760423a55402f2c954a3c5410416b396f499ba90 100644 (file)
@@ -6,25 +6,28 @@ menu.file=\u6587\u4ef6
 menu.file.addphotos=\u6dfb\u52a0\u76f8\u7247
 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.point.editpoint=\u7f16\u8f91\u8f68\u8ff9\u70b9
-menu.point.deletepoint=\u5220\u9664\u8f68\u8ff9\u70b9
-menu.range.deleterange=\u5220\u9664\u8f68\u8ff9\u70b9\u6bb5
 menu.track.deletemarked=\u5220\u9664\u5df2\u6807\u793a\u8f68\u8ff9\u70b9
-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.mergetracksegments=\u5408\u5e76\u8f68\u8ff9\u6bb5
 menu.track.rearrange=\u822a\u70b9\u91cd\u7f6e
 menu.track.rearrange.start=\u81f3\u8d77\u59cb\u4f4d\u7f6e
 menu.track.rearrange.end=\u81f3\u672b\u4f4d\u7f6e
 menu.track.rearrange.nearest=\u81f3\u6700\u8fd1\u8f68\u8ff9\u70b9
-menu.range.cutandmove=\u79fb\u52a8
+menu.range=\u822a\u6bb5
 menu.range.all=\u5168\u9009
 menu.range.none=\u64a4\u9500\u9009\u62e9
 menu.range.start=\u8bbe\u7f6e\u8d77\u70b9
 menu.range.end=\u8bbe\u7f6e\u672b\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.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.saveexif=\u5750\u6807\u4fdd\u5b58\u81f3Exif
 menu.photo.connect=\u94fe\u63a5\u76f8\u7247
@@ -36,7 +39,9 @@ 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.settings=\u8bbe\u7f6e
+menu.settings.onlinemode=\u4ece\u7f51\u4e0a\u5bfc\u5165\u5730\u56fe
 menu.help=\u5e2e\u52a9
 # Popup menu for map
 menu.map.zoomin=\u653e\u5927
@@ -59,20 +64,32 @@ function.editwaypointname=\u7f16\u8f91\u822a\u70b9\u540d
 function.compress=\u538b\u7f29\u8f68\u8ff9(\u6807\u793a\u8981\u5220\u9664\u822a\u70b9\uff09
 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.findwaypoint=\u67e5\u627e\u822a\u70b9
+function.pastecoordinates=\u8f93\u5165\u65b0\u5750\u6807
 function.charts=\u9ad8\u5ea6\u901f\u5ea6\u56fe\u8868
 function.show3d=3-D\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.lookupsrtm=\u4eceSRTM\u83b7\u5f97\u9ad8\u5ea6\u4fe1\u606f
+function.duplicatepoint=\u590d\u5236\u70b9
+function.setcolours=\u8bbe\u7f6e\u989c\u8272
+function.setlanguage=\u8bbe\u7f6e\u8bed\u8a00
 function.correlatephotos=\u94fe\u63a5\u76f8\u7247
+function.rearrangephotos=\u91cd\u6392\u76f8\u7247
+function.rotatephotoleft=\u5de6\u65cb\u8f6c
+function.rotatephotoright=\u53f3\u65cb\u8f6c
+function.ignoreexifthumb=\u5ffd\u7565Exif\u7f29\u7565\u56fe
 function.help=\u5e2e\u52a9
 function.showkeys=\u663e\u793a\u5feb\u6377\u952e
 function.about=\u5173\u4e8ePrune
 function.checkversion=\u68c0\u67e5\u66f4\u65b0
 function.saveconfig=\u4fdd\u5b58\u8bbe\u7f6e
+function.diskcache=\u4fdd\u5b58\u5730\u56fe
 
 # Dialogs
 dialog.exit.confirm.title=\u9000\u51fa
@@ -98,6 +115,7 @@ dialog.openoptions.deliminfo.records=\u6761\u8bb0\u5f55\uff0c
 dialog.openoptions.deliminfo.fields=\u6570\u636e\u6bb5
 dialog.openoptions.deliminfo.norecords=\u65e0\u7eaa\u5f55
 dialog.openoptions.altitudeunits=\u9ad8\u5ea6\u5355\u4f4d
+dialog.open.contentsdoubled=\u6587\u4ef6\u542b\u6709\u4e24\u5957\u70b9\u4fe1\u606f\uff0c\u4e00\u5957\u822a\u70b9\u4fe1\u606f\u548c\u4e00\u5957\u8f68\u8ff9\u70b9\u4fe1\u606f
 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
@@ -108,6 +126,7 @@ dialog.gpsload.device=GPS\u7aef\u53e3\u540d\u79f0
 dialog.gpsload.format=GPS\u6587\u4ef6\u683c\u5f0f
 dialog.gpsload.getwaypoints=\u5bfc\u5165\u822a\u70b9
 dialog.gpsload.gettracks=\u5bfc\u5165\u8f68\u8ff9
+dialog.gpsload.save=\u4fdd\u5b58\u5230\u6587\u4ef6
 dialog.gpssend.sendwaypoints=\u53d1\u9001\u822a\u70b9
 dialog.gpssend.sendtracks=\u53d1\u9001\u8f68\u8ff9
 dialog.gpssend.trackname=\u8f68\u8ff9\u540d
@@ -122,13 +141,16 @@ dialog.save.altitudeunits=\u9ad8\u5ea6\u5355\u4f4d
 dialog.save.timestampformat=\u65f6\u95f4\u683c\u5f0f
 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.kmz=\u538b\u7f29\u6210KMZ\u6587\u4ef6
 dialog.exportkml.exportimages=\u8f93\u51fa\u76f8\u7247\u7d22\u5f15\u56fe\u81f3KMZ
+dialog.exportkml.trackcolour=\u8f68\u8ff9\u989c\u8272
 dialog.exportgpx.name=\u540d\u79f0
 dialog.exportgpx.desc=\u63cf\u8ff0
 dialog.exportgpx.includetimestamps=\u5305\u542b\u65f6\u95f4
+dialog.exportgpx.copysource=\u4ece\u6e90XML\u6587\u4ef6\u590d\u5236
 dialog.exportpov.text=\u8bf7\u8f93\u5165POV\u53c2\u6570
 dialog.exportpov.font=\u5b57\u4f53
 dialog.exportpov.camerax=X\u76f8\u673a
@@ -142,6 +164,7 @@ 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.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\n\u662f\u5426\u7ee7\u7eed\uff1f
 dialog.confirmcutandmove.title=\u786e\u8ba4\u526a\u5207\u548c\u79fb\u52a8
@@ -184,6 +207,7 @@ dialog.saveexif.photostatus.connected=\u5df2\u94fe\u63a5
 dialog.saveexif.photostatus.disconnected=\u672a\u94fe\u63a5
 dialog.saveexif.photostatus.modified=\u5df2\u6539\u53d8
 dialog.saveexif.overwrite=\u8986\u76d6\u6587\u4ef6
+dialog.saveexif.force=\u5f3a\u5236\u6267\u884c\u5ffd\u7565\u9519\u8bef
 dialog.charts.xaxis=X\u8f74
 dialog.charts.yaxis=Y\u8f74
 dialog.charts.output=\u8f93\u51fa
@@ -198,15 +222,29 @@ dialog.distances.column.from=\u4ece\u6b64\u70b9
 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.setmapbg.mapnik=Mapnik(\u7f3a\u7701)
-dialog.setmapbg.osma=Osma
-dialog.setmapbg.cyclemap=Cyclemap
-dialog.setmapbg.other=\u5176\u4ed6(\u5728\u4e0b\u9762\u8f93\u5165URL)
-dialog.setmapbg.server=\u5730\u56fe\u670d\u52a1\u5668URL
+dialog.fullrangedetails.intro=\u822a\u6bb5\u8be6\u60c5
+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
+dialog.addmapsource.layer1url=\u7b2c\u4e00\u5c42URL
+dialog.addmapsource.layer2url=\u53ef\u9009\u7b2c\u4e8c\u5c42URL
+dialog.addmapsource.maxzoom=\u6700\u5927\u7f29\u653e\u7ea7\u6570
+dialog.addmapsource.cloudstyle=\u7c7b\u578b\u53f7
+dialog.addmapsource.noname=\u672a\u547d\u540d
 dialog.gpsies.column.name=\u8f68\u8ff9\u540d\u79f0
 dialog.gpsies.column.length=\u957f\u5ea6
 dialog.gpsies.description=\u63cf\u8ff0
 dialog.gpsies.nodescription=\u65e0\u63cf\u8ff0
+dialog.gpsies.nonefound=\u672a\u627e\u5230\u8f68\u8ff9
+dialog.gpsies.activities=\u6d3b\u52a8,\u9002\u5408
+dialog.gpsies.activity.trekking=\u5f92\u6b65
+dialog.gpsies.activity.walking=\u7ade\u8d70
+dialog.gpsies.activity.jogging=\u8dd1\u6b65
+dialog.gpsies.activity.biking=\u9a91\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.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\n\u7ee7\u7eed\uff1f
 dialog.correlate.photoselect.intro=\u9009\u62e9\u5df2\u94fe\u63a5\u76f8\u7247\u4f5c\u4e3a\u65f6\u95f4\u504f\u79fb
@@ -229,6 +267,12 @@ dialog.correlate.options.nodistancelimit=\u65e0\u8ddd\u79bb\u9650\u5236
 dialog.correlate.options.distancelimit=\u8ddd\u79bb\u9650\u5236
 dialog.correlate.options.correlate=\u94fe\u63a5
 dialog.correlate.alloutsiderange=\u65e0\u6cd5\u94fe\u63a5\uff0c\u6240\u6709\u76f8\u7247\u8d85\u51fa\u8f68\u8ff9\u65f6\u95f4\u8303\u56f4\n\u8bf7\u6539\u53d8\u65f6\u95f4\u504f\u79fb\u6216\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u76f8\u7247
+dialog.rearrangephotos.desc=\u9009\u62e9\u76ee\u7684\u5730\u53ca\u6392\u76f8\u7247\u70b9
+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
@@ -238,6 +282,9 @@ dialog.compress.singletons.title=\u79bb\u6563\u70b9\u5220\u9664
 dialog.compress.singletons.paramdesc=\u8ddd\u79bb\u7cfb\u6570
 dialog.compress.duplicates.title=\u91cd\u590d\u70b9\u5220\u9664
 dialog.compress.summarylabel=\u8981\u5220\u9664\u7684\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
 dialog.help.help=\u66f4\u591a\u4fe1\u606f\u548c\u7528\u6cd5\uff0c\u8bf7\u53c2\u8003\u7f51\u7ad9\nhttp://activityworkshop.net/software/prune///
 dialog.about.version=\u7248\u672c
 dialog.about.build=Build
@@ -254,6 +301,11 @@ dialog.about.systeminfo.povray=Povray \u662f\u5426\u5b89\u88c5
 dialog.about.systeminfo.exiftool=Exiftool \u662f\u5426\u5b89\u88c5
 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.external=\u5916\u90e8
+dialog.about.systeminfo.exiflib.external.failed=\u5916\u90e8\uff08\u672a\u627e\u5230\uff09
 dialog.about.yes=\u662f
 dialog.about.no=\u5426
 dialog.about.credits=\u81f4\u8c22
@@ -274,10 +326,14 @@ dialog.checkversion.releasedate1=\u65b0\u7248\u672c\u53d1\u884c\u4e8e
 dialog.checkversion.releasedate2=
 dialog.checkversion.download=\u4e0b\u8f7d\u6700\u65b0\u7248\u672c\uff0c\u8bf7\u767b\u9646\u7f51\u7ad9\uff1a\nhttp://activityworkshop.net/software/prune/download.html
 dialog.keys.intro=\u53ef\u7528\u4e0b\u5217\u5feb\u6377\u952e\u66ff\u4ee3\u9f20\u6807
+dialog.keys.keylist=<table><tr><td>\u7bad\u5934</td><td>\u4e0a\u4e0b\u5de6\u53f3\u79fb\u52a8\u5730\u56fe</td></tr><tr><td>Ctrl + \u5de6\u53f3\u7bad\u5934</td><td>\u9009\u53d6\u524d\uff0c\u540e\u70b9</td></tr><tr><td>Ctrl + \u4e0a\u4e0b\u7bad\u5934</td><td>\u653e\u5927\u7f29\u5c0f</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>\u9009\u62e9\u524d\u540e\u6bb5</td></tr><tr><td>Ctrl + Home, End</td><td>\u9009\u62e9\u9996\u672b\u70b9</td></tr><tr><td>Del</td><td>\u5220\u9664\u5f53\u524d\u70b9</td></tr></table>
+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.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
@@ -285,13 +341,39 @@ 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.mapserverindex=\u80cc\u666f\u5730\u56fe\u7801(1-4)
-dialog.saveconfig.prune.mapserverurl=\u90094\u65f6\u5730\u56fe\u670d\u52a1\u5668URL
+dialog.saveconfig.prune.mapsource=\u5df2\u9009\u62e9\u7684\u5730\u56fe\u6e90
+dialog.saveconfig.prune.mapsourcelist=\u5730\u56fe\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
+dialog.saveconfig.prune.colourscheme=\u989c\u8272
+dialog.saveconfig.prune.kmltrackcolour=KML\u8f68\u8ff9\u989c\u8272
 dialog.setpaths.intro=\u82e5\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
+dialog.setcolours.intro=\u70b9\u6309\u8272\u677f\u4ee5\u6539\u53d8\u989c\u8272
+dialog.setcolours.background=\u80cc\u666f
+dialog.setcolours.borders=\u8fb9\u754c
+dialog.setcolours.lines=\u7ebf\u4f53
+dialog.setcolours.primary=\u4e3b\u8981
+dialog.setcolours.secondary=\u8f85\u52a9
+dialog.setcolours.point=\u70b9
+dialog.setcolours.selection=\u9009\u62e9
+dialog.setcolours.text=\u6587\u5b57
+dialog.colourchooser.title=\u8bf7\u9009\u62e9\u989c\u8272
+dialog.colourchooser.red=\u7ea2
+dialog.colourchooser.green=\u7eff
+dialog.colourchooser.blue=\u84dd
+dialog.setlanguage.firstintro=\u4f60\u53ef\u4ee5\u9009\u62e9\u5df2\u6709\u8bed\u8a00,<p>\u6216\u9009\u62e9\u5916\u6302\u8bed\u8a00\u5305
+dialog.setlanguage.secondintro=\u8bf7\u4fdd\u5b58\u8bbe\u7f6e<p>\u5e76\u91cd\u542fPrune\u4f7f\u8bbe\u7f6e\u751f\u6548
+dialog.setlanguage.language=\u8bed\u8a00
+dialog.setlanguage.languagefile=\u8bed\u8a00\u5305
+dialog.setlanguage.endmessage=\u73b0\u5728\u8bf7\u4fdd\u5b58\u8bbe\u7f6e\u5e76\u91cd\u542fPrune\n\u4f7f\u8bbe\u7f6e\u751f\u6548
+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
 
 # 3d window
 dialog.3d.title=Prune 3D \u663e\u793a
@@ -310,8 +392,11 @@ 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.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.cutandmove=\u5df2\u79fb\u52a8\u7684\u8f68\u8ff9\u6bb5
+confirm.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u5df2\u8f6c\u6362
 confirm.saveexif.ok1=\u5df2\u4fdd\u5b58
 confirm.saveexif.ok2=\u76f8\u7247\u6587\u4ef6
 confirm.undo.single=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c
@@ -323,7 +408,10 @@ confirm.photo.disconnect=\u76f8\u7247\u672a\u94fe\u63a5
 confirm.correlate.single=\u76f8\u7247\u5df2\u94fe\u63a5
 confirm.correlate.multi=\u76f8\u7247\u5df2\u94fe\u63a5
 confirm.createpoint=\u5df2\u521b\u5efa\u70b9
+confirm.rotatephoto=\u76f8\u7247\u5df2\u65cb\u8f6c
 confirm.running=\u8bf7\u7a0d\u7b49...
+confirm.lookupsrtm1=\u627e\u5230
+confirm.lookupsrtm2=\u9ad8\u5ea6\u503c
 
 # Buttons || These are all the texts for buttons
 button.ok=\u786e\u5b9a
@@ -351,6 +439,10 @@ button.load=\u5bfc\u5165
 button.guessfields=\u731c\u4f30\u533a\u57df\u5185\u5bb9
 button.showwebpage=\u663e\u793a\u7f51\u9875
 button.check=\u68c0\u67e5
+button.resettodefaults=\u6062\u590d\u9ed8\u8ba4
+button.browse=\u6d4f\u89c8...
+button.addnew=\u6dfb\u52a0
+button.delete=\u5220\u9664
 
 # File types
 filetype.txt=TXT\u6587\u4ef6
@@ -365,6 +457,7 @@ filetype.svg=SVG\u6587\u4ef6
 # Display components || These are all for the side panels showing point/range details
 display.nodata=\u65e0\u6570\u636e
 display.noaltitudes=\u8f68\u8ff9\u6570\u636e\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f
+display.notimestamps=\u8f68\u8ff9\u6570\u636e\u672a\u542b\u65f6\u95f4\u4fe1\u606f
 details.trackdetails=\u8f68\u8ff9\u4fe1\u606f
 details.notrack=\u65e0\u8f68\u8ff9
 details.track.points=\u8f68\u8ff9\u70b9
@@ -390,7 +483,10 @@ 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.waypointsphotos.waypoints=\u822a\u70b9
 details.waypointsphotos.photos=\u76f8\u7247
 details.photodetails=\u76f8\u7247\u4fe1\u606f
@@ -464,7 +560,11 @@ undo.cutandmove=\u79fb\u52a8\u6bb5
 undo.connectphoto=\u94fe\u63a5\u76f8\u7247
 undo.disconnectphoto=\u65ad\u5f00\u94fe\u63a5
 undo.correlate=\u94fe\u63a5\u76f8\u7247
+undo.rearrangephotos=\u91cd\u62cd\u76f8\u7247
 undo.createpoint=\u521b\u5efa\u8f68\u8ff9\u70b9
+undo.rotatephoto=\u65cb\u8f6c\u76f8\u7247
+undo.convertnamestotimes=\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4
+undo.lookupsrtm=\u4eceSRTM\u67e5\u627e\u9ad8\u5ea6
 
 # Error messages
 error.save.dialogtitle=\u4fdd\u5b58\u6570\u636e\u9519\u8bef
@@ -473,6 +573,10 @@ 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\uff0c\u4fdd\u5b58\u526f\u672c\uff1f
+error.saveexif.failed1=\u56fe\u7247
+error.saveexif.failed2=\u4fdd\u5b58\u5931\u8d25
+error.saveexif.forced1=
+error.saveexif.forced2=\u4e2a\u56fe\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
@@ -484,6 +588,8 @@ error.jpegload.nofilesfound=\u627e\u4e0d\u5230\u6587\u4ef6
 error.jpegload.nojpegsfound=\u627e\u4e0d\u5230Jpeg\u6587\u4ef6
 error.jpegload.noexiffound=\u627e\u4e0d\u5230EXIF\u4fe1\u606f
 error.jpegload.nogpsfound=\u627e\u4e0d\u5230GPS\u4fe1\u606f
+error.jpegload.exifreadfailed=Exif\u8bfb\u53d6\u9519\u8bef\u3002\u9700\u8981\u5185\u90e8\n\u6216\u8005\u5916\u90e8\u5e93\u624d\u80fd\u8bfb\u53d6
+error.gpsload.unknown=\u672a\u77e5\u9519\u8bef
 error.undofailed.title=\u64a4\u9500\u5931\u8d25
 error.undofailed.text=\u64a4\u9500\u64cd\u4f5c\u5931\u8d25
 error.function.noop.title=\u529f\u80fd\u65e0\u6548
@@ -494,3 +600,5 @@ error.3d=3D \u663e\u793a\u65f6\u51fa\u73b0\u4e00\u4e2a\u9519\u8bef
 error.readme.notfound=\u627e\u4e0d\u5230\u7248\u672c\u4fe1\u606f\u6587\u4ef6
 error.osmimage.dialogtitle=\u5bfc\u5165\u5730\u56fe\u65f6\u9519\u8bef
 error.osmimage.failed=\u5bfc\u5165\u5730\u56fe\u9519\u8bef\uff0c\u68c0\u67e5\u7f51\u7edc\u8fde\u63a5
+error.language.wrongfile=\u6240\u9009\u8bed\u8a00\u5305\u6587\u4ef6\u683c\u5f0f\u9519\u8bef
+error.convertnamestotimes.nonames=\u540d\u79f0\u65e0\u6cd5\u8f6c\u6362\u6210\u65f6\u95f4
index 6b29379eee308fb0b715a7a682fcab4b8477dcaf..eb40756b5ede82fbd2eae1ef1902d201e260063b 100644 (file)
@@ -300,7 +300,8 @@ public abstract class FieldGuesser
                {
                        String upperValue = inValue.toUpperCase();
                        // This is a header line so look for english or local text
-                       return upperValue.equals("SEGMENT");
+                       return upperValue.equals("SEGMENT")
+                               || upperValue.equals(I18nManager.getText("fieldname.newsegment").toUpperCase());
                }
                else
                {
index c0ca7549807b9f354176d9bb5793610a5275753c..2196e0c3ff29695a13ac72f8abdf3e8bf528dcea 100644 (file)
@@ -326,7 +326,8 @@ public class GpsLoader extends GenericFunction implements Runnable
 
                        // Send data back to app
                        _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(), Altitude.Format.METRES,
-                               new SourceInfo(_deviceField.getText(), SourceInfo.FILE_TYPE.GPSBABEL));
+                               new SourceInfo(_deviceField.getText(), SourceInfo.FILE_TYPE.GPSBABEL),
+                               handler.getTrackNameList());
                }
        }
 
index fabcb4dde5d6e7bfe14d849f18189ba2f8f1d006..feec754f22f7049dc7bcef4b26d71cba534d2ed2 100644 (file)
@@ -274,6 +274,9 @@ public class JpegLoader implements Runnable
                        if (timestamp == null && jpegData.getOriginalTimestamp() != null) {
                                timestamp = createTimestamp(jpegData.getOriginalTimestamp());
                        }
+                       if (timestamp == null && jpegData.getDigitizedTimestamp() != null) {
+                               timestamp = createTimestamp(jpegData.getDigitizedTimestamp());
+                       }
                        photo.setExifThumbnail(jpegData.getThumbnailImage());
                        // Also extract orientation tag for setting rotation state of photo
                        photo.setRotation(jpegData.getRequiredRotation());
index f72893dd0316cfa458a4c8db501cbb7584393e03..440a56f7411a177beaa31e2b63353146d5a09c5a 100644 (file)
@@ -8,6 +8,9 @@ import java.awt.FlowLayout;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
 import javax.swing.*;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
@@ -110,7 +113,14 @@ public class TextFileLoader
                {
                        _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.openoptions.title"), true);
                        _dialog.setLocationRelativeTo(_parentFrame);
-                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
+                       // add closing listener
+                       _dialog.addWindowListener(new WindowAdapter() {
+                               public void windowClosing(WindowEvent e) {
+                                       _dialog.dispose();
+                                       _app.informNoDataLoaded();
+                               }
+                       });
                        _dialog.getContentPane().add(makeDialogComponents());
 
                        // select best separator according to row counts (more is better)
diff --git a/tim/prune/load/TrackNameList.java b/tim/prune/load/TrackNameList.java
new file mode 100644 (file)
index 0000000..287e71d
--- /dev/null
@@ -0,0 +1,96 @@
+package tim.prune.load;
+
+import java.util.ArrayList;
+
+/**
+ * Class to hold and manage a list of track names
+ * from a gpx file, and report back which points
+ * belong to which track
+ */
+public class TrackNameList
+{
+       /** Current point number */
+       private int _pointNum = -1;
+       /** Current track number */
+       private int _trackNum = -1;
+       /** List of unique track names */
+       private ArrayList<String> _trackNames = new ArrayList<String>();
+       /** List of point numbers at the start of each track */
+       private ArrayList<Integer> _startIndices = new ArrayList<Integer>();
+
+
+       /**
+        * Inform list of a new point
+        * @param inTrackNum number of track, starting at zero
+        * @param inTrackName name of track, if any
+        * @param inIsTrackpoint true if point is a trackpoint
+        */
+       public void addPoint(int inTrackNum, String inTrackName, boolean inIsTrackpoint)
+       {
+               _pointNum++;
+               if (inIsTrackpoint)
+               {
+                       // System.out.println("Point " + _pointNum + " is in track '" + inTrackName + "' (" + inTrackNum + ")");
+                       if (inTrackNum != _trackNum) {
+                               _trackNames.add(inTrackName);
+                               _startIndices.add(new Integer(_pointNum));
+                       }
+               }
+               _trackNum = inTrackNum;
+       }
+
+       /**
+        * @return number of tracks found in file
+        */
+       public int getNumTracks()
+       {
+               return _trackNames.size();
+       }
+
+       /**
+        * @param inTrackNum index of track, starting from zero
+        * @return name of specified track (or null if none given)
+        */
+       public String getTrackName(int inTrackNum)
+       {
+               if (inTrackNum < 0 || inTrackNum >= getNumTracks()) {return "";}
+               String name = _trackNames.get(inTrackNum);
+               return name;
+       }
+
+       /**
+        * @param inTrackNum index of track, starting from zero
+        * @return number of points in the specified track
+        */
+       public int getNumPointsInTrack(int inTrackNum)
+       {
+               if (inTrackNum < 0 || inTrackNum >= getNumTracks()) {return 0;}
+               if (inTrackNum == (getNumTracks()-1)) {
+                       // last track, use total points
+                       return _pointNum - _startIndices.get(inTrackNum) + 1;
+               }
+               return _startIndices.get(inTrackNum+1) - _startIndices.get(inTrackNum);
+       }
+
+       /**
+        * @param inTrackNum index of track, starting from zero
+        * @return start index of the specified track
+        */
+       public int getStartIndex(int inTrackNum)
+       {
+               if (inTrackNum < 0 || inTrackNum >= getNumTracks()) {return 0;}
+               return _startIndices.get(inTrackNum);
+       }
+
+       /**
+        * Print summary for debug
+        * @deprecated
+        */
+       public void summarize()
+       {
+               System.out.println("File has " + getNumTracks() + " tracks:");
+               for (int i=0; i<getNumTracks(); i++) {
+                       System.out.println("  Track " + i + " is called '" + _trackNames.get(i) + "' and has " + getNumPointsInTrack(i) + " points");
+               }
+       }
+}
index b4ae07a4e662c719d80b7b242be0519cbc532472..a4906ac0a5b81d9dac6076d1e542a0b4339a7e7f 100644 (file)
@@ -6,6 +6,7 @@ import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 
 import tim.prune.data.Field;
+import tim.prune.load.TrackNameList;
 
 
 /**
@@ -13,17 +14,23 @@ import tim.prune.data.Field;
  */
 public class GpxHandler extends XmlHandler
 {
+       private boolean _insidePoint = false;
        private boolean _insideWaypoint = false;
        private boolean _insideName = false;
        private boolean _insideElevation = false;
        private boolean _insideTime = false;
        private boolean _insideType = false;
        private boolean _startSegment = true;
+       private boolean _isTrackPoint = false;
+       private int _trackNum = -1;
+       private String _trackName = null;
        private String _name = null, _latitude = null, _longitude = null;
        private String _elevation = null;
        private String _time = null;
        private String _type = null;
        private ArrayList<String[]> _pointList = new ArrayList<String[]>();
+       private TrackNameList _trackNameList = new TrackNameList();
+
 
        /**
         * Receive the start of a tag
@@ -35,7 +42,9 @@ public class GpxHandler extends XmlHandler
                // Read the parameters for waypoints and track points
                if (qName.equalsIgnoreCase("wpt") || qName.equalsIgnoreCase("trkpt") || qName.equalsIgnoreCase("rtept"))
                {
+                       _insidePoint = true;
                        _insideWaypoint = qName.equalsIgnoreCase("wpt");
+                       _isTrackPoint = qName.equalsIgnoreCase("trkpt");
                        int numAttributes = attributes.getLength();
                        for (int i=0; i<numAttributes; i++)
                        {
@@ -54,6 +63,7 @@ public class GpxHandler extends XmlHandler
                }
                else if (qName.equalsIgnoreCase("name"))
                {
+                       _name = null;
                        _insideName = true;
                }
                else if (qName.equalsIgnoreCase("time"))
@@ -68,6 +78,11 @@ public class GpxHandler extends XmlHandler
                {
                        _startSegment = true;
                }
+               else if (qName.equalsIgnoreCase("trk"))
+               {
+                       _trackNum++;
+                       _trackName = null;
+               }
                super.startElement(uri, localName, qName, attributes);
        }
 
@@ -82,6 +97,7 @@ public class GpxHandler extends XmlHandler
                if (qName.equalsIgnoreCase("wpt") || qName.equalsIgnoreCase("trkpt") || qName.equalsIgnoreCase("rtept"))
                {
                        processPoint();
+                       _insidePoint = false;
                }
                else if (qName.equalsIgnoreCase("ele"))
                {
@@ -112,6 +128,7 @@ public class GpxHandler extends XmlHandler
        {
                String value = new String(ch, start, length);
                if (_insideName && _insideWaypoint) {_name = checkCharacters(_name, value);}
+               if (_insideName && !_insidePoint) {_trackName = checkCharacters(_trackName, value);}
                else if (_insideElevation) {_elevation = checkCharacters(_elevation, value);}
                else if (_insideTime) {_time = checkCharacters(_time, value);}
                else if (_insideType) {_type = checkCharacters(_type, value);}
@@ -148,6 +165,7 @@ public class GpxHandler extends XmlHandler
                }
                values[6] = _type;
                _pointList.add(values);
+               _trackNameList.addPoint(_trackNum, _trackName, _isTrackPoint);
        }
 
 
@@ -177,4 +195,12 @@ public class GpxHandler extends XmlHandler
                }
                return result;
        }
+
+
+       /**
+        * @return track name list
+        */
+       public TrackNameList getTrackNameList() {
+               return _trackNameList;
+       }
 }
index e21e9841d4e6d9ae0e2ebc6f92051ced2589f98b..70419f36681a8076dcb65c40f8ca3eb569733c2f 100644 (file)
@@ -53,7 +53,7 @@ public class GzipFileLoader
                                SourceInfo sourceInfo = new SourceInfo(inFile,
                                        (handler instanceof GpxHandler?SourceInfo.FILE_TYPE.GPX:SourceInfo.FILE_TYPE.KML));
                                _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(),
-                                       Altitude.Format.METRES, sourceInfo);
+                                       Altitude.Format.METRES, sourceInfo, handler.getTrackNameList());
                        }
                }
                catch (Exception e) {
index 7fa5ec1b80f8b1d693686dacb1cd2483709b1f89..4170dc9b747b9e4380d1832b156393b68a238743 100644 (file)
@@ -80,7 +80,7 @@ public class XmlFileLoader extends DefaultHandler implements Runnable
                                SourceInfo sourceInfo = new SourceInfo(_file,
                                        (_handler instanceof GpxHandler?SourceInfo.FILE_TYPE.GPX:SourceInfo.FILE_TYPE.KML));
                                _app.informDataLoaded(_handler.getFieldArray(), _handler.getDataArray(),
-                                       Altitude.Format.METRES, sourceInfo);
+                                       Altitude.Format.METRES, sourceInfo, _handler.getTrackNameList());
                        }
                }
                catch (Exception e)
index 954190a6742b252930f635995e33aa66524a3c0f..bdd351d399ceca68d7ff303d223e4e439592ddd6 100644 (file)
@@ -3,6 +3,7 @@ package tim.prune.load.xml;
 import org.xml.sax.helpers.DefaultHandler;
 
 import tim.prune.data.Field;
+import tim.prune.load.TrackNameList;
 
 /**
  * Abstract superclass of xml handlers
@@ -19,4 +20,13 @@ public abstract class XmlHandler extends DefaultHandler
         * @return field array describing fields of data
         */
        public abstract Field[] getFieldArray();
+
+       /**
+        * Can be overriden (eg by gpx handler) to provide a track name list
+        * @return track name list object if any, or null
+        */
+       public TrackNameList getTrackNameList()
+       {
+               return null;
+       }
 }
index 2d0f1693226cced1a8dd8aafd057d32fd506e936..df60576a057617eccae0652889afe6ce2b6ad2d8 100644 (file)
@@ -22,7 +22,7 @@ public class ZipFileLoader
        /** App for callback of file loading */
        private App _app = null;
        /** Object to do the handling of the xml */
-       XmlFileLoader _xmlLoader = null;
+       private XmlFileLoader _xmlLoader = null;
 
        /**
         * Constructor
@@ -67,7 +67,7 @@ public class ZipFileLoader
                                                        SourceInfo sourceInfo = new SourceInfo(inFile,
                                                                (handler instanceof GpxHandler?SourceInfo.FILE_TYPE.GPX:SourceInfo.FILE_TYPE.KML));
                                                        _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(),
-                                                               Altitude.Format.METRES, sourceInfo);
+                                                               Altitude.Format.METRES, sourceInfo, handler.getTrackNameList());
                                                        xmlFound = true;
                                                }
                                        }
@@ -113,7 +113,8 @@ public class ZipFileLoader
                                                else {
                                                        // Send back to app
                                                        _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(),
-                                                               Altitude.Format.METRES, new SourceInfo("gpsies", SourceInfo.FILE_TYPE.GPSIES));
+                                                               Altitude.Format.METRES, new SourceInfo("gpsies", SourceInfo.FILE_TYPE.GPSIES),
+                                                               handler.getTrackNameList());
                                                        xmlFound = true;
                                                }
                                        }
index 355ac71ec52e595156f13665a02f06480c22149d..197721b8a841fbcd500f00178e42a118037273bb 100644 (file)
@@ -1,4 +1,4 @@
-Prune version 10
+Prune version 11
 ================
 
 Prune is an application for viewing, editing and managing coordinate data from GPS systems,
@@ -17,7 +17,7 @@ Running
 =======
 
 To run Prune from the jar file, simply call it from a command prompt or shell:
-   java -jar prune_10.jar
+   java -jar prune_11.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,7 +25,19 @@ in a file manager window to execute it.  A shortcut, menu item, alias, desktop i
 or other link can of course be made should you wish.
 
 To specify a language other than the default, use an additional parameter, eg:
-   java -jar prune_10.jar --lang=DE
+   java -jar prune_11.jar --lang=DE
+
+New with version 11
+===================
+
+The following features were added since version 10:
+  - Option to select which of the named tracks to load out of a gpx file or gps
+  - Function to delete all values of a single field (eg all altitudes, all timestamps)
+  - Export of 3d view to svg file
+  - Function to upload current track to gpsies.com
+  - Option to hide sidebars (left panel, right panel and lower panel) to just show map
+  - Various bugfixes, especially with gpx export, track compression and scale bar
+  - Dutch and Czech translations thanks to generous volunteers
 
 New with version 10
 ===================
@@ -162,7 +174,7 @@ Further information and updates
 To obtain the source code (if it wasn't included in your jar file), or for further information,
 please visit the website:  http://activityworkshop.net/
 
-You will find there user guides and screenshots illustrating the major features.
+You will find there user guides, screenshots and demo videos illustrating the major features.
 As Prune is further developed, subsequent versions of the program will also be made freely
 available at this website.
 
index 986a07ee3052eaa33e3d5ad6e2626f93ca282d34..bc8d220c41ac7559f7695e107272bd04b71f49a1 100644 (file)
@@ -8,6 +8,7 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.io.File;
 
+import javax.swing.BorderFactory;
 import javax.swing.BoxLayout;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
@@ -129,7 +130,10 @@ public class ExifSaver implements Runnable
        {
                JPanel panel = new JPanel();
                panel.setLayout(new BorderLayout());
-               panel.add(new JLabel(I18nManager.getText("dialog.saveexif.intro")), BorderLayout.NORTH);
+               // Label at top
+               JLabel topLabel = new JLabel(I18nManager.getText("dialog.saveexif.intro"));
+               topLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
+               panel.add(topLabel, BorderLayout.NORTH);
                // centre panel with most controls
                JPanel centrePanel = new JPanel();
                centrePanel.setLayout(new BorderLayout());
index 63810143800e92aca3eeb81944529c563ae76862..78fbe3e5d38e49cecc98fee01b360c27e0b154b0 100644 (file)
@@ -397,8 +397,6 @@ public class FileSaver
                        _fileChooser = new JFileChooser();
                        _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
                        _fileChooser.addChoosableFileFilter(new GenericFileFilter("filetype.txt", new String[] {"txt", "text"}));
-                       _fileChooser.addChoosableFileFilter(new GenericFileFilter("filetype.gpx", new String[] {"gpx"}));
-                       _fileChooser.addChoosableFileFilter(new GenericFileFilter("filetype.kml", new String[] {"kml"}));
                        _fileChooser.setAcceptAllFileFilterUsed(true);
                        // start from directory in config which should be set
                        String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
@@ -445,9 +443,12 @@ public class FileSaver
                        }
                }
 
+               // Correct chosen filename if necessary
+               final File saveFile = (isFilenameOk(inSaveFile)?inSaveFile:new File(inSaveFile.getAbsolutePath() + ".txt"));
+
                // Check if file exists, and confirm overwrite if necessary
                Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
-               if (!inSaveFile.exists() || JOptionPane.showOptionDialog(_parentFrame,
+               if (!saveFile.exists() || JOptionPane.showOptionDialog(_parentFrame,
                                I18nManager.getText("dialog.save.overwrite.text"),
                                I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
                                JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
@@ -456,7 +457,7 @@ public class FileSaver
                        try
                        {
                                // Create output file
-                               writer = new FileWriter(inSaveFile);
+                               writer = new FileWriter(saveFile);
                                // Determine delimiter character to use
                                final char delimiter = getDelimiter();
                                FieldInfo info = null;
@@ -525,11 +526,11 @@ public class FileSaver
                                        writer.write(lineSeparator);
                                }
                                // Store directory in config for later
-                               Config.setConfigString(Config.KEY_TRACK_DIR, inSaveFile.getParentFile().getAbsolutePath());
+                               Config.setConfigString(Config.KEY_TRACK_DIR, saveFile.getParentFile().getAbsolutePath());
                                // Save successful
                                UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
                                         + " " + numSaved + " " + I18nManager.getText("confirm.save.ok2")
-                                        + " " + inSaveFile.getAbsolutePath());
+                                        + " " + saveFile.getAbsolutePath());
                                _app.informDataSaved();
                        }
                        catch (IOException ioe)
@@ -627,4 +628,17 @@ public class FileSaver
                // Wasn't any of those so must be 'other'
                return _otherDelimiterText.getText().charAt(0);
        }
+
+
+       /**
+        * Check the selected filename to see if it is acceptable
+        * @param inFile chosen file to save
+        * @return true if filename is ok
+        */
+       private static boolean isFilenameOk(File inFile)
+       {
+               String filename = inFile.getName().toLowerCase();
+               return (filename.length() <4 || (!filename.endsWith(".gpx")
+                       && !filename.endsWith(".kml") && !filename.endsWith(".kmz") && !filename.endsWith(".zip")));
+       }
 }
diff --git a/tim/prune/save/GpxCacher.java b/tim/prune/save/GpxCacher.java
deleted file mode 100644 (file)
index b7a3399..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-package tim.prune.save;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import tim.prune.data.DataPoint;
-import tim.prune.data.SourceInfo;
-
-/**
- * Class to read in a GPX file and cache all the point strings
- */
-public class GpxCacher extends DefaultHandler
-{
-       private SourceInfo _sourceInfo = null;
-       private String _headerString = null;
-       private String[] _strings = null;
-       private int _pointNum = 0;
-       private boolean _insidePoint = false;
-       private StringBuilder _builder = null;
-
-
-       /**
-        * Constructor
-        * @param inSourceInfo source information
-        */
-       public GpxCacher(SourceInfo inInfo)
-       {
-               _sourceInfo = inInfo;
-               _strings = new String[inInfo.getNumPoints()];
-               _pointNum = 0;
-               // Should be a gpx file, but might be raw, zipped or gzipped
-               File gpxFile = inInfo.getFile();
-               String fileName = gpxFile.getName().toLowerCase();
-               if (gpxFile.exists() && gpxFile.canRead())
-               {
-                       try {
-                               SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
-                               if (fileName.endsWith(".gpx") || fileName.endsWith(".xml")) {
-                                       saxParser.parse(inInfo.getFile(), this);
-                               }
-                               else if (fileName.endsWith(".zip")) {
-                                       saxParser.parse(getZipInputStream(inInfo.getFile()), this);
-                               }
-                               else if (fileName.endsWith(".gz")) {
-                                       saxParser.parse(new GZIPInputStream(new FileInputStream(inInfo.getFile())), this);
-                               }
-                               else {
-                                       System.out.println("GpxCacher unrecognised file type: " + inInfo.getFile().getName());
-                               }
-                       } catch (Exception e) {
-                               // TODO: Handle errors here with a list of warnings?
-                               e.printStackTrace();
-                       }
-               }
-               _builder = null;
-       }
-
-
-       /**
-        * Receive the start of a tag
-        */
-       public void startElement(String inUri, String inLocalName, String inTagName,
-               Attributes inAttributes) throws SAXException
-       {
-               if (inTagName.equalsIgnoreCase("gpx"))
-               {
-                       // store initial gpx tag
-                       _builder = new StringBuilder(60);
-                       appendTag(_builder, inTagName, inAttributes);
-                       _headerString = _builder.toString();
-               }
-               else
-               {
-                       if (inTagName.equalsIgnoreCase("wpt") || inTagName.equalsIgnoreCase("trkpt")
-                               || inTagName.equalsIgnoreCase("rtept"))
-                       {
-                               _insidePoint = true;
-                               _builder = new StringBuilder(60);
-                       }
-                       if (_insidePoint) {
-                               appendTag(_builder, inTagName, inAttributes);
-                       }
-               }
-               super.startElement(inUri, inLocalName, inTagName, inAttributes);
-       }
-
-       /**
-        * Receive characters between tags (inside or outside)
-        */
-       public void characters(char[] inChars, int inStart, int inLength)
-               throws SAXException
-       {
-               if (_insidePoint) {
-                       _builder.append(new String(inChars, inStart, inLength));
-               }
-               super.characters(inChars, inStart, inLength);
-       }
-
-       /**
-        * Receive end of xml tag
-        */
-       public void endElement(String inUri, String inLocalName, String inTagName)
-               throws SAXException
-       {
-               if (_insidePoint) {
-                       _builder.append("</").append(inTagName).append('>');
-               }
-               if (inTagName.equalsIgnoreCase("wpt") || inTagName.equalsIgnoreCase("trkpt")
-                       || inTagName.equalsIgnoreCase("rtept"))
-               {
-                       _strings[_pointNum] = _builder.toString();
-                       _pointNum++;
-                       _insidePoint = false;
-               }
-               super.endElement(inUri, inLocalName, inTagName);
-       }
-
-
-       /**
-        * Append the current tag to the supplied StringBuilder
-        * @param inBuilder Stringbuilder object to append tag to
-        * @param inTagName name of tag
-        * @param inAttributes attributes of tag
-        */
-       private static void appendTag(StringBuilder inBuilder, String inTagName, Attributes inAttributes)
-       {
-               inBuilder.append('<').append(inTagName);
-               int numAtts = inAttributes.getLength();
-               for (int i=0; i<numAtts; i++) {
-                       inBuilder.append(' ').append(inAttributes.getQName(i)).append("=\"")
-                               .append(inAttributes.getValue(i)).append('"');
-               }
-               inBuilder.append('>');
-       }
-
-
-       /**
-        * @return the header string from the GPX tag
-        */
-       public String getHeaderString()
-       {
-               return _headerString;
-       }
-
-       /**
-        * Get the source string for the given point
-        * @param inPoint point to retrieve
-        * @return string if found, otherwise null
-        */
-       public String getSourceString(DataPoint inPoint)
-       {
-               int index = _sourceInfo.getIndex(inPoint);
-               if (index >= 0) {
-                       return _strings[index];
-               }
-               return null;
-       }
-
-       /**
-        * Get an inputstream of a GPX file inside a zip
-        * @param inFile File object describing zip file
-        * @return input stream for Xml parser
-        */
-       private static InputStream getZipInputStream(File inFile)
-       {
-               try
-               {
-                       ZipInputStream zis = new ZipInputStream(new FileInputStream(inFile));
-                       while (zis.available() > 0)
-                       {
-                               ZipEntry entry = zis.getNextEntry();
-                               String entryName = entry.toString();
-                               if (entryName != null && entryName.length() > 4)
-                               {
-                                       String suffix = entryName.substring(entryName.length()-4).toLowerCase();
-                                       if (suffix.equals(".gpx") || suffix.equals(".xml")) {
-                                               // First matching file so must be gpx
-                                               return zis;
-                                       }
-                               }
-                       }
-               }
-               catch (Exception e) {} // ignore errors
-               // not found - error!
-               return null;
-       }
-}
index 019cc438bdbf96003595e7c7ebe7d84654839d11..7f75aeb17689397eccef5d1f45ba01a363f4f3c2 100644 (file)
@@ -38,6 +38,8 @@ import tim.prune.data.Field;
 import tim.prune.data.Timestamp;
 import tim.prune.data.TrackInfo;
 import tim.prune.load.GenericFileFilter;
+import tim.prune.save.xml.GpxCacherList;
+
 
 /**
  * Class to export track information
@@ -334,7 +336,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                                if (point.isWaypoint()) {
                                        if (exportWaypoints)
                                        {
-                                               String pointSource = (inUseCopy?gpxCachers.getSourceString(point):null);
+                                               String pointSource = (inUseCopy?getPointSource(gpxCachers, point):null);
                                                if (pointSource != null) {
                                                        inWriter.write(pointSource);
                                                        inWriter.write('\n');
@@ -404,7 +406,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                                if ((point.getPhoto()==null && inExportTrackpoints) || (point.getPhoto()!=null && inExportPhotos))
                                {
                                        // get the source from the point (if any)
-                                       String pointSource = (inCachers!=null?inCachers.getSourceString(point):null);
+                                       String pointSource = getPointSource(inCachers, point);
                                        boolean writePoint = (pointSource != null && pointSource.toLowerCase().startsWith(inPointTag))
                                                || (pointSource == null && !inOnlyCopies);
                                        if (writePoint)
@@ -430,6 +432,62 @@ public class GpxExporter extends GenericFunction implements Runnable
                return numSaved;
        }
 
+
+       /**
+        * Get the point source for the specified point
+        * @param inCachers list of GPX cachers to ask for source
+        * @param inPoint point object
+        * @return xml source if available, or null otherwise
+        */
+       private static String getPointSource(GpxCacherList inCachers, DataPoint inPoint)
+       {
+               if (inCachers == null || inPoint == null) {return null;}
+               String source = inCachers.getSourceString(inPoint);
+               if (source == null || !inPoint.isModified()) {return source;}
+               // Point has been modified - maybe it's possible to modify the source
+               source = replaceGpxTags(source, "lat=\"", "\"", inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
+               source = replaceGpxTags(source, "lon=\"", "\"", inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
+               source = replaceGpxTags(source, "<ele>", "</ele>", inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
+               source = replaceGpxTags(source, "<time>", "</time>", inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
+               if (inPoint.isWaypoint()) {source = replaceGpxTags(source, "<name>", "</name>", inPoint.getWaypointName());}  // only for waypoints
+               return source;
+       }
+
+       /**
+        * Replace the given value into the given XML string
+        * @param inSource source XML for point
+        * @param inStartTag start tag for field
+        * @param inEndTag end tag for field
+        * @param inValue value to replace between start tag and end tag
+        * @return modified String, or null if not possible
+        */
+       private static String replaceGpxTags(String inSource, String inStartTag, String inEndTag, String inValue)
+       {
+               if (inSource == null) {return null;}
+               // Look for start and end tags within source
+               final int startPos = inSource.indexOf(inStartTag);
+               final int endPos = inSource.indexOf(inEndTag, startPos+inStartTag.length());
+               if (startPos > 0 && endPos > 0)
+               {
+                       String origValue = inSource.substring(startPos + inStartTag.length(), endPos);
+                       if (inValue != null && origValue.equals(inValue)) {
+                               // Value unchanged
+                               return inSource;
+                       }
+                       else if (inValue == null || inValue.equals("")) {
+                               // Need to delete value
+                               return inSource.substring(0, startPos) + inSource.substring(endPos + inEndTag.length());
+                       }
+                       else {
+                               // Need to replace value
+                               return inSource.substring(0, startPos+inStartTag.length()) + inValue + inSource.substring(endPos);
+                       }
+               }
+               // Value not found for this field in original source
+               if (inValue == null || inValue.equals("")) {return inSource;}
+               return null;
+       }
+
        /**
         * Get the header string for the gpx
         * @param inCachers cacher list to ask for headers, if available
index 1b0edfbab07ed76297650040a5fc151e72a5d26d..cec38a2ef0d4efef537246dca164aa587e614e57 100644 (file)
@@ -12,6 +12,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
 
+import javax.swing.BorderFactory;
 import javax.swing.BoxLayout;
 import javax.swing.ButtonGroup;
 import javax.swing.JButton;
@@ -25,28 +26,27 @@ import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 
 import tim.prune.App;
-import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
 import tim.prune.config.Config;
+import tim.prune.data.NumberUtils;
 import tim.prune.data.Track;
+import tim.prune.function.Export3dFunction;
 import tim.prune.load.GenericFileFilter;
 import tim.prune.threedee.LineDialog;
 import tim.prune.threedee.ThreeDModel;
 
 /**
- * Class to export track information
- * into a specified Pov file
+ * Class to export a 3d scene of the track to a specified Pov file
  */
-public class PovExporter extends GenericFunction
+public class PovExporter extends Export3dFunction
 {
        private Track _track = null;
        private JDialog _dialog = null;
        private JFileChooser _fileChooser = null;
        private String _cameraX = null, _cameraY = null, _cameraZ = null;
        private JTextField _cameraXField = null, _cameraYField = null, _cameraZField = null;
-       private JTextField _fontName = null, _altitudeCapField = null;
-       private int _altitudeCap = ThreeDModel.MINIMUM_ALTITUDE_CAP;
+       private JTextField _fontName = null, _altitudeFactorField = null;
        private JRadioButton _ballsAndSticksButton = null;
 
        // defaults
@@ -55,7 +55,7 @@ public class PovExporter extends GenericFunction
 
 
        /**
-        * Constructor giving frame and track
+        * Constructor
         * @param inApp App object
         */
        public PovExporter(App inApp)
@@ -83,23 +83,10 @@ public class PovExporter extends GenericFunction
                double cameraDist = Math.sqrt(inX*inX + inY*inY + inZ*inZ);
                if (cameraDist > 0.0)
                {
-                       _cameraX = "" + (inX / cameraDist * DEFAULT_CAMERA_DISTANCE);
-                       _cameraY = "" + (inY / cameraDist * DEFAULT_CAMERA_DISTANCE);
+                       _cameraX = NumberUtils.formatNumber(inX / cameraDist * DEFAULT_CAMERA_DISTANCE, 5);
+                       _cameraY = NumberUtils.formatNumber(inY / cameraDist * DEFAULT_CAMERA_DISTANCE, 5);
                        // Careful! Need to convert from java3d (right-handed) to povray (left-handed) coordinate system!
-                       _cameraZ = "" + (-inZ / cameraDist * DEFAULT_CAMERA_DISTANCE);
-               }
-       }
-
-
-       /**
-        * @param inAltitudeCap altitude cap to use
-        */
-       public void setAltitudeCap(int inAltitudeCap)
-       {
-               _altitudeCap = inAltitudeCap;
-               if (_altitudeCap < ThreeDModel.MINIMUM_ALTITUDE_CAP)
-               {
-                       _altitudeCap = ThreeDModel.MINIMUM_ALTITUDE_CAP;
+                       _cameraZ = NumberUtils.formatNumber(-inZ / cameraDist * DEFAULT_CAMERA_DISTANCE, 5);
                }
        }
 
@@ -121,8 +108,7 @@ public class PovExporter extends GenericFunction
                _cameraXField.setText(_cameraX);
                _cameraYField.setText(_cameraY);
                _cameraZField.setText(_cameraZ);
-               // Set vertical scale
-               _altitudeCapField.setText("" + _altitudeCap);
+               _altitudeFactorField.setText("" + _altFactor);
                // Show dialog
                _dialog.pack();
                _dialog.setVisible(true);
@@ -137,7 +123,9 @@ public class PovExporter extends GenericFunction
        {
                JPanel panel = new JPanel();
                panel.setLayout(new BorderLayout());
-               panel.add(new JLabel(I18nManager.getText("dialog.exportpov.text")), BorderLayout.NORTH);
+               JLabel introLabel = new JLabel(I18nManager.getText("dialog.exportpov.text"));
+               introLabel.setBorder(BorderFactory.createEmptyBorder(4, 4, 6, 4));
+               panel.add(introLabel, BorderLayout.NORTH);
                // OK, Cancel buttons
                JPanel buttonPanel = new JPanel();
                buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
@@ -190,12 +178,12 @@ public class PovExporter extends GenericFunction
                centralPanel.add(cameraZLabel);
                _cameraZField = new JTextField("" + _cameraZ);
                centralPanel.add(_cameraZField);
-               // Altitude capping
-               JLabel altitudeCapLabel = new JLabel(I18nManager.getText("dialog.3d.altitudecap"));
+               // Altitude exaggeration
+               JLabel altitudeCapLabel = new JLabel(I18nManager.getText("dialog.3d.altitudefactor"));
                altitudeCapLabel.setHorizontalAlignment(SwingConstants.TRAILING);
                centralPanel.add(altitudeCapLabel);
-               _altitudeCapField = new JTextField("" + _altitudeCap);
-               centralPanel.add(_altitudeCapField);
+               _altitudeFactorField = new JTextField("1.0");
+               centralPanel.add(_altitudeFactorField);
 
                // Radio buttons for style - balls on sticks or tubes
                JPanel stylePanel = new JPanel();
@@ -330,10 +318,12 @@ public class PovExporter extends GenericFunction
                        try
                        {
                                // try to use given altitude cap
-                               _altitudeCap = Integer.parseInt(_altitudeCapField.getText());
-                               model.setAltitudeCap(_altitudeCap);
+                               double altFactor = Double.parseDouble(_altitudeFactorField.getText());
+                               model.setAltitudeFactor(altFactor);
+                       }
+                       catch (NumberFormatException nfe) { // parse failed, reset
+                               _altitudeFactorField.setText("1.0");
                        }
-                       catch (NumberFormatException nfe) {}
                        model.scale();
 
                        // Create file and write basics
diff --git a/tim/prune/save/SvgExporter.java b/tim/prune/save/SvgExporter.java
new file mode 100644 (file)
index 0000000..c27b2f3
--- /dev/null
@@ -0,0 +1,515 @@
+package tim.prune.save;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.NumberFormat;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+
+import tim.prune.App;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
+import tim.prune.data.Track;
+import tim.prune.function.Export3dFunction;
+import tim.prune.load.GenericFileFilter;
+import tim.prune.threedee.ThreeDModel;
+
+/**
+ * Class to export a 3d scene of the track to a specified Svg file
+ */
+public class SvgExporter extends Export3dFunction
+{
+       private Track _track = null;
+       private JDialog _dialog = null;
+       private JFileChooser _fileChooser = null;
+       private double _phi = 0.0, _theta = 0.0;
+       private JTextField _phiField = null, _thetaField = null;
+       private JTextField _altitudeFactorField = null;
+       private JCheckBox _gradientsCheckbox = null;
+       private static double _scaleFactor = 1.0;
+
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public SvgExporter(App inApp)
+       {
+               super(inApp);
+               _track = inApp.getTrackInfo().getTrack();
+               // Set default rotation angles
+               _phi = 30;  _theta = 55;
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.exportsvg";
+       }
+
+       /**
+        * Set the rotation angles using coordinates for the camera
+        * @param inX X coordinate of camera
+        * @param inY Y coordinate of camera
+        * @param inZ Z coordinate of camera
+        */
+       public void setCameraCoordinates(double inX, double inY, double inZ)
+       {
+               // Calculate phi and theta based on camera x,y,z
+               _phi = Math.toDegrees(Math.atan2(inX, inZ));
+               _theta = Math.toDegrees(Math.atan2(inY, Math.sqrt(inX*inX + inZ*inZ)));
+       }
+
+
+       /**
+        * Show the dialog to select options and export file
+        */
+       public void begin()
+       {
+               // Make dialog window to select angles, colours etc
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.getContentPane().add(makeDialogComponents());
+               }
+
+               // Set angles
+               NumberFormat threeDP = NumberFormat.getNumberInstance();
+               threeDP.setMaximumFractionDigits(3);
+               _phiField.setText(threeDP.format(_phi));
+               _thetaField.setText(threeDP.format(_theta));
+               // Set vertical scale
+               _altitudeFactorField.setText("" + _altFactor);
+               // Show dialog
+               _dialog.pack();
+               _dialog.setVisible(true);
+       }
+
+
+       /**
+        * Make the dialog components to select the export options
+        * @return Component holding gui elements
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel panel = new JPanel();
+               panel.setLayout(new BorderLayout());
+               JLabel introLabel = new JLabel(I18nManager.getText("dialog.exportsvg.text"));
+               introLabel.setBorder(BorderFactory.createEmptyBorder(4, 4, 6, 4));
+               panel.add(introLabel, BorderLayout.NORTH);
+               // OK, Cancel buttons
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton okButton = new JButton(I18nManager.getText("button.ok"));
+               okButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               doExport();
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(okButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               panel.add(buttonPanel, BorderLayout.SOUTH);
+
+               // central panel
+               JPanel centralPanel = new JPanel();
+               centralPanel.setLayout(new GridLayout(0, 2, 10, 4));
+
+               // rotation angles
+               JLabel phiLabel = new JLabel(I18nManager.getText("dialog.exportsvg.phi"));
+               phiLabel.setHorizontalAlignment(SwingConstants.TRAILING);
+               centralPanel.add(phiLabel);
+               _phiField = new JTextField("" + _phi);
+               centralPanel.add(_phiField);
+               JLabel thetaLabel = new JLabel(I18nManager.getText("dialog.exportsvg.theta"));
+               thetaLabel.setHorizontalAlignment(SwingConstants.TRAILING);
+               centralPanel.add(thetaLabel);
+               _thetaField = new JTextField("" + _theta);
+               centralPanel.add(_thetaField);
+               // Altitude exaggeration
+               JLabel altFactorLabel = new JLabel(I18nManager.getText("dialog.3d.altitudefactor"));
+               altFactorLabel.setHorizontalAlignment(SwingConstants.TRAILING);
+               centralPanel.add(altFactorLabel);
+               _altitudeFactorField = new JTextField("" + _altFactor);
+               centralPanel.add(_altitudeFactorField);
+               // Checkbox for gradients or not
+               JLabel gradientsLabel = new JLabel(I18nManager.getText("dialog.exportsvg.gradients"));
+               gradientsLabel.setHorizontalAlignment(SwingConstants.TRAILING);
+               centralPanel.add(gradientsLabel);
+               _gradientsCheckbox = new JCheckBox();
+               _gradientsCheckbox.setSelected(true);
+               centralPanel.add(_gradientsCheckbox);
+
+               // add this grid to the holder panel
+               JPanel holderPanel = new JPanel();
+               holderPanel.setLayout(new BorderLayout(5, 5));
+               JPanel boxPanel = new JPanel();
+               boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS));
+               boxPanel.add(centralPanel);
+               holderPanel.add(boxPanel, BorderLayout.CENTER);
+
+               panel.add(holderPanel, BorderLayout.CENTER);
+               return panel;
+       }
+
+
+       /**
+        * Select the file and export data to it
+        */
+       private void doExport()
+       {
+               // Copy camera coordinates
+               _phi = checkAngle(_phiField.getText());
+               _theta = checkAngle(_thetaField.getText());
+
+               // OK pressed, so choose output file
+               if (_fileChooser == null)
+               {
+                       _fileChooser = new JFileChooser();
+                       _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
+                       _fileChooser.setFileFilter(new GenericFileFilter("filetype.svg", new String[] {"svg"}));
+                       _fileChooser.setAcceptAllFileFilterUsed(false);
+                       // start from directory in config which should be set
+                       final String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
+                       if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));}
+               }
+
+               // Allow choose again if an existing file is selected
+               boolean chooseAgain = false;
+               do
+               {
+                       chooseAgain = false;
+                       if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
+                       {
+                               // OK pressed and file chosen
+                               File file = _fileChooser.getSelectedFile();
+                               if (!file.getName().toLowerCase().endsWith(".svg")) {
+                                       file = new File(file.getAbsolutePath() + ".svg");
+                               }
+                               // Check if file exists and if necessary prompt for overwrite
+                               Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
+                               if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame,
+                                               I18nManager.getText("dialog.save.overwrite.text"),
+                                               I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
+                                               JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
+                                       == JOptionPane.YES_OPTION)
+                               {
+                                       // Export the file
+                                       if (exportFile(file))
+                                       {
+                                               // file saved - store directory in config for later
+                                               Config.setConfigString(Config.KEY_TRACK_DIR, file.getParentFile().getAbsolutePath());
+                                       }
+                                       else {
+                                               // export failed so need to choose again
+                                               chooseAgain = true;
+                                       }
+                               }
+                               else {
+                                       // overwrite cancelled so need to choose again
+                                       chooseAgain = true;
+                               }
+                       }
+               } while (chooseAgain);
+       }
+
+
+       /**
+        * Export the track data to the specified file
+        * @param inFile File object to save to
+        * @return true if successful
+        */
+       private boolean exportFile(File inFile)
+       {
+               FileWriter writer = null;
+               // find out the line separator for this system
+               String lineSeparator = System.getProperty("line.separator");
+               try
+               {
+                       // create and scale model
+                       ThreeDModel model = new ThreeDModel(_track);
+                       try
+                       {
+                               // try to use given altitude factor
+                               _altFactor = Double.parseDouble(_altitudeFactorField.getText());
+                               model.setAltitudeFactor(_altFactor);
+                       }
+                       catch (NumberFormatException nfe) {}
+                       model.scale();
+                       _scaleFactor = 200 / model.getModelSize();
+
+                       boolean useGradients = _gradientsCheckbox.isSelected();
+
+                       // Create file and write basics
+                       writer = new FileWriter(inFile);
+                       writeStartOfFile(writer, useGradients, lineSeparator);
+                       writeBasePlane(writer, model.getModelSize(), lineSeparator);
+                       // write out cardinal letters NESW
+                       writeCardinals(writer, model.getModelSize(), lineSeparator);
+
+                       // write out points
+                       writeDataPoints(writer, model, useGradients, lineSeparator);
+                       writeEndOfFile(writer, lineSeparator);
+
+                       // everything worked
+                       UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
+                                + " " + _track.getNumPoints() + " " + I18nManager.getText("confirm.save.ok2")
+                                + " " + inFile.getAbsolutePath());
+                       return true;
+               }
+               catch (IOException ioe)
+               {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(),
+                               I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
+               }
+               finally
+               {
+                       // close file ignoring exceptions
+                       try {
+                               writer.close();
+                       }
+                       catch (Exception e) {}
+               }
+               return false;
+       }
+
+
+       /**
+        * Write the start of the Svg file
+        * @param inWriter Writer to use for writing file
+        * @param inUseGradients true to use gradients, false for flat fills
+        * @param inLineSeparator line separator to use
+        * @throws IOException on file writing error
+        */
+       private static void writeStartOfFile(FileWriter inWriter, boolean inUseGradients,
+               String inLineSeparator)
+       throws IOException
+       {
+               inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
+               inWriter.write(inLineSeparator);
+               inWriter.write("<!-- Svg file produced by Prune - see http://activityworkshop.net/ -->");
+               inWriter.write(inLineSeparator);
+               inWriter.write("<svg width=\"800\" height=\"700\">");
+               inWriter.write(inLineSeparator);
+               if (inUseGradients)
+               {
+                       final String defs = "<defs>" +
+                               "<radialGradient id=\"wayfill\" cx=\"0.5\" cy=\"0.5\" r=\"0.5\" fx=\"0.5\" fy=\"0.5\">" +
+                               "<stop offset=\"0%\" stop-color=\"#2323aa\"/>" +
+                               "<stop offset=\"100%\" stop-color=\"#000080\"/>" +
+                               "</radialGradient>" + inLineSeparator +
+                               "<radialGradient id=\"trackfill\" cx=\"0.5\" cy=\"0.5\" r=\"0.5\" fx=\"0.5\" fy=\"0.5\">" +
+                               "<stop offset=\"0%\" stop-color=\"#23aa23\"/>" +
+                               "<stop offset=\"100%\" stop-color=\"#008000\"/>" +
+                               "</radialGradient>" +
+                               "</defs>";
+                   inWriter.write(defs);
+                       inWriter.write(inLineSeparator);
+               }
+               inWriter.write("<g inkscape:label=\"Layer 1\" inkscape:groupmode=\"layer\" id=\"layer1\">");
+               inWriter.write(inLineSeparator);
+       }
+
+       /**
+        * Write the base plane
+        * @param inWriter Writer to use for writing file
+        * @param inModelSize model size
+        * @param inLineSeparator line separator to use
+        * @throws IOException on file writing error
+        */
+       private void writeBasePlane(FileWriter inWriter, double inModelSize, String inLineSeparator)
+       throws IOException
+       {
+               // Use model size and camera angles to draw path for base rectangle (using 3d transform)
+               int[] coords1 = convertCoordinates(-inModelSize, -inModelSize, 0);
+               int[] coords2 = convertCoordinates(inModelSize, -inModelSize, 0);
+               int[] coords3 = convertCoordinates(inModelSize, inModelSize, 0);
+               int[] coords4 = convertCoordinates(-inModelSize, inModelSize, 0);
+               final String corners = "M " + coords1[0] + "," + coords1[1]
+                       + " L " + coords2[0] + "," + coords2[1]
+                       + " L " + coords3[0] + "," + coords3[1]
+                       + " L " + coords4[0] + "," + coords4[1] + " z";
+               inWriter.write("<path style=\"fill:#446666;stroke:#000000;\" d=\"" + corners + "\" id=\"rect1\" />");
+               inWriter.write(inLineSeparator);
+       }
+
+       /**
+        * Write the cardinal letters NESW
+        * @param inWriter Writer to use for writing file
+        * @param inModelSize model size
+        * @param inLineSeparator line separator to use
+        * @throws IOException on file writing error
+        */
+       private void writeCardinals(FileWriter inWriter, double inModelSize, String inLineSeparator)
+       throws IOException
+       {
+               // Use model size and camera angles to calculate positions
+               int[] coordsN = convertCoordinates(0, inModelSize, 0);
+               writeCardinal(inWriter, coordsN[0], coordsN[1], "cardinal.n", inLineSeparator);
+               int[] coordsE = convertCoordinates(inModelSize, 0, 0);
+               writeCardinal(inWriter, coordsE[0], coordsE[1], "cardinal.e", inLineSeparator);
+               int[] coordsS = convertCoordinates(0, -inModelSize, 0);
+               writeCardinal(inWriter, coordsS[0], coordsS[1], "cardinal.s", inLineSeparator);
+               int[] coordsW = convertCoordinates(-inModelSize, 0, 0);
+               writeCardinal(inWriter, coordsW[0], coordsW[1], "cardinal.w", inLineSeparator);
+       }
+
+       /**
+        * Write a single cardinal letter
+        * @param inWriter Writer to use for writing file
+        * @param inX x coordinate
+        * @param inY y coordinate
+        * @param inKey key for string to write
+        * @param inLineSeparator line separator to use
+        * @throws IOException on file writing error
+        */
+       private static void writeCardinal(FileWriter inWriter, int inX, int inY, String inKey, String inLineSeparator)
+       throws IOException
+       {
+               inWriter.write("<text x=\"" + inX + "\" y=\"" + inY + "\" font-size=\"26\" fill=\"black\" " +
+                       "stroke=\"white\" stroke-width=\"0.5\">");
+               inWriter.write(I18nManager.getText(inKey));
+               inWriter.write("</text>");
+               inWriter.write(inLineSeparator);
+       }
+
+       /**
+        * Convert the given 3d coordinates into 2d coordinates by rotating and mapping
+        * @param inX x coordinate (east)
+        * @param inY y coordinate (north)
+        * @param inZ z coordinate (up)
+        * @return 2d coordinates as integer array
+        */
+       private int[] convertCoordinates(double inX, double inY, double inZ)
+       {
+               // Rotate by phi degrees around vertical axis
+               final double cosPhi = Math.cos(Math.toRadians(_phi));
+               final double sinPhi = Math.sin(Math.toRadians(_phi));
+               final double x2 = inX * cosPhi + inY * sinPhi;
+               final double y2 = inY * cosPhi - inX * sinPhi;
+               final double z2 = inZ;
+               // Rotate by theta degrees around horizontal axis
+               final double cosTheta = Math.cos(Math.toRadians(_theta));
+               final double sinTheta = Math.sin(Math.toRadians(_theta));
+               double x3 = x2;
+               double y3 = y2 * sinTheta + z2 * cosTheta;
+               // don't need to calculate z3
+               // Scale results to sensible scale for svg
+               x3 = x3 * _scaleFactor + 400;
+               y3 = -y3 * _scaleFactor + 350;
+               return new int[] {(int) x3, (int) y3};
+       }
+
+       /**
+        * Finish off the file by closing the tags
+        * @param inWriter Writer to use for writing file
+        * @param inLineSeparator line separator to use
+        * @throws IOException on file writing error
+        */
+       private static void writeEndOfFile(FileWriter inWriter, String inLineSeparator)
+       throws IOException
+       {
+               inWriter.write(inLineSeparator);
+               inWriter.write("</g></svg>");
+               inWriter.write(inLineSeparator);
+       }
+
+       /**
+        * Write out all the data points to the file in the balls-and-sticks style
+        * @param inWriter Writer to use for writing file
+        * @param inModel model object for getting data points
+        * @param inUseGradients true to use gradients, false for flat fills
+        * @param inLineSeparator line separator to use
+        * @throws IOException on file writing error
+        */
+       private void writeDataPoints(FileWriter inWriter, ThreeDModel inModel, boolean inUseGradients,
+               String inLineSeparator)
+       throws IOException
+       {
+               final int numPoints = inModel.getNumPoints();
+               TreeSet<SvgFragment> fragments = new TreeSet<SvgFragment>();
+               for (int i=0; i<numPoints; i++)
+               {
+                       StringBuilder builder = new StringBuilder();
+                       int[] coords = convertCoordinates(inModel.getScaledHorizValue(i), inModel.getScaledVertValue(i),
+                               inModel.getScaledAltValue(i));
+                       // vertical rod (if altitude positive)
+                       if (inModel.getScaledAltValue(i) > 0.0)
+                       {
+                               int[] baseCoords = convertCoordinates(inModel.getScaledHorizValue(i), inModel.getScaledVertValue(i), 0);
+                               builder.append("<line x1=\"").append(baseCoords[0]).append("\" y1=\"").append(baseCoords[1])
+                                       .append("\" x2=\"").append(coords[0]).append("\" y2=\"").append(coords[1])
+                                       .append("\" stroke=\"gray\" stroke-width=\"3\" />");
+                               builder.append(inLineSeparator);
+                       }
+                       // ball (different according to type)
+                       if (inModel.getPointType(i) == ThreeDModel.POINT_TYPE_WAYPOINT)
+                       {
+                               // waypoint ball
+                               builder.append("<circle cx=\"").append(coords[0]).append("\" cy=\"").append(coords[1])
+                                       .append("\" r=\"11\" ").append(inUseGradients?"fill=\"url(#wayfill)\"":"fill=\"blue\"")
+                                       .append(" stroke=\"green\" stroke-width=\"0.2\" />");
+                       }
+                       else
+                       {
+                               // normal track point ball
+                               builder.append("<circle cx=\"").append(coords[0]).append("\" cy=\"").append(coords[1])
+                                       .append("\" r=\"7\" ").append(inUseGradients?"fill=\"url(#trackfill)\"":"fill=\"green\"")
+                                       .append(" stroke=\"blue\" stroke-width=\"0.2\" />");
+                       }
+                       builder.append(inLineSeparator);
+                       // add to set
+                       fragments.add(new SvgFragment(builder.toString(), coords[1]));
+               }
+
+               // Iterate over the sorted set and write to file
+               Iterator<SvgFragment> iterator = fragments.iterator();
+               while (iterator.hasNext()) {
+                       inWriter.write(iterator.next().getFragment());
+               }
+       }
+
+
+       /**
+        * Check the given angle value
+        * @param inString String entered by user
+        * @return validated value
+        */
+       private static double checkAngle(String inString)
+       {
+               double value = 0.0;
+               try {
+                       value = Double.parseDouble(inString);
+               }
+               catch (Exception e) {} // ignore parse failures
+               return value;
+       }
+}
diff --git a/tim/prune/save/SvgFragment.java b/tim/prune/save/SvgFragment.java
new file mode 100644 (file)
index 0000000..fc01974
--- /dev/null
@@ -0,0 +1,48 @@
+package tim.prune.save;
+
+/**
+ * Class to enable the sorting of Svg fragments
+ */
+public class SvgFragment implements Comparable<SvgFragment>
+{
+       private String _fragment = null;
+       private int _yCoord = 0;
+
+       /**
+        * Constructor
+        * @param inFragment fragment of svg source
+        * @param inYCoord y coordinate of point, for sorting
+        */
+       public SvgFragment(String inFragment, int inYCoord)
+       {
+               _fragment = inFragment;
+               _yCoord = inYCoord;
+       }
+
+       /**
+        * @return svg fragment
+        */
+       public String getFragment()
+       {
+               return _fragment;
+       }
+
+       /**
+        * Compare method
+        */
+       public int compareTo(SvgFragment inOther)
+       {
+               int ycompare = _yCoord - inOther._yCoord;
+               if (ycompare != 0) {return ycompare;}
+               return _fragment.compareTo(inOther._fragment);
+       }
+
+       /**
+        * @param inOther other fragment to compare this one with
+        * @return true if the fragments are equal
+        */
+       public boolean equals(SvgFragment inOther)
+       {
+               return _fragment.equals(inOther._fragment);
+       }
+}
diff --git a/tim/prune/save/xml/GpxCacher.java b/tim/prune/save/xml/GpxCacher.java
new file mode 100644 (file)
index 0000000..2c3204d
--- /dev/null
@@ -0,0 +1,132 @@
+package tim.prune.save.xml;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import tim.prune.data.DataPoint;
+import tim.prune.data.SourceInfo;
+
+/**
+ * Class to read in a GPX file and cache all the point strings
+ */
+public class GpxCacher implements TagReceiver
+{
+       private SourceInfo _sourceInfo = null;
+       private String _headerString = null;
+       private String[] _strings = null;
+       private int _pointNum = 0;
+
+
+       /**
+        * Constructor
+        * @param inSourceInfo source information
+        */
+       public GpxCacher(SourceInfo inInfo)
+       {
+               _sourceInfo = inInfo;
+               _strings = new String[inInfo.getNumPoints()];
+               _pointNum = 0;
+               // Should be a gpx file, but might be raw, zipped or gzipped
+               File gpxFile = inInfo.getFile();
+               String fileName = gpxFile.getName().toLowerCase();
+               if (gpxFile.exists() && gpxFile.canRead())
+               {
+                       GpxSlicer slicer = new GpxSlicer(this);
+                       InputStream istream = null;
+                       BufferedInputStream bstream = null;
+                       try {
+                               if (fileName.endsWith(".gpx") || fileName.endsWith(".xml")) {
+                                       istream = new FileInputStream(inInfo.getFile());
+                               }
+                               else if (fileName.endsWith(".zip")) {
+                                       istream = getZipInputStream(inInfo.getFile());
+                               }
+                               else if (fileName.endsWith(".gz")) {
+                                       istream = new GZIPInputStream(new FileInputStream(inInfo.getFile()));
+                               }
+                               else {
+                                       System.out.println("GpxCacher unrecognised file type: " + inInfo.getFile().getName());
+                               }
+                               if (istream != null) {
+                                       bstream = new BufferedInputStream(istream);
+                                       slicer.slice(bstream);
+                                       bstream.close();
+                               }
+                       } catch (Exception e) {
+                               // TODO: Handle errors here with a list of warnings?
+                               e.printStackTrace();
+                       }
+               }
+       }
+
+       /**
+        * Accept a tag from the slicer
+        */
+       public void reportTag(String inTag)
+       {
+               if (_headerString == null) {
+                       _headerString = inTag;
+               }
+               else {
+                       _strings[_pointNum] = inTag;
+                       _pointNum++;
+               }
+       }
+
+
+       /**
+        * @return the header string from the GPX tag
+        */
+       public String getHeaderString()
+       {
+               return _headerString;
+       }
+
+       /**
+        * Get the source string for the given point
+        * @param inPoint point to retrieve
+        * @return string if found, otherwise null
+        */
+       public String getSourceString(DataPoint inPoint)
+       {
+               int index = _sourceInfo.getIndex(inPoint);
+               if (index >= 0) {
+                       return _strings[index];
+               }
+               return null;
+       }
+
+       /**
+        * Get an inputstream of a GPX file inside a zip
+        * @param inFile File object describing zip file
+        * @return input stream for Xml parser
+        */
+       private static InputStream getZipInputStream(File inFile)
+       {
+               try
+               {
+                       ZipInputStream zis = new ZipInputStream(new FileInputStream(inFile));
+                       while (zis.available() > 0)
+                       {
+                               ZipEntry entry = zis.getNextEntry();
+                               String entryName = entry.toString();
+                               if (entryName != null && entryName.length() > 4)
+                               {
+                                       String suffix = entryName.substring(entryName.length()-4).toLowerCase();
+                                       if (suffix.equals(".gpx") || suffix.equals(".xml")) {
+                                               // First matching file so must be gpx
+                                               return zis;
+                                       }
+                               }
+                       }
+               }
+               catch (Exception e) {} // ignore errors
+               // not found - error!
+               return null;
+       }
+}
similarity index 92%
rename from tim/prune/save/GpxCacherList.java
rename to tim/prune/save/xml/GpxCacherList.java
index 126c22d94066e95e5599be96740085219c20a00f..87b62b731465acee55248db1172474247153116b 100644 (file)
@@ -1,4 +1,4 @@
-package tim.prune.save;
+package tim.prune.save.xml;
 
 import tim.prune.data.DataPoint;
 import tim.prune.data.FileInfo;
@@ -37,8 +37,6 @@ public class GpxCacherList
        public String getSourceString(DataPoint inPoint)
        {
                String str = null;
-               // Check if point has been modified, if so return null
-               if (inPoint.isModified()) {return null;}
                // Loop over sources
                for (int i=0; i<_cacherList.length && (str == null); i++) {
                        GpxCacher cacher = _cacherList[i];
diff --git a/tim/prune/save/xml/GpxSlicer.java b/tim/prune/save/xml/GpxSlicer.java
new file mode 100644 (file)
index 0000000..efc58b4
--- /dev/null
@@ -0,0 +1,128 @@
+package tim.prune.save.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Class to slice up a gpx stream and report the found tags
+ * back to a listener.
+ * Used by Gpx caching to re-read and store the gpx source
+ */
+public class GpxSlicer
+{
+       /** listener to receive tags */
+       private TagReceiver _receiver = null;
+       /** string builder for copying source xml */
+       private StringBuilder _builder = null;
+
+       // character sequences for start and end of tags
+       private static final char[] GPX_START = "<gpx".toCharArray();
+       private static final char[] GPX_END = ">".toCharArray();
+       private static final char[] TRKPT_START = "<trkpt".toCharArray();
+       private static final char[] TRKPT_END = "/trkpt>".toCharArray();
+       private static final char[] WPT_START = "<wpt".toCharArray();
+       private static final char[] WPT_END = "/wpt>".toCharArray();
+       private static final char[] RTEPT_START = "<rtept".toCharArray();
+       private static final char[] RTEPT_END = "/rtept>".toCharArray();
+       private static final char[] CDATA_START = "<![CDATA[".toCharArray();
+       private static final char[] CDATA_END = "]]>".toCharArray();
+
+
+       /**
+        * Constructor
+        * @param inReceiver listener for tags
+        */
+       public GpxSlicer(TagReceiver inReceiver)
+       {
+               _receiver = inReceiver;
+       }
+
+       /**
+        * Begin the slicing and pass the found tags back to the listener
+        * @param inStream input stream for reading gpx source
+        */
+       public void slice(InputStream inStream)
+       {
+               _builder = new StringBuilder(100);
+               boolean insideTag = false;
+               boolean insideCdata = false;
+               char[] endTag = null;
+               boolean foundHeader = false;
+               int b = 0;
+               try
+               {
+                       while ((b = inStream.read()) >= 0)
+                       {
+                               if (!insideTag && !insideCdata) {
+                                       if (b == '<') _builder.setLength(0);
+                               }
+                               // copy character
+                               _builder.append((char)b);
+
+                               if (insideCdata) {
+                                       // Just look for end of cdata block
+                                       if (foundSequence(CDATA_END)) {insideCdata = false;}
+                               }
+                               else
+                               {
+                                       if (!insideTag)
+                                       {
+                                               // Look for start of one of the tags
+                                               if (!foundHeader && foundSequence(GPX_START)) {
+                                                       insideTag = true;
+                                                       foundHeader = true;
+                                                       endTag = GPX_END;
+                                               }
+                                               else if (b == 't')
+                                               {
+                                                       if (foundSequence(TRKPT_START)) {
+                                                               insideTag = true;
+                                                               endTag = TRKPT_END;
+                                                       }
+                                                       else if (foundSequence(WPT_START)) {
+                                                               insideTag = true;
+                                                               endTag = WPT_END;
+                                                       }
+                                                       else if (foundSequence(RTEPT_START)) {
+                                                               insideTag = true;
+                                                               endTag = RTEPT_END;
+                                                       }
+                                               }
+                                       }
+                                       else
+                                       {
+                                               // Look for end of found tag
+                                               if (foundSequence(endTag)) {
+                                                       _receiver.reportTag(_builder.toString());
+                                                       _builder.setLength(0);
+                                                       insideTag = false;
+                                               }
+                                       }
+                                       // Look for start of cdata block
+                                       if (foundSequence(CDATA_START)) {insideCdata = true;}
+                               }
+                       }
+               }
+               catch (IOException e) {} // ignore
+       }
+
+       /**
+        * Look for the given character sequence in the last characters read
+        * @param inChars sequence to look for
+        * @return true if sequence found
+        */
+       private boolean foundSequence(char[] inChars)
+       {
+               final int numChars = inChars.length;
+               final int bufflen = _builder.length();
+               if (bufflen < numChars) {return false;}
+               for (int i=0; i<numChars; i++)
+               {
+                       char searchChar = inChars[numChars - 1 - i];
+                       char sourceChar = _builder.charAt(bufflen - 1 - i);
+                       if (searchChar != sourceChar) {return false;}
+                       //if (Character.toLowerCase(searchChar) != Character.toLowerCase(sourceChar)) {return false;}
+               }
+               return true;
+       }
+}
diff --git a/tim/prune/save/xml/TagReceiver.java b/tim/prune/save/xml/TagReceiver.java
new file mode 100644 (file)
index 0000000..a6d7737
--- /dev/null
@@ -0,0 +1,14 @@
+package tim.prune.save.xml;
+
+/**
+ * Interface for receivers of tag strings
+ * used for reading tags from xml and reporting them back to a listener
+ */
+public interface TagReceiver
+{
+       /**
+        * Method to give a tag string to a listener
+        * @param inTag xml tag
+        */
+       public void reportTag(String inTag);
+}
index 0bde564c373fe45199aca872bdbcf49f18cf97de..4dffd90dcb03233031b603d2c255f57521a17e25 100644 (file)
@@ -44,8 +44,8 @@ import com.sun.j3d.utils.universe.SimpleUniverse;
 
 import tim.prune.FunctionLibrary;
 import tim.prune.I18nManager;
-import tim.prune.data.Altitude;
 import tim.prune.data.Track;
+import tim.prune.function.Export3dFunction;
 
 
 /**
@@ -58,7 +58,7 @@ public class Java3DWindow implements ThreeDWindow
        private JFrame _frame = null;
        private ThreeDModel _model = null;
        private OrbitBehavior _orbit = null;
-       private int _altitudeCap = ThreeDModel.MINIMUM_ALTITUDE_CAP;
+       private double _altFactor = 50.0;
 
        /** only prompt about big track size once */
        private static boolean TRACK_SIZE_WARNING_GIVEN = false;
@@ -95,18 +95,17 @@ public class Java3DWindow implements ThreeDWindow
         */
        public void show() throws ThreeDException
        {
-               // Get the altitude cap to use
-               String altitudeUnits = getAltitudeUnitsLabel(_track);
-               Object altCapString = JOptionPane.showInputDialog(_parentFrame,
-                       I18nManager.getText("dialog.3d.altitudecap") + " (" + altitudeUnits + ")",
+               // Get the altitude exaggeration to use
+               Object factorString = JOptionPane.showInputDialog(_parentFrame,
+                       I18nManager.getText("dialog.3d.altitudefactor"),
                        I18nManager.getText("dialog.3d.title"),
-                       JOptionPane.QUESTION_MESSAGE, null, null, "" + _altitudeCap);
-               if (altCapString == null) return;
-               try
-               {
-                       _altitudeCap = Integer.parseInt(altCapString.toString());
+                       JOptionPane.QUESTION_MESSAGE, null, null, _altFactor);
+               if (factorString == null) return;
+               try {
+                       _altFactor = Double.parseDouble(factorString.toString());
                }
                catch (Exception e) {} // Ignore parse errors
+               if (_altFactor < 1.0) {_altFactor = 1.0;}
 
                // Set up the graphics config
                GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
@@ -137,8 +136,7 @@ public class Java3DWindow implements ThreeDWindow
                                // opted to continue, don't show warning again
                                TRACK_SIZE_WARNING_GIVEN = true;
                        }
-                       else
-                       {
+                       else {
                                // opted to cancel - show warning again next time
                                return;
                        }
@@ -156,8 +154,7 @@ public class Java3DWindow implements ThreeDWindow
                u.getViewingPlatform().setNominalViewingTransform();
 
                // Add behaviour to rotate using mouse
-               _orbit = new OrbitBehavior(canvas, OrbitBehavior.REVERSE_ALL |
-                                                                 OrbitBehavior.STOP_ZOOM);
+               _orbit = new OrbitBehavior(canvas, OrbitBehavior.REVERSE_ALL | OrbitBehavior.STOP_ZOOM);
                BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
                _orbit.setSchedulingBounds(bounds);
                u.getViewingPlatform().setViewPlatformBehavior(_orbit);
@@ -172,19 +169,27 @@ public class Java3DWindow implements ThreeDWindow
                // Make panel for render, close buttons
                JPanel panel = new JPanel();
                panel.setLayout(new FlowLayout(FlowLayout.RIGHT));
-               // Add callback button for render
-               JButton renderButton = new JButton(I18nManager.getText("function.exportpov"));
-               renderButton.addActionListener(new ActionListener()
-               {
-                       /** Render button pressed */
+               // Add button for exporting pov
+               JButton povButton = new JButton(I18nManager.getText("function.exportpov"));
+               povButton.addActionListener(new ActionListener() {
+                       /** Export pov button pressed */
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               if (_orbit != null) {
+                                       callbackRender(FunctionLibrary.FUNCTION_POVEXPORT);
+                               }
+                       }});
+               panel.add(povButton);
+               // Add button for exporting svg
+               JButton svgButton = new JButton(I18nManager.getText("function.exportsvg"));
+               svgButton.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
                        {
-                               if (_orbit != null)
-                               {
-                                       callbackRender();
+                               if (_orbit != null) {
+                                       callbackRender(FunctionLibrary.FUNCTION_SVGEXPORT);
                                }
                        }});
-               panel.add(renderButton);
+               panel.add(svgButton);
                // Display coordinates of lat/long lines of 3d graph in separate dialog
                JButton showLinesButton = new JButton(I18nManager.getText("button.showlines"));
                showLinesButton.addActionListener(new ActionListener() {
@@ -202,10 +207,8 @@ public class Java3DWindow implements ThreeDWindow
                closeButton.addActionListener(new ActionListener()
                {
                        /** Close button pressed - clean up */
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _frame.dispose();
-                               _frame = null;
+                       public void actionPerformed(ActionEvent e) {
+                               dispose();
                                _orbit = null;
                        }
                });
@@ -215,16 +218,14 @@ public class Java3DWindow implements ThreeDWindow
                _frame.pack();
                // Add a listener to clean up when window closed
                _frame.addWindowListener(new WindowAdapter() {
-                       public void windowClosing(WindowEvent e)
-                       {
+                       public void windowClosing(WindowEvent e) {
                                dispose();
                        }
                });
 
                // show frame
                _frame.setVisible(true);
-               if (_frame.getState() == JFrame.ICONIFIED)
-               {
+               if (_frame.getState() == JFrame.ICONIFIED) {
                        _frame.setState(JFrame.NORMAL);
                }
        }
@@ -274,20 +275,22 @@ public class Java3DWindow implements ThreeDWindow
                Box plane = null;
                planeAppearance = new Appearance();
                planeAppearance.setMaterial(new Material(new Color3f(0.1f, 0.2f, 0.2f),
-                new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.3f, 0.4f, 0.4f),
-                new Color3f(0.3f, 0.3f, 0.3f), 0.0f));
+                       new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.3f, 0.4f, 0.4f),
+                       new Color3f(0.3f, 0.3f, 0.3f), 0.0f));
                plane = new Box(10f, 0.04f, 10f, planeAppearance);
                objTrans.addChild(plane);
 
                // N, S, E, W
                GeneralPath bevelPath = new GeneralPath();
                bevelPath.moveTo(0.0f, 0.0f);
-               for (int i=0; i<91; i+= 5)
+               for (int i=0; i<91; i+= 5) {
                        bevelPath.lineTo((float) (0.1 - 0.1 * Math.cos(Math.toRadians(i))),
                          (float) (0.1 * Math.sin(Math.toRadians(i))));
-               for (int i=90; i>0; i-=5)
+               }
+               for (int i=90; i>0; i-=5) {
                        bevelPath.lineTo((float) (0.3 + 0.1 * Math.cos(Math.toRadians(i))),
                          (float) (0.1 * Math.sin(Math.toRadians(i))));
+               }
                Font3D compassFont = new Font3D(
                        new Font(CARDINALS_FONT, Font.PLAIN, 1),
                        new FontExtrusion(bevelPath));
@@ -298,7 +301,7 @@ public class Java3DWindow implements ThreeDWindow
 
                // create and scale model
                _model = new ThreeDModel(_track);
-               _model.setAltitudeCap(_altitudeCap);
+               _model.setAltitudeFactor(_altFactor);
                _model.scale();
 
                // Lat/Long lines
@@ -308,27 +311,23 @@ public class Java3DWindow implements ThreeDWindow
                objTrans.addChild(createDataPoints(_model));
 
                // Create lights
-               BoundingSphere bounds =
-                 new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
+               BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
                AmbientLight aLgt = new AmbientLight(new Color3f(1.0f, 1.0f, 1.0f));
                aLgt.setInfluencingBounds(bounds);
                objTrans.addChild(aLgt);
 
                PointLight pLgt = new PointLight(new Color3f(1.0f, 1.0f, 1.0f),
-                new Point3f(0f, 0f, 2f),
-                new Point3f(0.25f, 0.05f, 0.0f) );
+                       new Point3f(0f, 0f, 2f), new Point3f(0.25f, 0.05f, 0.0f) );
                pLgt.setInfluencingBounds(bounds);
                objTrans.addChild(pLgt);
 
                PointLight pl2 = new PointLight(new Color3f(0.8f, 0.9f, 0.4f),
-                new Point3f(6f, 1f, 6f),
-                new Point3f(0.2f, 0.1f, 0.05f) );
+                       new Point3f(6f, 1f, 6f), new Point3f(0.2f, 0.1f, 0.05f) );
                pl2.setInfluencingBounds(bounds);
                objTrans.addChild(pl2);
 
                PointLight pl3 = new PointLight(new Color3f(0.7f, 0.7f, 0.7f),
-                new Point3f(0.0f, 12f, -2f),
-                new Point3f(0.1f, 0.1f, 0.0f) );
+                       new Point3f(0.0f, 12f, -2f), new Point3f(0.1f, 0.1f, 0.0f) );
                pl3.setInfluencingBounds(bounds);
                objTrans.addChild(pl3);
 
@@ -350,8 +349,8 @@ public class Java3DWindow implements ThreeDWindow
        {
                Text3D txt = new Text3D(inFont, inText, inLocn, Text3D.ALIGN_FIRST, Text3D.PATH_RIGHT);
                Material mat = new Material(new Color3f(0.5f, 0.5f, 0.55f),
-                new Color3f(0.05f, 0.05f, 0.1f), new Color3f(0.3f, 0.4f, 0.5f),
-                new Color3f(0.4f, 0.5f, 0.7f), 70.0f);
+                       new Color3f(0.05f, 0.05f, 0.1f), new Color3f(0.3f, 0.4f, 0.5f),
+                       new Color3f(0.4f, 0.5f, 0.7f), 70.0f);
                mat.setLightingEnable(true);
                Appearance app = new Appearance();
                app.setMaterial(mat);
@@ -392,7 +391,7 @@ public class Java3DWindow implements ThreeDWindow
        {
                Cylinder latline = new Cylinder(0.1f, (float) (inSize*2));
                Transform3D horizShift = new Transform3D();
-               horizShift.setTranslation(new Vector3d(0.0, 0.0, inLatitude));
+               horizShift.setTranslation(new Vector3d(0.0, 0.0, -inLatitude));
                TransformGroup horizTrans = new TransformGroup(horizShift);
                Transform3D zRot = new Transform3D();
                zRot.rotZ(Math.toRadians(90.0));
@@ -482,15 +481,17 @@ public class Java3DWindow implements ThreeDWindow
        }
 
 
+       /** @return track point object */
        private static Group createTrackpoint(Point3d inPointPos, byte inHeightCode)
        {
                Material mat = getTrackpointMaterial(inHeightCode);
                // MAYBE: sort symbol scaling
-               Sphere dot = new Sphere(0.2f); // * symbolScaling / 100f);
+               Sphere dot = new Sphere(0.2f);
                return createBall(inPointPos, dot, mat);
        }
 
 
+       /** @return Material object for track points with the appropriate colour for the height */
        private static Material getTrackpointMaterial(byte inHeightCode)
        {
                // create default material
@@ -499,10 +500,10 @@ public class Java3DWindow implements ThreeDWindow
                        new Color3f(1.0f, 0.6f, 0.6f), 70.0f);
                // change colour according to height code
                if (inHeightCode == 1) mat.setDiffuseColor(new Color3f(0.4f, 0.9f, 0.2f));
-               if (inHeightCode == 2) mat.setDiffuseColor(new Color3f(0.7f, 0.8f, 0.2f));
-               if (inHeightCode == 3) mat.setDiffuseColor(new Color3f(0.5f, 0.85f, 0.95f));
-               if (inHeightCode == 4) mat.setDiffuseColor(new Color3f(0.1f, 0.9f, 0.9f));
-               if (inHeightCode >= 5) mat.setDiffuseColor(new Color3f(1.0f, 1.0f, 1.0f));
+               else if (inHeightCode == 2) mat.setDiffuseColor(new Color3f(0.7f, 0.8f, 0.2f));
+               else if (inHeightCode == 3) mat.setDiffuseColor(new Color3f(0.3f, 0.6f, 0.4f));
+               else if (inHeightCode == 4) mat.setDiffuseColor(new Color3f(0.1f, 0.9f, 0.9f));
+               else if (inHeightCode >= 5) mat.setDiffuseColor(new Color3f(1.0f, 1.0f, 1.0f));
                // return object
                return mat;
        }
@@ -531,15 +532,14 @@ public class Java3DWindow implements ThreeDWindow
                // Also create rod for ball to sit on
                Cylinder rod = new Cylinder(0.1f, (float) inPosition.y);
                Material rodMat = new Material(new Color3f(0.2f, 0.2f, 0.2f),
-                new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.2f, 0.2f, 0.2f),
-                new Color3f(0.05f, 0.05f, 0.05f), 0.4f);
+                       new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.2f, 0.2f, 0.2f),
+                       new Color3f(0.05f, 0.05f, 0.05f), 0.4f);
                rodMat.setLightingEnable(true);
                Appearance rodApp = new Appearance();
                rodApp.setMaterial(rodMat);
                rod.setAppearance(rodApp);
                Transform3D rodShift = new Transform3D();
-               rodShift.setTranslation(new Vector3d(inPosition.x,
-                inPosition.y/2.0, inPosition.z));
+               rodShift.setTranslation(new Vector3d(inPosition.x, inPosition.y/2.0, inPosition.z));
                TransformGroup rodShiftTrans = new TransformGroup(rodShift);
                rodShiftTrans.addChild(rod);
                group.addChild(rodShiftTrans);
@@ -550,8 +550,9 @@ public class Java3DWindow implements ThreeDWindow
 
        /**
         * Calculate the angles and call them back to the app
+        * @param inFunction function to call (either pov or svg)
         */
-       private void callbackRender()
+       private void callbackRender(Export3dFunction inFunction)
        {
                Transform3D trans3d = new Transform3D();
                _orbit.getViewingPlatform().getViewPlatformTransform().getTransform(trans3d);
@@ -564,28 +565,13 @@ public class Java3DWindow implements ThreeDWindow
                firstTran.rotY(Math.toRadians(-INITIAL_Y_ROTATION));
                Transform3D secondTran = new Transform3D();
                secondTran.rotX(Math.toRadians(-INITIAL_X_ROTATION));
-               // Apply inverse rotations in reverse order to test point
+               // Apply inverse rotations in reverse order to the test point
                Point3d result = new Point3d();
                secondTran.transform(point, result);
                firstTran.transform(result);
                // Callback settings to pov export function
-               FunctionLibrary.FUNCTION_POVEXPORT.setCameraCoordinates(result.x, result.y, result.z);
-               FunctionLibrary.FUNCTION_POVEXPORT.setAltitudeCap(_altitudeCap);
-               FunctionLibrary.FUNCTION_POVEXPORT.begin();
-       }
-
-
-       /**
-        * Get a units label for the altitudes in the given Track
-        * @param inTrack Track object
-        * @return units label for altitude used in Track
-        */
-       private static String getAltitudeUnitsLabel(Track inTrack)
-       {
-               Altitude.Format altitudeFormat = inTrack.getAltitudeRange().getFormat();
-               if (altitudeFormat == Altitude.Format.METRES)
-                       return I18nManager.getText("units.metres.short");
-               return I18nManager.getText("units.feet.short");
+               inFunction.setCameraCoordinates(result.x, result.y, result.z);
+               inFunction.setAltitudeExaggeration(_altFactor);
+               inFunction.begin();
        }
-
 }
index a0f218a65e82bbcd8fe9221a906b957d894a984f..e371bd0429105ac502d1d0a46f14a3d72e715f35 100644 (file)
@@ -64,7 +64,9 @@ public class LineDialog
                JPanel panel = new JPanel();
                panel.setLayout(new BorderLayout());
                StringBuffer descBuffer = new StringBuffer();
-               if (_latLines == null || _latLines.length == 0 || _lonLines == null || _lonLines.length == 0)
+               final int numLatLines = (_latLines == null?0:_latLines.length);
+               final int numLonLines = (_lonLines == null?0:_lonLines.length);
+               if (numLatLines == 0 && numLonLines == 0)
                {
                        descBuffer.append("<p>").append(I18nManager.getText("dialog.3dlines.empty")).append("</p>");
                }
@@ -73,7 +75,7 @@ public class LineDialog
                        descBuffer.append("<p>").append(I18nManager.getText("dialog.3dlines.intro")).append(":</p>");
                        descBuffer.append("<p>").append(I18nManager.getText("fieldname.latitude")).append("<ul>");
                        Latitude lat = null;
-                       for (int i=0; i<_latLines.length; i++)
+                       for (int i=0; i<numLatLines; i++)
                        {
                                lat = new Latitude(_latLines[i], Latitude.FORMAT_DEG);
                                descBuffer.append("<li>").append(lat.output(Latitude.FORMAT_DEG_WHOLE_MIN)).append("</li>");
@@ -81,7 +83,7 @@ public class LineDialog
                        descBuffer.append("</ul></p>");
                        descBuffer.append("<p>").append(I18nManager.getText("fieldname.longitude")).append("<ul>");
                        Longitude lon = null;
-                       for (int i=0; i<_lonLines.length; i++)
+                       for (int i=0; i<numLonLines; i++)
                        {
                                lon = new Longitude(_lonLines[i], Longitude.FORMAT_DEG);
                                descBuffer.append("<li>").append(lon.output(Longitude.FORMAT_DEG_WHOLE_MIN)).append("</li>");
index 65f1152e913861555bd10a8f5786415bbe9ce15e..7245633aa0667b936a0d5307fc8b0b84f2b38136 100644 (file)
@@ -15,7 +15,6 @@ public class ThreeDModel
        private Track _track = null;
        private PointScaler _scaler = null;
        private double _modelSize;
-       private int _altitudeCap = -1;
        private double _scaleFactor = 1.0;
        private double _altFactor = 1.0;
        // MAYBE: How to store rods (lifts) in data?
@@ -23,8 +22,6 @@ public class ThreeDModel
        private byte[] _pointHeights = null;
 
        private static final double DEFAULT_MODEL_SIZE = 10.0;
-       /** Minimum altitude cap */
-       public static final int MINIMUM_ALTITUDE_CAP = 100;
 
        // Constants for point types
        public static final byte POINT_TYPE_WAYPOINT      = 1;
@@ -64,21 +61,16 @@ public class ThreeDModel
                return _track.getNumPoints();
        }
 
-
        /**
-        * Set the altitude cap
-        * @param inAltitudeCap altitude range to cap to (ignored if less than data range)
+        * @param inFactor altitude exaggeration factor (default 1.0)
         */
-       public void setAltitudeCap(int inAltitudeCap)
+       public void setAltitudeFactor(double inFactor)
        {
-               _altitudeCap = inAltitudeCap;
-               if (_altitudeCap < MINIMUM_ALTITUDE_CAP)
-               {
-                       _altitudeCap = MINIMUM_ALTITUDE_CAP;
+               if (inFactor >= 1.0) {
+                       _altFactor = inFactor;
                }
        }
 
-
        /**
         * Scale all points and calculate factors
         */
@@ -102,21 +94,11 @@ public class ThreeDModel
                                _scaleFactor = _modelSize / _scaler.getMaximumVert();
                        }
                }
-               // calculate altitude scale factor
-               _altFactor = 1.0;
-               if (_scaler.getMaximumAlt() >= 0)
-               {
-                       // limit by altitude cap or by data range?
-                       if (_scaler.getMaximumAlt() > _altitudeCap)
-                       {
-                               // data is bigger than cap
-                               _altFactor = _modelSize / _scaler.getMaximumAlt();
-                       }
-                       else
-                       {
-                               // capped
-                               _altFactor = _modelSize / _altitudeCap;
-                       }
+               // cap altitude scale factor if it's too big
+               double maxScaledAlt = _scaler.getMaxScaledAlt() * _altFactor;
+               if (maxScaledAlt > _modelSize) {
+                       // capped
+                       _altFactor = _altFactor * _modelSize / maxScaledAlt;
                }
                // calculate lat/long lines
                _scaler.calculateLatLongLines();
@@ -138,7 +120,8 @@ public class ThreeDModel
                for (int i=0; i<numPoints; i++)
                {
                        DataPoint point = _track.getPoint(i);
-                       _pointTypes[i] = (point.isWaypoint()?POINT_TYPE_WAYPOINT:(point.getSegmentStart()?POINT_TYPE_SEGMENT_START:POINT_TYPE_NORMAL_POINT));
+                       _pointTypes[i] = (point.isWaypoint()?POINT_TYPE_WAYPOINT:
+                               (point.getSegmentStart()?POINT_TYPE_SEGMENT_START:POINT_TYPE_NORMAL_POINT));
                        _pointHeights[i] = (byte) (point.getAltitude().getValue(Altitude.Format.METRES) / 500);
                }
        }
@@ -172,9 +155,9 @@ public class ThreeDModel
        public double getScaledAltValue(int inIndex)
        {
                // if no altitude, just return 0
-               int altVal = _scaler.getAltValue(inIndex);
+               double altVal = _scaler.getAltValue(inIndex);
                if (altVal < 0) return 0;
-               // scale according to altitude cap
+               // scale according to exaggeration factor
                return altVal * _altFactor;
        }
 
index 4dbdd08f3dc4b6f5c06d9e428662629b6d71e593..10f71c23773be747c372c298151bf0abb90b4e82 100644 (file)
@@ -3,6 +3,7 @@ package tim.prune.undo;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
 import tim.prune.data.Altitude;
+import tim.prune.data.DataPoint;
 import tim.prune.data.TrackInfo;
 
 /**
@@ -54,9 +55,12 @@ public class UndoAddAltitudeOffset implements UndoOperation
                // Perform the inverse operation
                final int numPoints = _altitudes.length;
                for (int i=0; i<numPoints; i++) {
-                       inTrackInfo.getTrack().getPoint(i+_startIndex).getAltitude().reset(_altitudes[i]);
+                       DataPoint point = inTrackInfo.getTrack().getPoint(i+_startIndex);
+                       point.getAltitude().reset(_altitudes[i]);
+                       point.setModified(true);
                }
                _altitudes = null;
+               inTrackInfo.getSelection().markInvalid();
                UpdateMessageBroker.informSubscribers();
        }
 }
index 329d09a3a6b3f059eeea9f0ca6c17b1180370f81..5238213f21c7739d43f5f0aa04dc9001554c6977 100644 (file)
@@ -45,7 +45,7 @@ public class UndoAddTimeOffset implements UndoOperation
        public void performUndo(TrackInfo inTrackInfo) throws UndoException
        {
                // Perform the inverse operation
-               inTrackInfo.getTrack().addTimeOffset(_startIndex, _endIndex, -_timeOffset);
+               inTrackInfo.getTrack().addTimeOffset(_startIndex, _endIndex, -_timeOffset, true);
                UpdateMessageBroker.informSubscribers();
        }
 }
index 3c25837c81e107695035455930b2a5bfe5c39cb6..39c7a86468fcaffde516767591def878307b6c60 100644 (file)
@@ -60,10 +60,14 @@ public class UndoCorrelatePhotos implements UndoOperation
                for (int i=0; i<_photoPoints.length; i++)\r
                {\r
                        Photo photo = inTrackInfo.getPhotoList().getPhoto(i);\r
-                       DataPoint point = _photoPoints[i];\r
-                       photo.setDataPoint(point);\r
-                       if (point != null) {\r
-                               point.setPhoto(photo);\r
+                       // Only need to look at connected photos, if they're still tagged then leave them\r
+                       if (photo.getCurrentStatus() == Photo.Status.CONNECTED)\r
+                       {\r
+                               DataPoint point = _photoPoints[i];\r
+                               photo.setDataPoint(point);\r
+                               if (point != null) {\r
+                                       point.setPhoto(photo);\r
+                               }\r
                        }\r
                }\r
                // clear selection\r
diff --git a/tim/prune/undo/UndoDeleteFieldValues.java b/tim/prune/undo/UndoDeleteFieldValues.java
new file mode 100644 (file)
index 0000000..adad922
--- /dev/null
@@ -0,0 +1,75 @@
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.UpdateMessageBroker;\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.Field;\r
+import tim.prune.data.Track;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo the deletion of field values\r
+ */\r
+public class UndoDeleteFieldValues implements UndoOperation\r
+{\r
+       /** Start and end indices of section */\r
+       private int _startIndex, _endIndex;\r
+       /** Field to be deleted */\r
+       private Field _field = null;\r
+       /** Field values before operation */\r
+       private String[] _fieldValues = null;\r
+\r
+       /**\r
+        * Constructor\r
+        * @param inTrackInfo track info object to copy values from\r
+        * @param inField field to delete\r
+        */\r
+       public UndoDeleteFieldValues(TrackInfo inTrackInfo, Field inField)\r
+       {\r
+               _startIndex = inTrackInfo.getSelection().getStart();\r
+               _endIndex = inTrackInfo.getSelection().getEnd();\r
+               final int numPoints = _endIndex - _startIndex + 1;\r
+               _fieldValues = new String[numPoints];\r
+               _field = inField;\r
+               // Loop over points in selection, and copy field values\r
+               for (int i=_startIndex; i<=_endIndex; i++)\r
+               {\r
+                       DataPoint point = inTrackInfo.getTrack().getPoint(i);\r
+                       _fieldValues[i-_startIndex] = point.getFieldValue(inField);\r
+               }\r
+       }\r
+\r
+\r
+       /**\r
+        * @return description of operation\r
+        */\r
+       public String getDescription()\r
+       {\r
+               return I18nManager.getText("undo.deletefieldvalues");\r
+       }\r
+\r
+\r
+       /**\r
+        * Perform the undo operation on the given Track\r
+        * @param inTrackInfo TrackInfo object on which to perform the operation\r
+        */\r
+       public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+       {\r
+               // Sanity check\r
+               Track track = inTrackInfo.getTrack();\r
+               if (track.getNumPoints() <= _endIndex || _endIndex <= _startIndex) {\r
+                       throw new UndoException("Cannot undo conversion, track length doesn't match");\r
+               }\r
+               // Loop over points in selection and replace field values\r
+               for (int i=_startIndex; i<=_endIndex; i++)\r
+               {\r
+                       String storedValue = _fieldValues[i-_startIndex];\r
+                       if (storedValue != null) {\r
+                               track.getPoint(i).setFieldValue(_field, storedValue, true);\r
+                       }\r
+               }\r
+               track.requestRescale();\r
+               inTrackInfo.getSelection().markInvalid();\r
+               UpdateMessageBroker.informSubscribers();\r
+       }\r
+}\r
index 2672bd55d95cf10a3576d30b6f90870a71fa4a23..d82714ae1e6444ce18fda96d1d2cddbc1a0fcff9 100644 (file)
@@ -14,6 +14,8 @@ public class UndoLookupSrtm implements UndoOperation
 {
        /** DataPoint objects which didn't have altitudes before */
        private DataPoint[] _points;
+       /** Altitude strings if present */
+       private String[] _altitudes;
 
 
        /**
@@ -24,12 +26,16 @@ public class UndoLookupSrtm implements UndoOperation
        {
                Track track = inTrackInfo.getTrack();
                int numPoints = track.getNumPoints();
-               // Make array of points without altitudes
+               // Make arrays of points and altitudes
                _points = new DataPoint[numPoints];
+               _altitudes = new String[numPoints];
                for (int i=0; i<numPoints; i++) {
                        DataPoint point = track.getPoint(i);
-                       if (!point.hasAltitude()) {
+                       if (!point.hasAltitude() || point.getAltitude().getValue() == 0) {
                                _points[i] = point;
+                               if (point.hasAltitude()) {
+                                       _altitudes[i] = point.getFieldValue(Field.ALTITUDE);
+                               }
                        }
                }
        }
@@ -55,7 +61,12 @@ public class UndoLookupSrtm implements UndoOperation
                for (int i=0; i<numPoints; i++) {
                        DataPoint point = _points[i];
                        if (point != null && point.hasAltitude()) {
-                               point.setFieldValue(Field.ALTITUDE, null, true);
+                               if (_altitudes[i] == null) {
+                                       point.setFieldValue(Field.ALTITUDE, null, true);
+                               }
+                               else {
+                                       point.setFieldValue(Field.ALTITUDE, _altitudes[i], true);
+                               }
                        }
                }
                _points = null;