[gpsprune] 01/07: Imported Upstream version 16.2
David Paleino
dapal at debian.org
Thu Jul 3 08:57:09 UTC 2014
This is an automated email from the git hooks/post-receive script.
dapal pushed a commit to annotated tag debian/16.2-1
in repository gpsprune.
commit 279ef3c52e285bd729d3a9827e1890e67713cdd6
Author: David Paleino <dapal at debian.org>
Date: Wed Jul 2 21:44:13 2014 +0200
Imported Upstream version 16.2
---
build.sh | 2 +-
tim/prune/App.java | 31 +-
tim/prune/DataStatus.java | 34 ++
tim/prune/ExternalTools.java | 11 +
tim/prune/FunctionLibrary.java | 15 +
tim/prune/GpsPrune.java | 6 +-
tim/prune/I18nManager.java | 18 +
tim/prune/UpdateMessageBroker.java | 14 +
tim/prune/config/Config.java | 29 ++
tim/prune/copyright.txt | 2 +-
tim/prune/correlate/Correlator.java | 17 +-
tim/prune/data/DataPoint.java | 42 +-
tim/prune/data/DoubleRange.java | 12 +
tim/prune/data/PointScaler.java | 83 +++-
tim/prune/data/RangeStats.java | 23 +-
tim/prune/data/Selection.java | 46 +-
tim/prune/data/Track.java | 16 +-
tim/prune/function/AboutScreen.java | 19 +-
tim/prune/function/AddMapSourceDialog.java | 159 +------
tim/prune/function/DeleteFieldValues.java | 4 +-
tim/prune/function/Export3dFunction.java | 22 +
tim/prune/function/FieldListModel.java | 4 +-
tim/prune/function/FindWaypoint.java | 4 +-
tim/prune/function/FullRangeDetails.java | 3 +-
tim/prune/function/GetWikipediaXmlHandler.java | 4 +-
tim/prune/function/MapSourceListModel.java | 4 +-
tim/prune/function/PasteCoordinates.java | 4 +-
tim/prune/function/RearrangeWaypointsFunction.java | 1 +
tim/prune/function/SelectTracksFunction.java | 4 +-
tim/prune/function/SetLanguage.java | 8 +-
tim/prune/function/SetMapBgFunction.java | 4 +-
tim/prune/function/ShowThreeDFunction.java | 127 +++++-
tim/prune/function/browser/BrowserLauncher.java | 11 +-
tim/prune/function/cache/ManageCacheFunction.java | 3 +-
.../function/compress/CompressTrackFunction.java | 28 +-
.../function/compress/DouglasPeuckerAlgorithm.java | 44 +-
.../compress/MarkPointsInRectangleFunction.java | 35 +-
tim/prune/function/estimate/EstimateTime.java | 16 +-
.../function/estimate/EstimationParameters.java | 13 +
tim/prune/function/estimate/LearnParameters.java | 2 +-
tim/prune/function/sew/CandidateSorter.java | 29 ++
tim/prune/function/sew/SegmentEnd.java | 197 +++++++++
.../function/sew/SewTrackSegmentsFunction.java | 331 ++++++++++++++
tim/prune/function/sew/SplitPoint.java | 101 +++++
tim/prune/function/sew/SplitSegmentsFunction.java | 285 ++++++++++++
tim/prune/function/srtm/DownloadSrtmFunction.java | 222 ++++++++++
tim/prune/function/srtm/LookupSrtmFunction.java | 207 ++++++---
tim/prune/function/srtm/SrtmTile.java | 13 +-
.../weather/GetWeatherForecastFunction.java | 480 +++++++++++++++++++++
tim/prune/function/weather/IconRenderer.java | 37 ++
tim/prune/function/weather/OWMCurrentHandler.java | 92 ++++
tim/prune/function/weather/OWMForecastHandler.java | 102 +++++
tim/prune/function/weather/ResultSet.java | 83 ++++
tim/prune/function/weather/SingleForecast.java | 185 ++++++++
tim/prune/function/weather/WeatherResults.java | 166 +++++++
tim/prune/function/weather/WeatherTableModel.java | 124 ++++++
tim/prune/gui/BaseImageDefinitionPanel.java | 157 +++++++
tim/prune/gui/DecimalNumberField.java | 3 +-
tim/prune/gui/DetailsDisplay.java | 17 +-
tim/prune/gui/DisplayUtils.java | 20 +
.../GenericProgressDialog.java} | 32 +-
tim/prune/gui/MediaListModel.java | 4 +-
tim/prune/gui/MenuManager.java | 57 ++-
tim/prune/gui/ProgressDialog.java | 8 +-
tim/prune/gui/SelectorDisplay.java | 14 +-
tim/prune/gui/TerrainDefinitionPanel.java | 85 ++++
tim/prune/gui/UndoManager.java | 4 +-
tim/prune/gui/WaypointListModel.java | 4 +-
tim/prune/gui/WaypointNameMatcher.java | 4 +-
tim/prune/gui/images/weather-clear-day.png | Bin 0 -> 3088 bytes
tim/prune/gui/images/weather-clear-night.png | Bin 0 -> 3063 bytes
tim/prune/gui/images/weather-clouds-day.png | Bin 0 -> 3766 bytes
tim/prune/gui/images/weather-clouds-night.png | Bin 0 -> 3824 bytes
tim/prune/gui/images/weather-clouds.png | Bin 0 -> 2618 bytes
tim/prune/gui/images/weather-extreme.png | Bin 0 -> 1533 bytes
tim/prune/gui/images/weather-fog.png | Bin 0 -> 1521 bytes
tim/prune/gui/images/weather-hail.png | Bin 0 -> 1708 bytes
tim/prune/gui/images/weather-lightrain.png | Bin 0 -> 1731 bytes
tim/prune/gui/images/weather-rain.png | Bin 0 -> 2184 bytes
tim/prune/gui/images/weather-snow.png | Bin 0 -> 2106 bytes
tim/prune/gui/images/weather-storm.png | Bin 0 -> 2984 bytes
tim/prune/gui/map/CloudmadeMapSource.java | 61 ---
tim/prune/gui/map/DiskTileCacher.java | 13 +-
tim/prune/gui/map/MapCanvas.java | 55 ++-
tim/prune/gui/map/MapSourceLibrary.java | 2 -
tim/prune/gui/map/MapTileManager.java | 110 +++--
tim/prune/gui/map/TileConsumer.java | 10 +
tim/prune/gui/map/TileDownloader.java | 3 +
tim/prune/jpeg/ExifGateway.java | 21 +-
tim/prune/jpeg/ExternalExifLibrary.java | 64 +--
tim/prune/lang/prune-texts_af.properties | 15 +-
tim/prune/lang/prune-texts_cz.properties | 78 +++-
tim/prune/lang/prune-texts_da.properties | 84 ++++
tim/prune/lang/prune-texts_de.properties | 77 +++-
tim/prune/lang/prune-texts_de_CH.properties | 97 +++--
tim/prune/lang/prune-texts_en.properties | 77 +++-
tim/prune/lang/prune-texts_en_US.properties | 1 +
tim/prune/lang/prune-texts_es.properties | 85 +++-
tim/prune/lang/prune-texts_fa.properties | 73 ++++
tim/prune/lang/prune-texts_fr.properties | 90 +++-
tim/prune/lang/prune-texts_hu.properties | 210 +++++++--
tim/prune/lang/prune-texts_in.properties | 111 +++++
tim/prune/lang/prune-texts_it.properties | 77 +++-
tim/prune/lang/prune-texts_ja.properties | 26 +-
tim/prune/lang/prune-texts_ko.properties | 25 +-
tim/prune/lang/prune-texts_nl.properties | 77 +++-
tim/prune/lang/prune-texts_pl.properties | 65 ++-
tim/prune/lang/prune-texts_pt.properties | 217 ++++++++--
tim/prune/lang/prune-texts_ro.properties | 97 ++++-
tim/prune/lang/prune-texts_ru.properties | 55 ++-
tim/prune/lang/prune-texts_sv.properties | 58 +++
tim/prune/lang/prune-texts_tr.properties | 16 +-
tim/prune/lang/prune-texts_uk.properties | 234 ++++++++++
tim/prune/lang/prune-texts_zh.properties | 90 +++-
tim/prune/load/BabelFileFormats.java | 4 +-
tim/prune/load/BabelLoadFromFile.java | 39 +-
tim/prune/load/FileCacher.java | 4 +-
tim/prune/load/MediaLoadProgressDialog.java | 93 +---
tim/prune/load/TextFileLoader.java | 18 +-
tim/prune/load/babel/DiscardFilter.java | 4 +-
tim/prune/load/babel/DistanceFilter.java | 4 +-
tim/prune/load/babel/InterpolateFilter.java | 4 +-
tim/prune/load/babel/SimplifyFilter.java | 4 +-
tim/prune/load/xml/GzipFileLoader.java | 6 +-
tim/prune/load/xml/XmlFileLoader.java | 60 ++-
tim/prune/load/xml/ZipFileLoader.java | 4 +-
tim/prune/readme.txt | 41 +-
tim/prune/save/BaseImageConfigDialog.java | 197 ++++++---
tim/prune/save/BaseImageConsumer.java | 10 +
tim/prune/save/ExifSaver.java | 9 +-
tim/prune/save/GpxExporter.java | 73 +---
tim/prune/save/GroutedImage.java | 7 -
tim/prune/save/ImageExporter.java | 139 +++---
tim/prune/save/KmlExporter.java | 8 +-
tim/prune/save/MapGrouter.java | 69 ++-
tim/prune/save/PovExporter.java | 255 ++++++-----
tim/prune/save/SvgExporter.java | 9 +-
tim/prune/save/xml/ByteBuffer.java | 88 ++++
tim/prune/save/xml/GpxSlicer.java | 68 ++-
tim/prune/save/xml/XmlUtils.java | 72 ++++
tim/prune/threedee/ImageDefinition.java | 66 +++
tim/prune/threedee/Java3DWindow.java | 208 +++++++--
tim/prune/threedee/TerrainCache.java | 57 +++
tim/prune/threedee/TerrainDefinition.java | 68 +++
tim/prune/threedee/TerrainHelper.java | 477 ++++++++++++++++++++
tim/prune/threedee/ThreeDModel.java | 53 ++-
tim/prune/threedee/ThreeDWindow.java | 20 +
tim/prune/threedee/WindowFactory.java | 6 +-
tim/prune/tips/TipDefinition.java | 66 +++
tim/prune/tips/TipManager.java | 45 ++
tim/prune/undo/UndoAddAltitudeOffset.java | 3 +-
tim/prune/undo/UndoCutAndMove.java | 1 +
tim/prune/undo/UndoDeleteAudio.java | 4 +-
tim/prune/undo/UndoDeleteOperation.java | 38 ++
tim/prune/undo/UndoDeletePhoto.java | 4 +-
tim/prune/undo/UndoDeletePoint.java | 4 +-
tim/prune/undo/UndoDeleteRange.java | 17 +-
tim/prune/undo/UndoReorder.java | 3 +-
tim/prune/undo/UndoSewSegments.java | 44 ++
tim/prune/undo/UndoSplitSegments.java | 23 +
tim/prune/undo/UndoStack.java | 24 ++
161 files changed, 7447 insertions(+), 1434 deletions(-)
diff --git a/build.sh b/build.sh
index 329c51f..d6f0c68 100644
--- a/build.sh
+++ b/build.sh
@@ -1,6 +1,6 @@
# Build script using external exif library
# Version number
-PRUNENAME=gpsprune_15.1
+PRUNENAME=gpsprune_16.2
# remove compile directory
rm -rf compile
# remove dist directory
diff --git a/tim/prune/App.java b/tim/prune/App.java
index 686b3de..e57dcf6 100644
--- a/tim/prune/App.java
+++ b/tim/prune/App.java
@@ -41,6 +41,7 @@ import tim.prune.load.MediaLinkInfo;
import tim.prune.load.TrackNameList;
import tim.prune.save.ExifSaver;
import tim.prune.save.FileSaver;
+import tim.prune.tips.TipManager;
import tim.prune.undo.*;
@@ -59,7 +60,7 @@ public class App
private FileLoader _fileLoader = null;
private JpegLoader _jpegLoader = null;
private FileSaver _fileSaver = null;
- private Stack<UndoOperation> _undoStack = null;
+ private UndoStack _undoStack = null;
private boolean _mangleTimestampsConfirmed = false;
private Viewport _viewport = null;
private ArrayList<File> _dataFiles = null;
@@ -78,7 +79,7 @@ public class App
public App(JFrame inFrame)
{
_frame = inFrame;
- _undoStack = new Stack<UndoOperation>();
+ _undoStack = new UndoStack();
_track = new Track();
_trackInfo = new TrackInfo(_track);
FunctionLibrary.initialise(this);
@@ -119,6 +120,22 @@ public class App
return _undoStack;
}
+
+ /**
+ * Show the specified tip if appropriate
+ * @param inTipNumber tip number from TipManager
+ */
+ public void showTip(int inTipNumber)
+ {
+ String key = TipManager.fireTipTrigger(inTipNumber);
+ if (key != null && !key.equals(""))
+ {
+ JOptionPane.showMessageDialog(_frame, I18nManager.getText(key),
+ I18nManager.getText("tip.title"), JOptionPane.INFORMATION_MESSAGE);
+ }
+ }
+
+
/**
* Load the specified data files one by one
* @param inDataFiles arraylist containing File objects to load
@@ -511,8 +528,8 @@ public class App
// ensure track's field list contains point's fields
_track.extendFieldList(inPoint.getFieldList());
_trackInfo.selectPoint(inIndex);
- final int selStart = _trackInfo.getSelection().getStart();
- final int selEnd = _trackInfo.getSelection().getEnd();
+ final int selStart = _trackInfo.getSelection().getStart();
+ final int selEnd = _trackInfo.getSelection().getEnd();
if (selStart < inIndex && selEnd > inIndex)
{
// Extend end of selection by 1
@@ -894,6 +911,12 @@ public class App
UpdateMessageBroker.informSubscribers();
}
+ /**
+ * @return the current data status, used for later comparison
+ */
+ public DataStatus getCurrentDataStatus() {
+ return new DataStatus(_undoStack.size(), _undoStack.getNumTimesDeleted());
+ }
/**
* Show a map url in an external browser
diff --git a/tim/prune/DataStatus.java b/tim/prune/DataStatus.java
new file mode 100644
index 0000000..cacb288
--- /dev/null
+++ b/tim/prune/DataStatus.java
@@ -0,0 +1,34 @@
+package tim.prune;
+
+/**
+ * Class to remember the current status of the data,
+ * and make it possible to see whether the data has
+ * changed in any way since the DataStatus was requested
+ */
+public class DataStatus
+{
+ private int _undoSize = 0;
+ private int _deleteCount = 0;
+
+ /**
+ * Constructor
+ * @param inUndoSize current size of undo stack
+ * @param inDeleteCount number of times undo stack has been deleted
+ */
+ public DataStatus(int inUndoSize, int inDeleteCount)
+ {
+ _undoSize = inUndoSize;
+ _deleteCount = inDeleteCount;
+ }
+
+ /**
+ * Has the data changed compared to the previous status?
+ * @param inPreviousStatus previous status obtained from App
+ * @return true if the status is now different
+ */
+ public boolean hasDataChanged(DataStatus inPreviousStatus)
+ {
+ return _undoSize != inPreviousStatus._undoSize
+ || _deleteCount != inPreviousStatus._deleteCount;
+ }
+}
diff --git a/tim/prune/ExternalTools.java b/tim/prune/ExternalTools.java
index 31c0302..8cd1aef 100644
--- a/tim/prune/ExternalTools.java
+++ b/tim/prune/ExternalTools.java
@@ -16,6 +16,8 @@ public abstract class ExternalTools
public static final int TOOL_GPSBABEL = 1;
/** Constant for Gnuplot */
public static final int TOOL_GNUPLOT = 2;
+ /** Constant for Xerces xml library */
+ public static final int TOOL_XERCES = 3;
/** Config keys in order that the tools are defined above */
private static final String[] CONFIG_KEYS = {Config.KEY_EXIFTOOL_PATH, Config.KEY_GPSBABEL_PATH, Config.KEY_GNUPLOT_PATH};
/** Verification flags for the tools in the order defined above */
@@ -37,6 +39,15 @@ public abstract class ExternalTools
if (toolPath != null && toolPath.length() > 0) {
return check(toolPath + " " + VERIFY_FLAGS[inToolNum]);
}
+ break;
+ case TOOL_XERCES:
+ try {
+ return Class.forName("org.apache.xerces.parsers.SAXParser").getClassLoader() != null;
+ }
+ catch (ClassNotFoundException e) {
+ // System.err.println(e.getClass().getName() + " : " + e.getMessage());
+ }
+ break;
}
// Not found
return false;
diff --git a/tim/prune/FunctionLibrary.java b/tim/prune/FunctionLibrary.java
index 53d8563..9c51c8d 100644
--- a/tim/prune/FunctionLibrary.java
+++ b/tim/prune/FunctionLibrary.java
@@ -5,13 +5,18 @@ import tim.prune.correlate.PhotoCorrelator;
import tim.prune.function.*;
import tim.prune.function.charts.Charter;
import tim.prune.function.compress.CompressTrackFunction;
+import tim.prune.function.compress.MarkPointsInRectangleFunction;
import tim.prune.function.distance.DistanceFunction;
import tim.prune.function.edit.PointNameEditor;
import tim.prune.function.estimate.EstimateTime;
import tim.prune.function.estimate.LearnParameters;
import tim.prune.function.gpsies.GetGpsiesFunction;
import tim.prune.function.gpsies.UploadGpsiesFunction;
+import tim.prune.function.sew.SewTrackSegmentsFunction;
+import tim.prune.function.sew.SplitSegmentsFunction;
+import tim.prune.function.srtm.DownloadSrtmFunction;
import tim.prune.function.srtm.LookupSrtmFunction;
+import tim.prune.function.weather.GetWeatherForecastFunction;
import tim.prune.load.AudioLoader;
import tim.prune.load.BabelLoadFromFile;
import tim.prune.load.BabelLoadFromGps;
@@ -38,12 +43,16 @@ public abstract class FunctionLibrary
public static GenericFunction FUNCTION_SAVECONFIG = null;
public static GenericFunction FUNCTION_EDIT_WAYPOINT_NAME = null;
public static RearrangeWaypointsFunction FUNCTION_REARRANGE_WAYPOINTS = null;
+ public static GenericFunction FUNCTION_SPLIT_SEGMENTS = null;
+ public static GenericFunction FUNCTION_SEW_SEGMENTS = null;
public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null;
public static GenericFunction FUNCTION_COMPRESS = null;
public static GenericFunction FUNCTION_DELETE_RANGE = null;
public static GenericFunction FUNCTION_CROP_TRACK = null;
+ public static GenericFunction FUNCTION_MARK_IN_RECTANGLE = null;
public static GenericFunction FUNCTION_INTERPOLATE = null;
public static GenericFunction FUNCTION_LOOKUP_SRTM = null;
+ public static GenericFunction FUNCTION_DOWNLOAD_SRTM = null;
public static GenericFunction FUNCTION_LOOKUP_WIKIPEDIA = null;
public static GenericFunction FUNCTION_SEARCH_WIKIPEDIA = null;
public static GenericFunction FUNCTION_DOWNLOAD_OSM = null;
@@ -71,6 +80,7 @@ public abstract class FunctionLibrary
public static GenericFunction FUNCTION_LEARN_ESTIMATION_PARAMS = null;
public static GenericFunction FUNCTION_GET_GPSIES = null;
public static GenericFunction FUNCTION_UPLOAD_GPSIES = null;
+ public static GenericFunction FUNCTION_GET_WEATHER_FORECAST = null;
public static GenericFunction FUNCTION_LOAD_AUDIO = null;
public static GenericFunction FUNCTION_REMOVE_AUDIO = null;
public static GenericFunction FUNCTION_CORRELATE_AUDIOS = null;
@@ -105,12 +115,16 @@ public abstract class FunctionLibrary
FUNCTION_SAVECONFIG = new SaveConfig(inApp);
FUNCTION_EDIT_WAYPOINT_NAME = new PointNameEditor(inApp);
FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp);
+ FUNCTION_SPLIT_SEGMENTS = new SplitSegmentsFunction(inApp);
+ FUNCTION_SEW_SEGMENTS = new SewTrackSegmentsFunction(inApp);
FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp);
FUNCTION_COMPRESS = new CompressTrackFunction(inApp);
FUNCTION_DELETE_RANGE = new DeleteSelectedRangeFunction(inApp);
FUNCTION_CROP_TRACK = new CropToSelection(inApp);
+ FUNCTION_MARK_IN_RECTANGLE = new MarkPointsInRectangleFunction(inApp);
FUNCTION_INTERPOLATE = new InterpolateFunction(inApp);
FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp);
+ FUNCTION_DOWNLOAD_SRTM = new DownloadSrtmFunction(inApp);
FUNCTION_LOOKUP_WIKIPEDIA = new GetWikipediaFunction(inApp);
FUNCTION_SEARCH_WIKIPEDIA = new SearchWikipediaNames(inApp);
FUNCTION_DOWNLOAD_OSM = new DownloadOsmFunction(inApp);
@@ -137,6 +151,7 @@ public abstract class FunctionLibrary
FUNCTION_LEARN_ESTIMATION_PARAMS = new LearnParameters(inApp);
FUNCTION_GET_GPSIES = new GetGpsiesFunction(inApp);
FUNCTION_UPLOAD_GPSIES = new UploadGpsiesFunction(inApp);
+ FUNCTION_GET_WEATHER_FORECAST = new GetWeatherForecastFunction(inApp);
FUNCTION_LOAD_AUDIO = new AudioLoader(inApp);
FUNCTION_REMOVE_AUDIO = new RemoveAudioFunction(inApp);
FUNCTION_CORRELATE_AUDIOS = new AudioCorrelator(inApp);
diff --git a/tim/prune/GpsPrune.java b/tim/prune/GpsPrune.java
index 1f4d6c8..d8e9549 100644
--- a/tim/prune/GpsPrune.java
+++ b/tim/prune/GpsPrune.java
@@ -28,16 +28,16 @@ import tim.prune.gui.profile.ProfileChart;
/**
* GpsPrune is a tool to visualize, edit, convert and prune GPS data
* Please see the included readme.txt or http://activityworkshop.net
- * This software is copyright activityworkshop.net 2006-2013 and made available through the Gnu GPL version 2.
+ * This software is copyright activityworkshop.net 2006-2014 and made available through the Gnu GPL version 2.
* For license details please see the included license.txt.
* GpsPrune is the main entry point to the application, including initialisation and launch
*/
public class GpsPrune
{
/** Version number of application, used in about screen and for version check */
- public static final String VERSION_NUMBER = "15.1";
+ public static final String VERSION_NUMBER = "16.2";
/** Build number, just used for about screen */
- public static final String BUILD_NUMBER = "283a";
+ public static final String BUILD_NUMBER = "303b";
/** Static reference to App object */
private static App APP = null;
diff --git a/tim/prune/I18nManager.java b/tim/prune/I18nManager.java
index 349db3f..24a4fde 100644
--- a/tim/prune/I18nManager.java
+++ b/tim/prune/I18nManager.java
@@ -115,4 +115,22 @@ public abstract class I18nManager
// return the key itself
return inKey;
}
+
+ /**
+ * Lookup the given key and return the associated text, formatting with the number
+ * @param inKey key to lookup (text should contain a %d)
+ * @param inNumber number to substitute into the %d
+ * @return associated text, or the key if not found
+ */
+ public static String getTextWithNumber(String inKey, int inNumber)
+ {
+ String localText = getText(inKey);
+ try
+ {
+ localText = String.format(localText, inNumber);
+ }
+ catch (Exception e)
+ {} // printf formatting didn't work, maybe the placeholders are wrong?
+ return localText;
+ }
}
diff --git a/tim/prune/UpdateMessageBroker.java b/tim/prune/UpdateMessageBroker.java
index 337fc27..c032359 100644
--- a/tim/prune/UpdateMessageBroker.java
+++ b/tim/prune/UpdateMessageBroker.java
@@ -7,8 +7,12 @@ package tim.prune;
public abstract class UpdateMessageBroker
{
private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 6;
+ /** Array of all subscribers */
private static DataSubscriber[] _subscribers = new DataSubscriber[MAXIMUM_NUMBER_SUBSCRIBERS];
+ /** Counter of the number of subscribers added so far */
private static int _subscriberNum = 0;
+ /** Enable/disabled flag */
+ private static boolean _enabled = true;
/**
@@ -21,6 +25,14 @@ public abstract class UpdateMessageBroker
_subscriberNum++;
}
+ /**
+ * Enable or disable the messaging (to allow temporary disabling for multiple operations)
+ * @param inEnabled false to disable, true to enable again
+ */
+ public static void enableMessaging(boolean inEnabled)
+ {
+ _enabled = inEnabled;
+ }
/**
* Send a message to all subscribers that
@@ -39,6 +51,7 @@ public abstract class UpdateMessageBroker
public static void informSubscribers(byte inChange)
{
// TODO: Launch separate thread so that whatever caused the inform can finish
+ if (!_enabled) return;
for (int i=0; i<_subscribers.length; i++)
{
if (_subscribers[i] != null)
@@ -54,6 +67,7 @@ public abstract class UpdateMessageBroker
*/
public static void informSubscribers(String inMessage)
{
+ if (!_enabled) return;
for (int i=0; i<_subscribers.length; i++)
{
if (_subscribers[i] != null)
diff --git a/tim/prune/config/Config.java b/tim/prune/config/Config.java
index 385a5d9..35f5a31 100644
--- a/tim/prune/config/Config.java
+++ b/tim/prune/config/Config.java
@@ -7,6 +7,7 @@ import java.util.Properties;
import tim.prune.data.RecentFileList;
import tim.prune.data.UnitSet;
import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.map.MapSourceLibrary;
/**
@@ -44,6 +45,8 @@ public abstract class Config
public static final String KEY_GPS_FORMAT = "prune.gpsformat";
/** Key for GPSBabel filter string */
public static final String KEY_GPSBABEL_FILTER = "prune.gpsbabelfilter";
+ /** Key for GPSBabel import file format */
+ public static final String KEY_IMPORT_FILE_FORMAT = "prune.lastimportfileformat";
/** Key for Povray font */
public static final String KEY_POVRAY_FONT = "prune.povrayfont";
/** Key for the selected unit set */
@@ -80,6 +83,8 @@ public abstract class Config
public static final String KEY_RECENT_FILES = "prune.recentfiles";
/** Key for estimation parameters */
public static final String KEY_ESTIMATION_PARAMS = "prune.estimationparams";
+ /** Key for 3D exaggeration factor */
+ public static final String KEY_HEIGHT_EXAGGERATION = "prune.heightexaggeration";
/** Initialise the default properties */
@@ -142,6 +147,8 @@ public abstract class Config
_colourScheme.loadFromHex(_configValues.getProperty(KEY_COLOUR_SCHEME));
_recentFiles = new RecentFileList(_configValues.getProperty(KEY_RECENT_FILES));
_unitSet = UnitSetLibrary.getUnitSet(_configValues.getProperty(KEY_UNITSET_KEY));
+ // Adjust map source index if necessary
+ adjustSelectedMap();
if (loadFailed) {
throw new ConfigException();
@@ -164,13 +171,35 @@ public abstract class Config
props.put(KEY_EXIFTOOL_PATH, "exiftool");
props.put(KEY_GNUPLOT_PATH, "gnuplot");
props.put(KEY_GPSBABEL_PATH, "gpsbabel");
+ props.put(KEY_IMPORT_FILE_FORMAT, "-1"); // no file format selected
props.put(KEY_KMZ_IMAGE_SIZE, "240");
props.put(KEY_AUTOSAVE_SETTINGS, "0"); // autosave false by default
props.put(KEY_UNITSET_KEY, "unitset.kilometres"); // metric by default
+ props.put(KEY_HEIGHT_EXAGGERATION, "100"); // 100%, no exaggeration
return props;
}
/**
+ * Adjust the index of the selected map
+ * (only required if config was loaded from a previous version of GpsPrune)
+ */
+ private static void adjustSelectedMap()
+ {
+ int sourceNum = getConfigInt(Config.KEY_MAPSOURCE_INDEX);
+ int prevNumFixed = getConfigInt(Config.KEY_NUM_FIXED_MAPS);
+ // Number of fixed maps not specified in version <=13, default to 6
+ if (prevNumFixed == 0) prevNumFixed = 6;
+ int currNumFixed = MapSourceLibrary.getNumFixedSources();
+ // Only need to do something if the number has changed
+ if (currNumFixed != prevNumFixed && (sourceNum >= prevNumFixed || sourceNum >= currNumFixed))
+ {
+ sourceNum += (currNumFixed - prevNumFixed);
+ setConfigInt(Config.KEY_MAPSOURCE_INDEX, sourceNum);
+ }
+ setConfigInt(Config.KEY_NUM_FIXED_MAPS, currNumFixed);
+ }
+
+ /**
* @param inString String to parse
* @return int value of String, or 0 if unparseable
*/
diff --git a/tim/prune/copyright.txt b/tim/prune/copyright.txt
index 1add3e7..7990765 100644
--- a/tim/prune/copyright.txt
+++ b/tim/prune/copyright.txt
@@ -1,4 +1,4 @@
-The source code of GpsPrune is copyright 2006-2013 activityworkshop.net
+The source code of GpsPrune is copyright 2006-2014 activityworkshop.net
and is distributed under the terms of the Gnu GPL version 2.
Portions of the package jpeg.drew (if included in this package) were taken
diff --git a/tim/prune/correlate/Correlator.java b/tim/prune/correlate/Correlator.java
index 85b96e9..7e887ff 100644
--- a/tim/prune/correlate/Correlator.java
+++ b/tim/prune/correlate/Correlator.java
@@ -37,6 +37,7 @@ import tim.prune.data.Timestamp;
import tim.prune.data.Track;
import tim.prune.data.Unit;
import tim.prune.data.UnitSetLibrary;
+import tim.prune.tips.TipManager;
/**
* Abstract superclass of the two correlator functions
@@ -45,7 +46,6 @@ public abstract class Correlator extends GenericFunction
{
protected JDialog _dialog;
private CardStack _cards = null;
- private JLabel _tipLabel = null;
private JTable _selectionTable = null;
protected JTable _previewTable = null;
private boolean _previewEnabled = false; // flag required to enable preview function on final panel
@@ -55,7 +55,7 @@ public abstract class Correlator extends GenericFunction
private JRadioButton _timeLimitRadio = null, _distLimitRadio = null;
private JTextField _limitMinBox = null, _limitSecBox = null;
private JTextField _limitDistBox = null;
- private JComboBox _distUnitsDropdown = null;
+ private JComboBox<String> _distUnitsDropdown = null;
private JButton _nextButton = null, _backButton = null;
protected JButton _okButton = null;
@@ -117,7 +117,9 @@ public abstract class Correlator extends GenericFunction
_cards.showCard(card);
showCard(0); // does set up and next/prev enabling
_okButton.setEnabled(false);
- _tipLabel.setVisible(!isCardEnabled(1));
+ if (!isCardEnabled(1)) {
+ _app.showTip(TipManager.Tip_ManuallyCorrelateOne);
+ }
_dialog.setVisible(true);
}
@@ -339,9 +341,6 @@ public abstract class Correlator extends GenericFunction
card2.setLayout(new BorderLayout());
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);
JLabel introLabel = new JLabel(I18nManager.getText("dialog.correlate.options.intro"));
introLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
card2Top.add(introLabel);
@@ -400,7 +399,7 @@ public abstract class Correlator extends GenericFunction
noTimeLimitRadio.addItemListener(optionsChangedListener);
noTimeLimitRadio.addActionListener(radioListener);
timeLimitPanel.add(noTimeLimitRadio);
- _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + " : ");
+ _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + ": ");
_timeLimitRadio.addItemListener(optionsChangedListener);
_timeLimitRadio.addActionListener(radioListener);
timeLimitPanel.add(_timeLimitRadio);
@@ -421,7 +420,7 @@ public abstract class Correlator extends GenericFunction
noDistLimitRadio.addItemListener(optionsChangedListener);
noDistLimitRadio.addActionListener(radioListener);
distLimitPanel.add(noDistLimitRadio);
- _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit"));
+ _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit") + ": ");
_distLimitRadio.addItemListener(optionsChangedListener);
_distLimitRadio.addActionListener(radioListener);
distLimitPanel.add(_distLimitRadio);
@@ -431,7 +430,7 @@ public abstract class Correlator extends GenericFunction
distLimitPanel.add(_limitDistBox);
String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"),
I18nManager.getText("units.miles")};
- _distUnitsDropdown = new JComboBox(distUnitsOptions);
+ _distUnitsDropdown = new JComboBox<String>(distUnitsOptions);
_distUnitsDropdown.addItemListener(optionsChangedListener);
distLimitPanel.add(_distUnitsDropdown);
limitsPanel.add(distLimitPanel);
diff --git a/tim/prune/data/DataPoint.java b/tim/prune/data/DataPoint.java
index 4de97ef..c244622 100644
--- a/tim/prune/data/DataPoint.java
+++ b/tim/prune/data/DataPoint.java
@@ -138,7 +138,7 @@ public class DataPoint
* @param inIndex index number starting at zero
* @return field value, or null if not found
*/
- public String getFieldValue(int inIndex)
+ private String getFieldValue(int inIndex)
{
if (_fieldValues == null || inIndex < 0 || inIndex >= _fieldValues.length)
return null;
@@ -338,6 +338,46 @@ public class DataPoint
return (inOther._waypointName != null && inOther._waypointName.equals(_waypointName));
}
+ /**
+ * Add an altitude offset to this point, and keep the point's string value in sync
+ * @param inOffset offset as double
+ * @param inUnit unit of offset, feet or metres
+ * @param inDecimals number of decimal places
+ */
+ public void addAltitudeOffset(double inOffset, Unit inUnit, int inDecimals)
+ {
+ if (hasAltitude())
+ {
+ _altitude.addOffset(inOffset, inUnit, inDecimals);
+ _fieldValues[_fieldList.getFieldIndex(Field.ALTITUDE)] = _altitude.getStringValue(null);
+ setModified(false);
+ }
+ }
+
+ /**
+ * Reset the altitude to the previous value (by an undo)
+ * @param inClone altitude object cloned from earlier
+ */
+ public void resetAltitude(Altitude inClone)
+ {
+ _altitude.reset(inClone);
+ _fieldValues[_fieldList.getFieldIndex(Field.ALTITUDE)] = _altitude.getStringValue(null);
+ setModified(true);
+ }
+
+ /**
+ * Add a time offset to this point
+ * @param inOffset offset to add (-ve to subtract)
+ */
+ public void addTimeOffset(long inOffset)
+ {
+ if (hasTimestamp())
+ {
+ _timestamp.addOffset(inOffset);
+ _fieldValues[_fieldList.getFieldIndex(Field.TIMESTAMP)] = _timestamp.getText();
+ setModified(false);
+ }
+ }
/**
* Set the photo for this data point
diff --git a/tim/prune/data/DoubleRange.java b/tim/prune/data/DoubleRange.java
index 4e36122..3f252ee 100644
--- a/tim/prune/data/DoubleRange.java
+++ b/tim/prune/data/DoubleRange.java
@@ -45,6 +45,18 @@ public class DoubleRange
_empty = false;
}
+ /**
+ * Combine this range with another one
+ * @param inOtherRange other range to add to this one
+ */
+ public void combine(DoubleRange inOtherRange)
+ {
+ if (inOtherRange != null && inOtherRange.getRange() > 1.0)
+ {
+ addValue(inOtherRange.getMinimum());
+ addValue(inOtherRange.getMaximum());
+ }
+ }
/**
* @return true if data values were found
diff --git a/tim/prune/data/PointScaler.java b/tim/prune/data/PointScaler.java
index a133885..446c0a2 100644
--- a/tim/prune/data/PointScaler.java
+++ b/tim/prune/data/PointScaler.java
@@ -5,14 +5,23 @@ package tim.prune.data;
*/
public class PointScaler
{
- // Original data
+ /** Original data */
private Track _track = null;
- // Scaled values
+ /** Secondary data for terrain grid */
+ private Track _terrainTrack = null;
+ // Scaled values for data track
private double[] _xValues = null;
private double[] _yValues = null;
private double[] _altValues = null;
+ // Scaled values for terrain track, if any
+ private double[] _terrainxValues = null;
+ private double[] _terrainyValues = null;
+ private double[] _terrainAltValues = null;
// Altitude range
private double _altitudeRange = 0.0;
+ private double _minAltitudeMetres = 0.0;
+ // Horizontal distance
+ private double _horizDistanceMetres = 0.0;
/**
@@ -24,6 +33,13 @@ public class PointScaler
_track = inTrack;
}
+ /**
+ * @param inTrack terrain track to add
+ */
+ public void addTerrain(Track inTrack)
+ {
+ _terrainTrack = inTrack;
+ }
/**
* Scale the points
@@ -33,12 +49,16 @@ public class PointScaler
// Work out extents
TrackExtents extents = new TrackExtents(_track);
extents.applySquareBorder();
- final double horizDistance = Math.max(extents.getHorizontalDistanceMetres(), 1.0);
+ _horizDistanceMetres = Math.max(extents.getHorizontalDistanceMetres(), 1.0);
final int numPoints = _track.getNumPoints();
- // Find altitude range
- _altitudeRange = extents.getAltitudeRange().getRange() / horizDistance;
- final double minAltitude = extents.getAltitudeRange().getMinimum();
+ // Find altitude range (including terrain)
+ DoubleRange altRangeMetres = extents.getAltitudeRange();
+ if (_terrainTrack != null) {
+ altRangeMetres.combine(new TrackExtents(_terrainTrack).getAltitudeRange());
+ }
+ _altitudeRange = altRangeMetres.getRange() / _horizDistanceMetres;
+ _minAltitudeMetres = altRangeMetres.getMinimum();
// create new arrays for scaled values
if (_xValues == null || _xValues.length != numPoints)
@@ -46,6 +66,12 @@ public class PointScaler
_xValues = new double[numPoints];
_yValues = new double[numPoints];
_altValues = new double[numPoints];
+ if (_terrainTrack != null)
+ {
+ _terrainxValues = new double[_terrainTrack.getNumPoints()];
+ _terrainyValues = new double[_terrainTrack.getNumPoints()];
+ _terrainAltValues = new double[_terrainTrack.getNumPoints()];
+ }
}
final double midXvalue = extents.getXRange().getMidValue();
@@ -60,7 +86,16 @@ public class PointScaler
{
_xValues[p] = (_track.getX(p) - midXvalue) / xyRange;
_yValues[p] = (midYvalue - _track.getY(p)) / xyRange; // y values have to be inverted
- _altValues[p] = (point.getAltitude().getMetricValue() - minAltitude) / horizDistance;
+ _altValues[p] = (point.getAltitude().getMetricValue() - _minAltitudeMetres) / _horizDistanceMetres;
+ }
+ }
+ if (_terrainTrack != null)
+ {
+ for (int p=0; p<_terrainTrack.getNumPoints(); p++)
+ {
+ _terrainxValues[p] = (_terrainTrack.getX(p) - midXvalue) / xyRange;
+ _terrainyValues[p] = (midYvalue - _terrainTrack.getY(p)) / xyRange; // y values have to be inverted
+ _terrainAltValues[p] = (_terrainTrack.getPoint(p).getAltitude().getMetricValue() - _minAltitudeMetres) / _horizDistanceMetres;
}
}
}
@@ -98,10 +133,42 @@ public class PointScaler
}
/**
- * @return altitude range, in metres
+ * @return altitude range as fraction of horizontal range
*/
public double getAltitudeRange()
{
return _altitudeRange;
}
+
+ /**
+ * Get the horizontal value for the specified point
+ * @param inIndex index of point, starting at 0
+ * @return scaled horizontal value
+ */
+ public double getTerrainHorizValue(int inIndex)
+ {
+ return _terrainxValues[inIndex];
+ }
+
+ /**
+ * Get the vertical value for the specified point
+ * @param inIndex index of point, starting at 0
+ * @return scaled vertical value
+ */
+ public double getTerrainVertValue(int inIndex)
+ {
+ return _terrainyValues[inIndex];
+ }
+
+ /**
+ * @param inIndex index of point in terrain track
+ * @return scaled altitude value for the specified terrain point
+ */
+ public double getTerrainAltValue(int inIndex)
+ {
+ if (_terrainAltValues != null) {
+ return _terrainAltValues[inIndex];
+ }
+ return 0.0;
+ }
}
diff --git a/tim/prune/data/RangeStats.java b/tim/prune/data/RangeStats.java
index 0ed8fc7..37d061b 100644
--- a/tim/prune/data/RangeStats.java
+++ b/tim/prune/data/RangeStats.java
@@ -9,6 +9,7 @@ import tim.prune.config.Config;
*/
public class RangeStats
{
+ // MAYBE: Split into basic stats (quick to calculate, for detailsdisplay) and full stats (for other two)
private boolean _valid = false;
private int _numPoints = 0;
private int _startIndex = 0, _endIndex = 0;
@@ -17,7 +18,8 @@ public class RangeStats
private AltitudeRange _gentleAltitudeRange = null, _steepAltitudeRange = null;
private Timestamp _earliestTimestamp = null, _latestTimestamp = null;
private long _movingMilliseconds = 0L;
- private boolean _timestampsIncomplete = false;
+ private boolean _timesIncomplete = false;
+ private boolean _timesOutOfSequence = false;
private double _totalDistanceRads = 0.0, _movingDistanceRads = 0.0;
// Note, maximum speed is not calculated here, use the SpeedData method instead
@@ -123,12 +125,14 @@ public class RangeStats
if (!p.getSegmentStart() && prevPoint != null && prevPoint.hasTimestamp())
{
long millisLater = p.getTimestamp().getMillisecondsSince(prevPoint.getTimestamp());
- if (millisLater < 0) {_timestampsIncomplete = true;}
- _movingMilliseconds += millisLater;
+ if (millisLater < 0) {_timesOutOfSequence = true;}
+ else {
+ _movingMilliseconds += millisLater;
+ }
}
}
- else if (!p.getSegmentStart()) {
- _timestampsIncomplete = true;
+ else {
+ _timesIncomplete = true;
}
prevPoint = p;
@@ -207,9 +211,14 @@ public class RangeStats
return _movingMilliseconds / 1000;
}
- /** @return true if any timestamps are missing or out of sequence */
+ /** @return true if any timestamps are missing */
public boolean getTimestampsIncomplete() {
- return _timestampsIncomplete;
+ return _timesIncomplete;
+ }
+
+ /** @return true if any timestamps are out of sequence */
+ public boolean getTimestampsOutOfSequence() {
+ return _timesOutOfSequence;
}
/** @return total distance in the current distance units (km or mi) */
diff --git a/tim/prune/data/Selection.java b/tim/prune/data/Selection.java
index 6125eac..68654e8 100644
--- a/tim/prune/data/Selection.java
+++ b/tim/prune/data/Selection.java
@@ -17,8 +17,8 @@ public class Selection
private int _currentPhotoIndex = -1;
private int _currentAudioIndex = -1;
private AltitudeRange _altitudeRange = null;
- private long _totalSeconds = 0L, _movingSeconds = 0L;
- private double _angDistance = -1.0, _angMovingDistance = -1.0;
+ private long _movingSeconds = 0L;
+ private double _angMovingDistance = -1.0;
/**
@@ -65,7 +65,8 @@ public class Selection
{
final int numPoints = _track.getNumPoints();
// Recheck if the number of points has changed
- if (numPoints != _prevNumPoints) {
+ if (numPoints != _prevNumPoints)
+ {
_prevNumPoints = numPoints;
check();
}
@@ -73,11 +74,10 @@ public class Selection
{
_altitudeRange = new AltitudeRange();
Altitude altitude = null;
- Timestamp time = null, startTime = null, endTime = null;
- Timestamp previousTime = null;
+ Timestamp time = null, previousTime = null;
DataPoint lastPoint = null, currPoint = null;
- _angDistance = 0.0; _angMovingDistance = 0.0;
- _totalSeconds = 0L; _movingSeconds = 0L;
+ _angMovingDistance = 0.0;
+ _movingSeconds = 0L;
// Loop over points in selection
for (int i=_startIndex; i<=_endIndex; i++)
{
@@ -86,14 +86,17 @@ public class Selection
// Ignore waypoints in altitude calculations
if (!currPoint.isWaypoint() && altitude.isValid())
{
- _altitudeRange.addValue(altitude);
+ if (currPoint.getSegmentStart()) {
+ _altitudeRange.ignoreValue(altitude);
+ }
+ else {
+ _altitudeRange.addValue(altitude);
+ }
}
- // Store the first and last timestamp in the range
+ // Compare timestamps within the segments
time = currPoint.getTimestamp();
if (time.isValid())
{
- if (startTime == null || startTime.isAfter(time)) startTime = time;
- if (endTime == null || time.isAfter(endTime)) endTime = time;
// add moving time
if (!currPoint.getSegmentStart() && previousTime != null && time.isAfter(previousTime)) {
_movingSeconds += time.getSecondsSince(previousTime);
@@ -106,7 +109,6 @@ public class Selection
if (lastPoint != null)
{
double radians = DataPoint.calculateRadiansBetween(lastPoint, currPoint);
- _angDistance += radians;
if (!currPoint.getSegmentStart()) {
_angMovingDistance += radians;
}
@@ -114,9 +116,6 @@ public class Selection
lastPoint = currPoint;
}
}
- if (endTime != null) {
- _totalSeconds = endTime.getSecondsSince(startTime);
- }
}
_valid = true;
}
@@ -152,15 +151,6 @@ public class Selection
/**
- * @return number of seconds spanned by selection
- */
- public long getNumSeconds()
- {
- if (!_valid) recalculate();
- return _totalSeconds;
- }
-
- /**
* @return number of seconds spanned by segments within selection
*/
public long getMovingSeconds()
@@ -170,14 +160,6 @@ public class Selection
}
/**
- * @return distance of Selection in specified units
- */
- public double getDistance()
- {
- return Distance.convertRadiansToDistance(_angDistance);
- }
-
- /**
* @return moving distance of Selection in current units
*/
public double getMovingDistance()
diff --git a/tim/prune/data/Track.java b/tim/prune/data/Track.java
index 8623c58..45810e2 100644
--- a/tim/prune/data/Track.java
+++ b/tim/prune/data/Track.java
@@ -316,13 +316,13 @@ public class Track
// Loop over all points within range
for (int i=inStart; i<=inEnd; i++)
{
- Timestamp timestamp = _dataPoints[i].getTimestamp();
- if (timestamp != null)
+ DataPoint p = _dataPoints[i];
+ if (p != null && p.hasTimestamp())
{
// This point has a timestamp so add the offset to it
foundTimestamp = true;
- timestamp.addOffset(inOffset);
- _dataPoints[i].setModified(inUndo);
+ p.addTimeOffset(inOffset);
+ p.setModified(inUndo);
}
}
return foundTimestamp;
@@ -348,13 +348,13 @@ public class Track
// Loop over all points within range
for (int i=inStart; i<=inEnd; i++)
{
- Altitude alt = _dataPoints[i].getAltitude();
- if (alt != null && alt.isValid())
+ DataPoint p = _dataPoints[i];
+ if (p != null && p.hasAltitude())
{
// This point has an altitude so add the offset to it
foundAlt = true;
- alt.addOffset(inOffset, inUnit, inDecimals);
- _dataPoints[i].setModified(false);
+ p.addAltitudeOffset(inOffset, inUnit, inDecimals);
+ p.setModified(false);
}
}
// needs to be scaled again
diff --git a/tim/prune/function/AboutScreen.java b/tim/prune/function/AboutScreen.java
index 7a95693..5a463a3 100644
--- a/tim/prune/function/AboutScreen.java
+++ b/tim/prune/function/AboutScreen.java
@@ -99,7 +99,7 @@ public class AboutScreen extends GenericFunction
descBuffer.append("<p>").append(I18nManager.getText("dialog.about.languages")).append(" : ")
.append("\u010de\u0161tina, deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano, magyar,<br>" +
" nederlands, polski, portugu\u00EAs, \u0440\u0443\u0441\u0441\u043a\u0438\u0439 (russian), \u4e2d\u6587 (chinese), \u65E5\u672C\u8A9E (japanese),<br>" +
- " \uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean), schwiizerd\u00FC\u00FCtsch, t\u00FCrk\u00E7e, afrikaans, rom\u00E2n\u0103</p>");
+ " \uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean), schwiizerd\u00FC\u00FCtsch, t\u00FCrk\u00E7e, afrikaans, rom\u00E2n\u0103, ukrainian</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));
@@ -130,7 +130,7 @@ public class AboutScreen extends GenericFunction
new JLabel(System.getProperty("java.runtime.version")),
1, 1);
// Create install labels to be populated later
- final int NUM_INSTALL_CHECKS = 4;
+ final int NUM_INSTALL_CHECKS = 5;
_installedLabels = new JLabel[NUM_INSTALL_CHECKS];
for (int i=0; i<NUM_INSTALL_CHECKS; i++) {
_installedLabels[i] = new JLabel("...");
@@ -151,13 +151,15 @@ public class AboutScreen extends GenericFunction
new JLabel(I18nManager.getText("dialog.about.systeminfo.gnuplot") + " : "),
0, 5);
addToGridBagPanel(sysInfoPanel, gridBag, constraints, _installedLabels[3], 1, 5);
+ addToGridBagPanel(sysInfoPanel, gridBag, constraints, new JLabel("Xerces : "), 0, 6);
+ addToGridBagPanel(sysInfoPanel, gridBag, constraints, _installedLabels[4], 1, 6);
// Exif library
addToGridBagPanel(sysInfoPanel, gridBag, constraints,
new JLabel(I18nManager.getText("dialog.about.systeminfo.exiflib") + " : "),
- 0, 6);
+ 0, 7);
final String exiflibkey = "dialog.about.systeminfo.exiflib." + ExifGateway.getDescriptionKey();
addToGridBagPanel(sysInfoPanel, gridBag, constraints,
- new JLabel(I18nManager.getText(exiflibkey)), 1, 6);
+ new JLabel(I18nManager.getText(exiflibkey)), 1, 7);
_tabs.add(I18nManager.getText("dialog.about.systeminfo"), sysInfoPanel);
// Third pane for credits
@@ -198,7 +200,7 @@ public class AboutScreen extends GenericFunction
new JLabel(" katpatuka, R\u00E9mi, Marcus, Ali, Javier, Jeroen, prot_d, Gy\u00F6rgy,"),
1, 5);
addToGridBagPanel(creditsPanel, gridBag, constraints,
- new JLabel(" HooAU, Sergey"),
+ new JLabel(" HooAU, Sergey, P\u00E9ter, serhijdubyk"),
1, 6);
addToGridBagPanel(creditsPanel, gridBag, constraints,
new JLabel(I18nManager.getText("dialog.about.credits.translations") + " : "),
@@ -210,13 +212,13 @@ public class AboutScreen extends GenericFunction
new JLabel(I18nManager.getText("dialog.about.credits.devtools") + " : "),
0, 8);
addToGridBagPanel(creditsPanel, gridBag, constraints,
- new JLabel("Debian Linux, Sun Java, Eclipse, Svn, Gimp, Inkscape"),
+ new JLabel("Debian Linux, Sun Java, OpenJDK, Eclipse, Svn, Gimp, Inkscape"),
1, 8);
addToGridBagPanel(creditsPanel, gridBag, constraints,
new JLabel(I18nManager.getText("dialog.about.credits.othertools") + " : "),
0, 9);
addToGridBagPanel(creditsPanel, gridBag, constraints,
- new JLabel("Openstreetmap, Povray, Exiftool, Google Earth, Gpsbabel, Gnuplot"),
+ new JLabel("Openstreetmap, Povray, Exiftool, Gpsbabel, Gnuplot"),
1, 9);
addToGridBagPanel(creditsPanel, gridBag, constraints,
new JLabel(I18nManager.getText("dialog.about.credits.thanks") + " : "),
@@ -380,7 +382,8 @@ public class AboutScreen extends GenericFunction
String yesText = I18nManager.getText("dialog.about.yes");
String noText = I18nManager.getText("dialog.about.no");
_installedLabels[0].setText(WindowFactory.isJava3dEnabled()?yesText:noText);
- final int[] tools = {ExternalTools.TOOL_EXIFTOOL, ExternalTools.TOOL_GPSBABEL, ExternalTools.TOOL_GNUPLOT};
+ final int[] tools = {ExternalTools.TOOL_EXIFTOOL, ExternalTools.TOOL_GPSBABEL,
+ ExternalTools.TOOL_GNUPLOT, ExternalTools.TOOL_XERCES};
for (int i=0; i<tools.length; i++) {
_installedLabels[i+1].setText(ExternalTools.isToolInstalled(tools[i])?yesText:noText);
}
diff --git a/tim/prune/function/AddMapSourceDialog.java b/tim/prune/function/AddMapSourceDialog.java
index 52a8874..9207ac5 100644
--- a/tim/prune/function/AddMapSourceDialog.java
+++ b/tim/prune/function/AddMapSourceDialog.java
@@ -1,12 +1,10 @@
package tim.prune.function;
import java.awt.BorderLayout;
-import java.awt.CardLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
-import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -24,7 +22,6 @@ import javax.swing.JRadioButton;
import javax.swing.JTextField;
import tim.prune.I18nManager;
-import tim.prune.gui.map.CloudmadeMapSource;
import tim.prune.gui.map.MapSource;
import tim.prune.gui.map.MapSourceLibrary;
import tim.prune.gui.map.OsmMapSource;
@@ -36,18 +33,12 @@ public class AddMapSourceDialog
{
private SetMapBgFunction _parent = null;
private JDialog _addDialog = null;
- private JRadioButton[] _sourceTypeRadios = null;
- private JPanel _cards = null;
private MapSource _originalSource = null;
// controls for osm panel
private JTextField _oNameField = null;
private JTextField _baseUrlField = null, _topUrlField = null;
private JRadioButton[] _baseTypeRadios = null, _topTypeRadios = null;
- private JComboBox _oZoomCombo = null;
- // controls for cloudmade panel
- private JTextField _cNameField = null;
- private JTextField _cStyleField = null;
- private JComboBox _cZoomCombo = null;
+ private JComboBox<Integer> _oZoomCombo = null;
private JButton _okButton = null;
/** array of file types */
@@ -76,31 +67,9 @@ public class AddMapSourceDialog
{
JPanel dialogPanel = new JPanel();
dialogPanel.setLayout(new BorderLayout());
- // Top panel with two radio buttons to select source type
- JPanel radioPanel = new JPanel();
- ButtonGroup radioGroup = new ButtonGroup();
- radioPanel.setLayout(new GridLayout(1, 0));
- _sourceTypeRadios = new JRadioButton[2];
- _sourceTypeRadios[0] = new JRadioButton("Openstreetmap");
- radioGroup.add(_sourceTypeRadios[0]);
- radioPanel.add(_sourceTypeRadios[0]);
- _sourceTypeRadios[1] = new JRadioButton("Cloudmade");
- radioGroup.add(_sourceTypeRadios[1]);
- radioPanel.add(_sourceTypeRadios[1]);
- _sourceTypeRadios[0].setSelected(true);
- // listener for clicks on type radios
- ActionListener typeListener = new ActionListener() {
- public void actionPerformed(ActionEvent arg0) {
- onRadioClicked();
- }
- };
- _sourceTypeRadios[0].addActionListener(typeListener);
- _sourceTypeRadios[1].addActionListener(typeListener);
- radioPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
- dialogPanel.add(radioPanel, BorderLayout.NORTH);
+ // Top panel with spacer
+ dialogPanel.add(new JLabel(" "), BorderLayout.NORTH);
- _cards = new JPanel();
- _cards.setLayout(new CardLayout());
// listener
KeyAdapter keyListener = new KeyAdapter() {
public void keyReleased(KeyEvent e) {
@@ -149,7 +118,7 @@ public class AddMapSourceDialog
c.gridx = 1; c.weightx = 1.0;
gbPanel.add(_baseUrlField, c);
_baseTypeRadios = new JRadioButton[3];
- radioGroup = new ButtonGroup();
+ ButtonGroup radioGroup = new ButtonGroup();
for (int i=0; i<3; i++)
{
_baseTypeRadios[i] = new JRadioButton(FILE_TYPES[i]);
@@ -181,44 +150,20 @@ public class AddMapSourceDialog
// Max zoom
c.gridx = 0; c.gridy = 3;
gbPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.maxzoom")), c);
- _oZoomCombo = new JComboBox();
+ _oZoomCombo = new JComboBox<Integer>();
for (int i=10; i<=20; i++) {
- _oZoomCombo.addItem("" + i);
+ _oZoomCombo.addItem(i);
}
// zoom dropdown needs listener to call enableOk()
_oZoomCombo.addActionListener(okEnabler);
c.gridx = 1;
gbPanel.add(_oZoomCombo, c);
osmPanel.add(gbPanel, BorderLayout.NORTH);
- _cards.add(osmPanel, "card1");
- // Panel for cloudmade source
- JPanel cloudPanel = new JPanel();
- cloudPanel.setBorder(BorderFactory.createEmptyBorder(6, 3, 4, 3));
- // Use a gridlayout inside a borderlayout to avoid stretching
- cloudPanel.setLayout(new BorderLayout());
- JPanel cloudGridPanel = new JPanel();
- cloudGridPanel.setLayout(new GridLayout(0, 2, 5, 5));
- cloudGridPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.sourcename")));
- _cNameField = new JTextField(18);
- _cNameField.addKeyListener(keyListener);
- cloudGridPanel.add(_cNameField);
- cloudGridPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.cloudstyle")));
- _cStyleField = new JTextField(18);
- _cStyleField.addKeyListener(keyListener);
- cloudGridPanel.add(_cStyleField);
- cloudGridPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.maxzoom")));
- _cZoomCombo = new JComboBox();
- for (int i=10; i<=20; i++) {
- _cZoomCombo.addItem("" + i);
- }
- cloudGridPanel.add(_cZoomCombo);
- cloudPanel.add(cloudGridPanel, BorderLayout.NORTH);
- _cards.add(cloudPanel, "card2");
// cards
JPanel holderPanel = new JPanel();
holderPanel.setLayout(new BorderLayout());
- holderPanel.add(_cards, BorderLayout.NORTH);
+ holderPanel.add(osmPanel, BorderLayout.NORTH);
dialogPanel.add(holderPanel, BorderLayout.CENTER);
// button panel at bottom
@@ -267,13 +212,7 @@ public class AddMapSourceDialog
_topUrlField.setText("");
_topTypeRadios[0].setSelected(true);
_oZoomCombo.setSelectedIndex(8);
- _cNameField.setText("");
- _cStyleField.setText("");
- _cZoomCombo.setSelectedIndex(8);
_okButton.setEnabled(false);
- for (int i=0; i<2; i++) {
- _sourceTypeRadios[i].setEnabled(true);
- }
_addDialog.setVisible(true);
}
@@ -287,67 +226,32 @@ public class AddMapSourceDialog
clearAllFields();
return;
}
- boolean sourceFound = false;
- // See if it's a cloudmade source
+
+ // See if it's an osm source
try
{
- CloudmadeMapSource cloudSource = (CloudmadeMapSource) _originalSource;
- sourceFound = true;
- _cNameField.setText(cloudSource.getName());
- _cStyleField.setText(cloudSource.getStyle());
- _cZoomCombo.setSelectedIndex(getZoomIndex(cloudSource.getMaxZoomLevel()));
- _sourceTypeRadios[1].setSelected(true);
+ OsmMapSource osmSource = (OsmMapSource) _originalSource;
+ _oNameField.setText(osmSource.getName());
+ _baseUrlField.setText(osmSource.getBaseUrl(0));
+ int baseType = getBaseType(osmSource.getFileExtension(0));
+ _baseTypeRadios[baseType].setSelected(true);
+ _topUrlField.setText(osmSource.getNumLayers()==0?"":osmSource.getBaseUrl(1));
+ int topType = getBaseType(osmSource.getFileExtension(1));
+ _topTypeRadios[topType].setSelected(true);
+ _oZoomCombo.setSelectedIndex(getZoomIndex(osmSource.getMaxZoomLevel()));
}
catch (ClassCastException cce) {} // ignore, sourceFound flag stays false
- // See if it's an osm source
- if (!sourceFound)
- {
- try
- {
- OsmMapSource osmSource = (OsmMapSource) _originalSource;
- sourceFound = true;
- _oNameField.setText(osmSource.getName());
- _baseUrlField.setText(osmSource.getBaseUrl(0));
- int baseType = getBaseType(osmSource.getFileExtension(0));
- _baseTypeRadios[baseType].setSelected(true);
- _topUrlField.setText(osmSource.getNumLayers()==0?"":osmSource.getBaseUrl(1));
- int topType = getBaseType(osmSource.getFileExtension(1));
- _topTypeRadios[topType].setSelected(true);
- _oZoomCombo.setSelectedIndex(getZoomIndex(osmSource.getMaxZoomLevel()));
- _sourceTypeRadios[0].setSelected(true);
- }
- catch (ClassCastException cce) {} // ignore, sourceFound flag stays false
- }
- for (int i=0; i<2; i++) {
- _sourceTypeRadios[i].setEnabled(false);
- }
- onRadioClicked();
_okButton.setEnabled(false);
_addDialog.setVisible(true);
}
-
- /**
- * React to one of the type radio buttons being clicked
- */
- private void onRadioClicked()
- {
- CardLayout cl = (CardLayout) _cards.getLayout();
- if (_sourceTypeRadios[0].isSelected()) {cl.first(_cards);}
- else {cl.last(_cards);}
- enableOK();
- }
-
/**
* Check the currently entered details and enable the OK button if it looks OK
*/
private void enableOK()
{
- boolean ok = false;
- if (_sourceTypeRadios[0].isSelected()) {ok = isOsmPanelOk();}
- if (_sourceTypeRadios[1].isSelected()) {ok = isCloudPanelOk();}
- _okButton.setEnabled(ok);
+ _okButton.setEnabled(isOsmPanelOk());
}
/**
@@ -371,28 +275,14 @@ public class AddMapSourceDialog
}
/**
- * Check the cloudmade panel if all details are complete
- * @return true if details look ok
- */
- private boolean isCloudPanelOk()
- {
- boolean ok = _cNameField.getText().trim().length() > 1;
- int styleNum = 0;
- try {
- styleNum = Integer.parseInt(_cStyleField.getText());
- }
- catch (NumberFormatException nfe) {}
- return (ok && styleNum > 0);
- }
-
- /**
* Finish by adding the requested source and refreshing the parent
*/
private void finish()
{
MapSource newSource = null;
String origName = (_originalSource == null ? null : _originalSource.getName());
- if (_sourceTypeRadios[0].isSelected())
+
+ if (isOsmPanelOk())
{
// Openstreetmap source
String sourceName = getValidSourcename(_oNameField.getText(), origName);
@@ -402,12 +292,7 @@ public class AddMapSourceDialog
String ext2 = getFileExtension(_topTypeRadios);
newSource = new OsmMapSource(sourceName, url1, ext1, url2, ext2, _oZoomCombo.getSelectedIndex()+10);
}
- else if (_sourceTypeRadios[1].isSelected())
- {
- String sourceName = getValidSourcename(_cNameField.getText(), origName);
- newSource = new CloudmadeMapSource(sourceName, _cStyleField.getText(),
- _cZoomCombo.getSelectedIndex()+10);
- }
+
// Add new source if ok
if (newSource != null)
{
diff --git a/tim/prune/function/DeleteFieldValues.java b/tim/prune/function/DeleteFieldValues.java
index b6c97f5..0c0e44f 100644
--- a/tim/prune/function/DeleteFieldValues.java
+++ b/tim/prune/function/DeleteFieldValues.java
@@ -33,7 +33,7 @@ import tim.prune.undo.UndoDeleteFieldValues;
public class DeleteFieldValues extends GenericFunction
{
private JDialog _dialog = null;
- private JList _fieldList = null;
+ private JList<String> _fieldList = null;
private FieldListModel _listModel = null;
private JButton _okButton = null;
@@ -88,7 +88,7 @@ public class DeleteFieldValues extends GenericFunction
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"});
+ _fieldList = new JList<String>(new String[] {"First field", "Second field"});
// These entries will be replaced by the initDialog method
_fieldList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
_fieldList.addListSelectionListener(new ListSelectionListener() {
diff --git a/tim/prune/function/Export3dFunction.java b/tim/prune/function/Export3dFunction.java
index 9e92e62..d32828a 100644
--- a/tim/prune/function/Export3dFunction.java
+++ b/tim/prune/function/Export3dFunction.java
@@ -2,6 +2,8 @@ package tim.prune.function;
import tim.prune.App;
import tim.prune.GenericFunction;
+import tim.prune.threedee.ImageDefinition;
+import tim.prune.threedee.TerrainDefinition;
/**
* Abstract superclass for pov and svg export functions
@@ -10,6 +12,10 @@ public abstract class Export3dFunction extends GenericFunction
{
/** altitude exaggeration factor */
protected double _altFactor = 5.0;
+ /** definition of terrain */
+ protected TerrainDefinition _terrainDef = null;
+ /** definition of base image */
+ protected ImageDefinition _imageDef = null;
/**
* Required constructor
@@ -36,4 +42,20 @@ public abstract class Export3dFunction extends GenericFunction
_altFactor = inFactor;
}
}
+
+ /**
+ * @param inDefinition terrain definition, or null
+ */
+ public void setTerrainDefinition(TerrainDefinition inDefinition)
+ {
+ _terrainDef = inDefinition;
+ }
+
+ /**
+ * @param inDefinition image definition, or null
+ */
+ public void setImageDefinition(ImageDefinition inDefinition)
+ {
+ _imageDef = inDefinition;
+ }
}
diff --git a/tim/prune/function/FieldListModel.java b/tim/prune/function/FieldListModel.java
index 89a721c..b4589d8 100644
--- a/tim/prune/function/FieldListModel.java
+++ b/tim/prune/function/FieldListModel.java
@@ -7,7 +7,7 @@ import tim.prune.data.Field;
/**
* Class to act as a list model for the delete field values function
*/
-public class FieldListModel extends AbstractListModel
+public class FieldListModel extends AbstractListModel<String>
{
/** ArrayList containing fields */
private ArrayList<Field> _fields = new ArrayList<Field>();
@@ -34,7 +34,7 @@ public class FieldListModel extends AbstractListModel
* @param inRow row number
* @return String for specified row
*/
- public Object getElementAt(int inRow)
+ public String getElementAt(int inRow)
{
if (inRow < 0 || inRow >= getSize()) {return null;}
return _fields.get(inRow).getName();
diff --git a/tim/prune/function/FindWaypoint.java b/tim/prune/function/FindWaypoint.java
index 8b6beea..ac0bd37 100644
--- a/tim/prune/function/FindWaypoint.java
+++ b/tim/prune/function/FindWaypoint.java
@@ -34,7 +34,7 @@ public class FindWaypoint extends GenericFunction
private WaypointNameMatcher _nameMatcher = null;
private JDialog _dialog = null;
private JTextField _searchField = null;
- private JList _pointList = null;
+ private JList<String> _pointList = null;
private JButton _okButton = null;
@@ -108,7 +108,7 @@ public class FindWaypoint extends GenericFunction
// middle panel with list
_nameMatcher = new WaypointNameMatcher();
- _pointList = new JList(_nameMatcher);
+ _pointList = new JList<String>(_nameMatcher);
_pointList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
{
diff --git a/tim/prune/function/FullRangeDetails.java b/tim/prune/function/FullRangeDetails.java
index d9dbb70..07ef39e 100644
--- a/tim/prune/function/FullRangeDetails.java
+++ b/tim/prune/function/FullRangeDetails.java
@@ -251,7 +251,8 @@ public class FullRangeDetails extends GenericFunction
final boolean isMultiSegments = (stats.getNumSegments() > 1);
// Set visibility of third column accordingly
_movingDistanceLabel.setVisible(isMultiSegments);
- _movingDurationLabel.setVisible(isMultiSegments);
+ _movingDurationLabel.setVisible(isMultiSegments || stats.getTimestampsOutOfSequence());
+ // FIXME: What to show if timestamps are out of sequence? Warning message?
_movingClimbLabel.setVisible(isMultiSegments);
_movingDescentLabel.setVisible(isMultiSegments);
_movingSpeedLabel.setVisible(isMultiSegments);
diff --git a/tim/prune/function/GetWikipediaXmlHandler.java b/tim/prune/function/GetWikipediaXmlHandler.java
index d9a3b74..f70e5f7 100644
--- a/tim/prune/function/GetWikipediaXmlHandler.java
+++ b/tim/prune/function/GetWikipediaXmlHandler.java
@@ -9,7 +9,7 @@ import org.xml.sax.helpers.DefaultHandler;
import tim.prune.function.gpsies.GpsiesTrack;
/**
- * XML handler for dealing with XML returned from gpsies.com
+ * XML handler for dealing with XML returned from the geonames api
*/
public class GetWikipediaXmlHandler extends DefaultHandler
{
@@ -71,7 +71,7 @@ public class GetWikipediaXmlHandler extends DefaultHandler
catch (NumberFormatException nfe) {}
}
else if (inTagName.equals("wikipediaUrl")) {
- _track.setWebUrl(_value);
+ _track.setWebUrl(_value.replaceFirst("http://", "https://"));
}
super.endElement(inUri, inLocalName, inTagName);
}
diff --git a/tim/prune/function/MapSourceListModel.java b/tim/prune/function/MapSourceListModel.java
index 4548cc4..489f95d 100644
--- a/tim/prune/function/MapSourceListModel.java
+++ b/tim/prune/function/MapSourceListModel.java
@@ -8,7 +8,7 @@ import tim.prune.gui.map.MapSourceLibrary;
/**
* Class to act as list model for the map source list
*/
-public class MapSourceListModel extends AbstractListModel
+public class MapSourceListModel extends AbstractListModel<String>
{
/**
* @see javax.swing.ListModel#getSize()
@@ -21,7 +21,7 @@ public class MapSourceListModel extends AbstractListModel
/**
* @see javax.swing.ListModel#getElementAt(int)
*/
- public Object getElementAt(int inIndex)
+ public String getElementAt(int inIndex)
{
if (inIndex < 0 || inIndex >= getSize()) return "";
return MapSourceLibrary.getSource(inIndex).getName();
diff --git a/tim/prune/function/PasteCoordinates.java b/tim/prune/function/PasteCoordinates.java
index 0228014..c9b2ad1 100644
--- a/tim/prune/function/PasteCoordinates.java
+++ b/tim/prune/function/PasteCoordinates.java
@@ -43,7 +43,7 @@ public class PasteCoordinates extends GenericFunction
private JTextField _nameField = null;
private JTextField _coordField = null;
private JButton _okButton = null;
- private JComboBox _altUnitsDropDown;
+ private JComboBox<String> _altUnitsDropDown;
/**
@@ -122,7 +122,7 @@ public class PasteCoordinates extends GenericFunction
formatLabel.setHorizontalAlignment(SwingConstants.RIGHT);
grid.add(formatLabel);
final String[] altunits = {I18nManager.getText("units.metres"), I18nManager.getText("units.feet")};
- _altUnitsDropDown = new JComboBox(altunits);
+ _altUnitsDropDown = new JComboBox<String>(altunits);
grid.add(_altUnitsDropDown);
// Waypoint name
JLabel nameLabel = new JLabel(I18nManager.getText("dialog.pointnameedit.name"));
diff --git a/tim/prune/function/RearrangeWaypointsFunction.java b/tim/prune/function/RearrangeWaypointsFunction.java
index 9007391..e04aea0 100644
--- a/tim/prune/function/RearrangeWaypointsFunction.java
+++ b/tim/prune/function/RearrangeWaypointsFunction.java
@@ -64,6 +64,7 @@ public class RearrangeWaypointsFunction extends GenericFunction
}
if (success)
{
+ _app.getTrackInfo().getSelection().clearAll(); // clear selected point and range
_app.completeFunction(undo, I18nManager.getText("confirm.rearrangewaypoints"));
}
else
diff --git a/tim/prune/function/SelectTracksFunction.java b/tim/prune/function/SelectTracksFunction.java
index f2fe23b..fa06dae 100644
--- a/tim/prune/function/SelectTracksFunction.java
+++ b/tim/prune/function/SelectTracksFunction.java
@@ -32,7 +32,7 @@ public class SelectTracksFunction extends GenericFunction
private SourceInfo _sourceInfo = null;
private TrackNameList _trackNameList = null;
private JDialog _dialog = null;
- private JList _trackList = null;
+ private JList<String> _trackList = null;
/**
* Constructor
@@ -82,7 +82,7 @@ public class SelectTracksFunction extends GenericFunction
}
names[i] = name + " (" + _trackNameList.getNumPointsInTrack(i) + ")";
}
- _trackList = new JList(names);
+ _trackList = new JList<String>(names);
_trackList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
mainPanel.add(new JScrollPane(_trackList), BorderLayout.CENTER);
// select all button
diff --git a/tim/prune/function/SetLanguage.java b/tim/prune/function/SetLanguage.java
index 3962787..27ec94e 100644
--- a/tim/prune/function/SetLanguage.java
+++ b/tim/prune/function/SetLanguage.java
@@ -36,7 +36,7 @@ import tim.prune.load.GenericFileFilter;
public class SetLanguage extends GenericFunction
{
private JDialog _dialog = null;
- private JComboBox _languageDropDown = null;
+ private JComboBox<String> _languageDropDown = null;
private JTextField _langFileBox = null;
private int _startIndex = 0;
@@ -45,11 +45,11 @@ public class SetLanguage extends GenericFunction
"espa\u00F1ol", "fran\u00E7ais", "italiano", "magyar", "nederlands", "polski",
"portugu\u00EAs", "\u0440\u0443\u0441\u0441\u043a\u0438\u0439 (russian)", "\u4e2d\u6587 (chinese)", "\u65E5\u672C\u8A9E (japanese)",
"\uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean)", "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e",
- "afrikaans", "rom\u00E2n\u0103"
+ "afrikaans", "rom\u00E2n\u0103", "\u0443\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430 \u043c\u043e\u0432\u0430 (ukrainian)"
};
/** Associated language codes (must be in same order as names!) */
private static final String[] LANGUAGE_CODES = {"cz", "de", "en", "en_us", "es", "fr", "it", "hu",
- "nl", "pl", "pt", "ru", "zh", "ja", "ko", "de_ch", "tr", "af", "ro"
+ "nl", "pl", "pt", "ru", "zh", "ja", "ko", "de_ch", "tr", "af", "ro", "uk"
};
@@ -99,7 +99,7 @@ public class SetLanguage extends GenericFunction
builtinPanel.setLayout(new BoxLayout(builtinPanel, BoxLayout.X_AXIS));
builtinPanel.add(new JLabel(I18nManager.getText("dialog.setlanguage.language") + " : "));
// Language dropdown
- _languageDropDown = new JComboBox(LANGUAGE_NAMES);
+ _languageDropDown = new JComboBox<String>(LANGUAGE_NAMES);
builtinPanel.add(_languageDropDown);
builtinPanel.add(Box.createHorizontalGlue());
JButton selectLangButton = new JButton(I18nManager.getText("button.select"));
diff --git a/tim/prune/function/SetMapBgFunction.java b/tim/prune/function/SetMapBgFunction.java
index a42b789..a875d00 100644
--- a/tim/prune/function/SetMapBgFunction.java
+++ b/tim/prune/function/SetMapBgFunction.java
@@ -37,7 +37,7 @@ import tim.prune.gui.map.MapSourceLibrary;
public class SetMapBgFunction extends GenericFunction
{
private JDialog _dialog = null;
- private JList _list = null;
+ private JList<String> _list = null;
private MapSourceListModel _listModel = null;
private String _initialSource = null;
private JButton _okButton = null, _cancelButton = null;
@@ -96,7 +96,7 @@ public class SetMapBgFunction extends GenericFunction
dialogPanel.add(introLabel, BorderLayout.NORTH);
// list box
_listModel = new MapSourceListModel();
- _list = new JList(_listModel);
+ _list = new JList<String>(_listModel);
_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
dialogPanel.add(new JScrollPane(_list), BorderLayout.CENTER);
_list.addListSelectionListener(new ListSelectionListener() {
diff --git a/tim/prune/function/ShowThreeDFunction.java b/tim/prune/function/ShowThreeDFunction.java
index 8ba0f41..40c4e5a 100644
--- a/tim/prune/function/ShowThreeDFunction.java
+++ b/tim/prune/function/ShowThreeDFunction.java
@@ -1,19 +1,45 @@
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.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
import javax.swing.JOptionPane;
+import javax.swing.JPanel;
import tim.prune.App;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.gui.BaseImageDefinitionPanel;
+import tim.prune.gui.DecimalNumberField;
+import tim.prune.gui.TerrainDefinitionPanel;
+import tim.prune.threedee.TerrainDefinition;
import tim.prune.threedee.ThreeDException;
import tim.prune.threedee.ThreeDWindow;
import tim.prune.threedee.WindowFactory;
+import tim.prune.tips.TipManager;
/**
* Class to show the 3d window
*/
public class ShowThreeDFunction extends GenericFunction
{
+ /** Dialog for input parameters */
+ private JDialog _dialog = null;
+ /** Field for altitude exaggeration value */
+ private DecimalNumberField _exaggField = null;
+ /** Component for defining the base image */
+ private BaseImageDefinitionPanel _baseImagePanel = null;
+ /** Component for defining the terrain */
+ private TerrainDefinitionPanel _terrainPanel = null;
+
/**
* Constructor
* @param inApp app object
@@ -31,7 +57,7 @@ public class ShowThreeDFunction extends GenericFunction
}
/**
- * Show the help screen
+ * Begin the function
*/
public void begin()
{
@@ -43,10 +69,107 @@ public class ShowThreeDFunction extends GenericFunction
}
else
{
+ // See if the track has any altitudes at all - if not, show a tip to use SRTM
+ if (!_app.getTrackInfo().getTrack().hasAltitudeData()) {
+ _app.showTip(TipManager.Tip_UseSrtmFor3d);
+ }
+ // Show a dialog to get the parameters
+ if (_dialog == null)
+ {
+ _dialog = new JDialog(_app.getFrame(), I18nManager.getText(getNameKey()), true);
+ _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ _dialog.getContentPane().add(makeDialogComponents());
+ _dialog.pack();
+ }
+ final int exaggFactor = Config.getConfigInt(Config.KEY_HEIGHT_EXAGGERATION);
+ if (exaggFactor > 0) {
+ _exaggField.setValue(exaggFactor / 100.0);
+ }
+ _baseImagePanel.updateBaseImageDetails();
+ _dialog.setLocationRelativeTo(_app.getFrame());
+ _dialog.setVisible(true);
+ }
+ }
+
+ /**
+ * Make the dialog components to select the options
+ * @return JPanel holding the gui elements
+ */
+ private JPanel makeDialogComponents()
+ {
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BorderLayout(4, 4));
+
+ JPanel innerPanel = new JPanel();
+ innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS));
+ // Panel for altitude exaggeration
+ JPanel exaggPanel = new JPanel();
+ exaggPanel.setLayout(new FlowLayout());
+ exaggPanel.add(new JLabel(I18nManager.getText("dialog.3d.altitudefactor") + ": "));
+ _exaggField = new DecimalNumberField(); // don't allow negative numbers
+ _exaggField.setText("5.0");
+ exaggPanel.add(_exaggField);
+ innerPanel.add(exaggPanel);
+ innerPanel.add(Box.createVerticalStrut(4));
+
+ // Panel for terrain
+ _terrainPanel = new TerrainDefinitionPanel();
+ innerPanel.add(_terrainPanel);
+ mainPanel.add(innerPanel, BorderLayout.NORTH);
+ innerPanel.add(Box.createVerticalStrut(4));
+
+ // Panel for base image (null because we don't need callback)
+ _baseImagePanel = new BaseImageDefinitionPanel(null, _dialog, _app.getTrackInfo().getTrack());
+ innerPanel.add(_baseImagePanel);
+
+ // 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)
+ {
+ _dialog.dispose();
+ new Thread(new Runnable() {
+ public void run() {
+ finish(); // needs to be in separate thread
+ }
+ }).start();
+ }
+ });
+ 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);
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+ return mainPanel;
+ }
+
+ /**
+ * All parameters have been selected in the input dialog, now we can go to the 3d window
+ */
+ private void finish()
+ {
+ // Store exaggeration factor in config
+ Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_exaggField.getValue() * 100));
+ ThreeDWindow window = WindowFactory.getWindow(_parentFrame);
+ if (window != null)
+ {
try
{
- // Pass the track object and show the window
+ // Pass the parameters to use and show the window
window.setTrack(_app.getTrackInfo().getTrack());
+ window.setAltitudeFactor(_exaggField.getValue());
+ // Also pass the base image parameters from input dialog
+ window.setBaseImageParameters(_baseImagePanel.getImageDefinition());
+ window.setTerrainParameters(new TerrainDefinition(_terrainPanel.getUseTerrain(), _terrainPanel.getGridSize()));
+ window.setDataStatus(_app.getCurrentDataStatus());
window.show();
}
catch (ThreeDException e)
diff --git a/tim/prune/function/browser/BrowserLauncher.java b/tim/prune/function/browser/BrowserLauncher.java
index d23a2f7..4ba4b8d 100644
--- a/tim/prune/function/browser/BrowserLauncher.java
+++ b/tim/prune/function/browser/BrowserLauncher.java
@@ -26,12 +26,15 @@ public abstract class BrowserLauncher
{
// which exists, so try browsers in turn
String[] browsersToTry = {"firefox", "iceweasel", "konqueror", "opera", "epiphany",
- "mozilla", "safari", "google-chrome", "lynx"};
+ "mozilla", "chromium", "midori", "safari", "lynx"};
String browserFound = null;
- for (int i=0; i<browsersToTry.length && browserFound == null; i++)
+ for (String browser : browsersToTry)
{
- if (commandExists(browsersToTry[i]))
- browserFound = browsersToTry[i];
+ if (commandExists(browser))
+ {
+ browserFound = browser;
+ break;
+ }
}
if (browserFound != null) {
_browserCommand = new String[] {browserFound, null};
diff --git a/tim/prune/function/cache/ManageCacheFunction.java b/tim/prune/function/cache/ManageCacheFunction.java
index 04e3cc1..59ef13a 100644
--- a/tim/prune/function/cache/ManageCacheFunction.java
+++ b/tim/prune/function/cache/ManageCacheFunction.java
@@ -365,8 +365,7 @@ public class ManageCacheFunction extends GenericFunction implements Runnable
if (totalDeleted > 0)
{
// Show confirmation message
- JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.diskcache.deleted1")
- + " " + totalDeleted + " " + I18nManager.getText("dialog.diskcache.deleted2"),
+ JOptionPane.showMessageDialog(_dialog, I18nManager.getTextWithNumber("dialog.diskcache.deleted", totalDeleted),
I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
// reload model
_cards.first(_cardPanel);
diff --git a/tim/prune/function/compress/CompressTrackFunction.java b/tim/prune/function/compress/CompressTrackFunction.java
index 5aa76aa..ce0203c 100644
--- a/tim/prune/function/compress/CompressTrackFunction.java
+++ b/tim/prune/function/compress/CompressTrackFunction.java
@@ -31,6 +31,8 @@ public class CompressTrackFunction extends GenericFunction
private JButton _okButton = null;
private CompressionAlgorithm[] _algorithms = null;
private SummaryLabel _summaryLabel = null;
+ /** flag to remember whether the automatic deletion has been set to always */
+ private boolean _automaticallyDelete = false;
/**
@@ -183,12 +185,28 @@ public class CompressTrackFunction extends GenericFunction
UpdateMessageBroker.informSubscribers();
_dialog.dispose();
// Show confirmation dialog with OK button (not status bar message)
- if (numMarked > 0) {
- JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.compress.confirm1")
- + " " + numMarked + " " + I18nManager.getText("dialog.compress.confirm2"),
- I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+ if (numMarked > 0)
+ {
+ // Allow calling of delete function with one click
+ final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
+ I18nManager.getText("button.always")};
+ int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
+ JOptionPane.showOptionDialog(_parentFrame,
+ I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked),
+ I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
+ JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
+ if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
+ if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
+ {
+ new Thread(new Runnable() {
+ public void run() {
+ _app.finishCompressTrack();
+ }
+ }).start();
+ }
}
- else {
+ else
+ {
JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.compress.confirmnone"),
I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
}
diff --git a/tim/prune/function/compress/DouglasPeuckerAlgorithm.java b/tim/prune/function/compress/DouglasPeuckerAlgorithm.java
index 42d7ece..046e72e 100644
--- a/tim/prune/function/compress/DouglasPeuckerAlgorithm.java
+++ b/tim/prune/function/compress/DouglasPeuckerAlgorithm.java
@@ -107,7 +107,7 @@ public class DouglasPeuckerAlgorithm extends SingleParameterAlgorithm
private void compressSegment(int[] inFlags, int inSegStart, int inSegEnd,
double inThreshold)
{
- //System.out.println("Compress segment " + inSegStart + "-" + inSegEnd);
+ // System.out.println("Compress segment " + inSegStart + "-" + inSegEnd);
final int numPoints = inSegEnd - inSegStart + 1;
if (numPoints < 3) {return;} // segment too short to compress
// Calculate parameters of straight line between first and last
@@ -118,6 +118,17 @@ public class DouglasPeuckerAlgorithm extends SingleParameterAlgorithm
// create unit vector perpendicular to AB
final double distAB = ab.len();
XYpoint perpendicular = new XYpoint(ab.y/distAB, -ab.x/distAB);
+ // Check whether distAB is 0.0 - if so, find furthest point from startxy and compress from start to here and here to end
+ if (distAB <= 0.0)
+ {
+ final int furthestIndex = getFurthestPointIndex(inSegStart, inSegEnd);
+ if (furthestIndex > inSegStart)
+ {
+ compressSegment(inFlags, inSegStart, furthestIndex, inThreshold);
+ compressSegment(inFlags, furthestIndex, inSegEnd, inThreshold);
+ }
+ return;
+ }
double maxDist = -1.0, dist = -1.0;
int furthestIndex = -1;
@@ -172,4 +183,35 @@ public class DouglasPeuckerAlgorithm extends SingleParameterAlgorithm
{
return "dialog.compress.douglaspeucker.title";
}
+
+ /**
+ * Find the index of the point furthest away from the start and end points
+ * @param inStartIndex start index of segment to check
+ * @param inEndIndex end index of segment to check
+ * @return index of furthest point, or -1 if none found
+ */
+ private int getFurthestPointIndex(int inStartIndex, int inEndIndex)
+ {
+ int furthestIndex = -1;
+ if (inStartIndex >= 0 && inEndIndex > inStartIndex)
+ {
+ final DataPoint startPoint = _track.getPoint(inStartIndex);
+ double maxDist = 0.0;
+ // Loop over points between start and end
+ for (int i=inStartIndex+1; i<inEndIndex; i++)
+ {
+ DataPoint p = _track.getPoint(i);
+ if (!p.isWaypoint())
+ {
+ double distFromStart = DataPoint.calculateRadiansBetween(startPoint, p);
+ if (distFromStart > maxDist)
+ {
+ furthestIndex = i;
+ maxDist = distFromStart;
+ }
+ }
+ }
+ }
+ return furthestIndex;
+ }
}
diff --git a/tim/prune/function/compress/MarkPointsInRectangleFunction.java b/tim/prune/function/compress/MarkPointsInRectangleFunction.java
index 73c867f..f6ffe20 100644
--- a/tim/prune/function/compress/MarkPointsInRectangleFunction.java
+++ b/tim/prune/function/compress/MarkPointsInRectangleFunction.java
@@ -17,6 +17,8 @@ public class MarkPointsInRectangleFunction extends GenericFunction
private double _minLat = 0.0, _maxLat = 0.0;
/** Minimum and maximum longitude values of rectangle */
private double _minLon = 0.0, _maxLon = 0.0;
+ /** flag to remember whether the automatic deletion has been set to always */
+ private boolean _automaticallyDelete = false;
/**
@@ -28,6 +30,11 @@ public class MarkPointsInRectangleFunction extends GenericFunction
super(inApp);
}
+ /** @return name key */
+ public String getNameKey() {
+ return "menu.track.markrectangle";
+ }
+
/**
* Set the coordinates of the rectangle
* @param inLon1 first longitude value
@@ -89,15 +96,25 @@ public class MarkPointsInRectangleFunction extends GenericFunction
// Inform subscribers to update display
UpdateMessageBroker.informSubscribers();
// Confirm message showing how many marked
- if (numMarked > 0) {
- JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.compress.confirm1")
- + " " + numMarked + " " + I18nManager.getText("dialog.compress.confirm2"),
- I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+ if (numMarked > 0)
+ {
+ // Allow calling of delete function with one click
+ final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
+ I18nManager.getText("button.always")};
+ int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
+ JOptionPane.showOptionDialog(_parentFrame,
+ I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked),
+ I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
+ JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
+ if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
+ if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
+ {
+ new Thread(new Runnable() {
+ public void run() {
+ _app.finishCompressTrack();
+ }
+ }).start();
+ }
}
}
-
- /** @return name key */
- public String getNameKey() {
- return "menu.track.markrectangle";
- }
}
diff --git a/tim/prune/function/estimate/EstimateTime.java b/tim/prune/function/estimate/EstimateTime.java
index 7a502e4..ef69793 100644
--- a/tim/prune/function/estimate/EstimateTime.java
+++ b/tim/prune/function/estimate/EstimateTime.java
@@ -29,6 +29,7 @@ import tim.prune.data.Unit;
import tim.prune.gui.DecimalNumberField;
import tim.prune.gui.DisplayUtils;
import tim.prune.gui.GuiGridLayout;
+import tim.prune.tips.TipManager;
/**
* Class to calculate and show the results of estimating (hike) time for the current range
@@ -86,7 +87,8 @@ public class EstimateTime extends GenericFunction
}
if (_dialog == null)
{
- // TODO: Check whether params are at default, show tip message if unaltered?
+ // First time in, check whether params are at default, show tip message if unaltered
+ showTip();
_dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
_dialog.setLocationRelativeTo(_parentFrame);
_dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
@@ -360,4 +362,16 @@ public class EstimateTime extends GenericFunction
}
_dialog.dispose();
}
+
+ /**
+ * Show a tip to use the learn function, if appropriate
+ */
+ private void showTip()
+ {
+ EstimationParameters currParams = new EstimationParameters(
+ Config.getConfigString(Config.KEY_ESTIMATION_PARAMS));
+ if (currParams.sameAsDefaults()) {
+ _app.showTip(TipManager.Tip_LearnTimeParams);
+ }
+ }
}
diff --git a/tim/prune/function/estimate/EstimationParameters.java b/tim/prune/function/estimate/EstimationParameters.java
index e78994b..1b80467 100644
--- a/tim/prune/function/estimate/EstimationParameters.java
+++ b/tim/prune/function/estimate/EstimationParameters.java
@@ -62,6 +62,19 @@ public class EstimationParameters
}
/**
+ * @return true if this set of parameters is the same as the default set
+ */
+ public boolean sameAsDefaults()
+ {
+ EstimationParameters defaultParams = new EstimationParameters();
+ return _flatMins == defaultParams._flatMins
+ && _gentleClimbMins == defaultParams._gentleClimbMins
+ && _steepClimbMins == defaultParams._steepClimbMins
+ && _gentleDescentMins == defaultParams._gentleDescentMins
+ && _steepDescentMins == defaultParams._steepDescentMins;
+ }
+
+ /**
* Populate the values from the config, which means all values are metric
* @param inString semicolon-separated string of five parameters
*/
diff --git a/tim/prune/function/estimate/LearnParameters.java b/tim/prune/function/estimate/LearnParameters.java
index 8f4ea68..74021dd 100644
--- a/tim/prune/function/estimate/LearnParameters.java
+++ b/tim/prune/function/estimate/LearnParameters.java
@@ -110,7 +110,7 @@ public class LearnParameters extends GenericFunction implements Runnable
int startIndex = i * sampleSize;
RangeStats stats = getRangeStats(track, startIndex, startIndex + sampleSize, prevStartIndex);
if (stats != null && stats.getMovingDistanceKilometres() > 1.0
- && !stats.getTimestampsIncomplete()
+ && !stats.getTimestampsIncomplete() && !stats.getTimestampsOutOfSequence()
&& stats.getTotalDurationInSeconds() > 100
&& stats.getStartIndex() > prevStartIndex)
{
diff --git a/tim/prune/function/sew/CandidateSorter.java b/tim/prune/function/sew/CandidateSorter.java
new file mode 100644
index 0000000..6736f32
--- /dev/null
+++ b/tim/prune/function/sew/CandidateSorter.java
@@ -0,0 +1,29 @@
+package tim.prune.function.sew;
+
+import java.util.Comparator;
+
+/**
+ * Class to sort the candidates for segment splitting
+ */
+public class CandidateSorter implements Comparator<SplitPoint>
+{
+ /**
+ * Sort the objects by distance (greatest first)
+ */
+ public int compare(SplitPoint inFirst, SplitPoint inSecond)
+ {
+ if (inFirst == null) return 1;
+ if (inSecond == null) return -1;
+ // First, sort by distance
+ final double dist1 = inFirst.getDistanceToPrevPoint();
+ final double dist2 = inSecond.getDistanceToPrevPoint();
+ if (dist1 > dist2) {
+ return -1;
+ }
+ if (dist1 < dist2) {
+ return 1;
+ }
+ // If the distances are identical, then just sort by point index
+ return inFirst.getPointIndex() - inSecond.getPointIndex();
+ }
+}
diff --git a/tim/prune/function/sew/SegmentEnd.java b/tim/prune/function/sew/SegmentEnd.java
new file mode 100644
index 0000000..9f075f1
--- /dev/null
+++ b/tim/prune/function/sew/SegmentEnd.java
@@ -0,0 +1,197 @@
+package tim.prune.function.sew;
+
+import tim.prune.data.Coordinate;
+import tim.prune.data.DataPoint;
+
+/**
+ * Class to represent one end of a segment, including the
+ * coordinates and the other end of the segment
+ */
+public class SegmentEnd implements Comparable<SegmentEnd>
+{
+ private SegmentEnd _otherEnd = null;
+ private Coordinate _longitude = null;
+ private Coordinate _latitude = null;
+ private int _pointIndex = 0;
+ private boolean _active = true;
+
+
+ /**
+ * Constructor
+ * @param inPoint data point
+ * @param inIndex point index within track
+ */
+ public SegmentEnd(DataPoint inPoint, int inIndex)
+ {
+ _longitude = inPoint.getLongitude();
+ _latitude = inPoint.getLatitude();
+ _pointIndex = inIndex;
+ _active = true;
+ }
+
+ /**
+ * @param inOther other end of the segment
+ */
+ public void setOtherEnd(SegmentEnd inOther)
+ {
+ _otherEnd = inOther;
+ }
+
+ /**
+ * @return other end
+ */
+ public SegmentEnd getOtherEnd()
+ {
+ return _otherEnd;
+ }
+
+ /**
+ * @return true if this is the start of the segment
+ */
+ public boolean isStart()
+ {
+ return _otherEnd == null || _otherEnd._pointIndex > _pointIndex;
+ }
+
+ /** @return point index */
+ public int getPointIndex() {
+ return _pointIndex;
+ }
+
+ /** @return point index of other end */
+ public int getOtherPointIndex()
+ {
+ return _otherEnd == null ? _pointIndex : _otherEnd._pointIndex;
+ }
+
+ /** @return get the earlier of the two point indices */
+ public int getEarlierIndex() {
+ return isStart() ? _pointIndex : _otherEnd._pointIndex;
+ }
+
+ /** @return get the later of the two point indices */
+ public int getLaterIndex() {
+ return isStart() ? _otherEnd._pointIndex : _pointIndex;
+ }
+
+ /**
+ * @return earlier end of this segment
+ */
+ public SegmentEnd getEarlierEnd() {
+ return isStart() ? this : _otherEnd;
+ }
+
+ /**
+ * @return later end of this segment
+ */
+ public SegmentEnd getLaterEnd() {
+ return isStart() ? _otherEnd : this;
+ }
+
+ /**
+ * Reverse this segment, by swapping the point indices of the start and end
+ * isStart() will thereby also be reversed for both ends
+ */
+ public void reverseSegment()
+ {
+ if (_otherEnd != null)
+ {
+ int pointIndex = _pointIndex;
+ _pointIndex = _otherEnd._pointIndex;
+ _otherEnd._pointIndex = pointIndex;
+ }
+ }
+
+ /**
+ * @return true if this node is still active
+ */
+ public boolean isActive() {
+ return _active;
+ }
+
+ /**
+ * Deactive this node, don't use it any more (it's already been merged)
+ */
+ public void deactivate() {
+ _active = false;
+ }
+
+ /**
+ * @param inOther other segment end
+ * @return true if the coordinates are identical
+ */
+ public boolean atSamePointAs(SegmentEnd inOther)
+ {
+ return inOther != null && _latitude.equals(inOther._latitude) && _longitude.equals(inOther._longitude);
+ }
+
+ /**
+ * Compare two objects for sorting
+ */
+ public int compareTo(SegmentEnd o)
+ {
+ if (o == null) return -1;
+ // First, sort by latitude
+ if (!_latitude.equals(o._latitude)) {
+ return (_latitude.getDouble() < o._latitude.getDouble() ? -1 : 1);
+ }
+ // Latitudes same, so sort by longitude
+ if (!_longitude.equals(o._longitude)) {
+ return (_longitude.getDouble() < o._longitude.getDouble() ? -1 : 1);
+ }
+ // Points are identical so just sort by index
+ return _pointIndex - o._pointIndex;
+ }
+
+ /**
+ * Adjust the point index as a result of a cut/move operation on the track
+ * @param inSegmentStart index of start of segment to be moved
+ * @param inSegmentEnd index of end of segment to be moved
+ * @param inMoveTo index of point before which the segment should be moved
+ */
+ public void adjustPointIndex(int inSegmentStart, int inSegmentEnd, int inMoveTo)
+ {
+ final int segmentSize = inSegmentEnd - inSegmentStart + 1; // number of points moved
+ final boolean forwardsMove = inMoveTo > inSegmentEnd;
+ // Min and max indices of affected points (apart from segment to be moved)
+ final int minIndex = forwardsMove ? inSegmentEnd + 1 : inMoveTo;
+ final int maxIndex = forwardsMove ? inMoveTo - 1 : inSegmentStart - 1;
+ if (_pointIndex >= minIndex && _pointIndex <= maxIndex)
+ {
+ // final int origIndex = _pointIndex;
+ if (forwardsMove) {
+ _pointIndex -= segmentSize; // segment moved forwards, point indices reduced
+ }
+ else {
+ _pointIndex += segmentSize; // segment moved backwards, point indices shifted forwards
+ }
+ // System.out.println(" Need to adjust index: " + origIndex + " -> " + _pointIndex);
+ }
+ else if (_pointIndex == inSegmentStart)
+ {
+ // final int origIndex = _pointIndex;
+ if (forwardsMove) {
+ _pointIndex = inMoveTo - segmentSize;
+ }
+ else
+ {
+ // Point index moves to moveTo
+ _pointIndex = inMoveTo;
+ }
+ // System.out.println(" Need to adjust movedseg: " + origIndex + " -> " + _pointIndex);
+ }
+ else if (_pointIndex == inSegmentEnd)
+ {
+ // final int origEndIndex = _otherEnd._pointIndex;
+ if (forwardsMove) {
+ _pointIndex = inMoveTo - 1;
+ }
+ else
+ {
+ // Point index moves to moveTo
+ _pointIndex = inMoveTo + inSegmentEnd - inSegmentStart;
+ }
+ // System.out.println(" Need to adjust movedseg: " + origEndIndex + " -> " + _pointIndex);
+ }
+ }
+}
diff --git a/tim/prune/function/sew/SewTrackSegmentsFunction.java b/tim/prune/function/sew/SewTrackSegmentsFunction.java
new file mode 100644
index 0000000..44ad9db
--- /dev/null
+++ b/tim/prune/function/sew/SewTrackSegmentsFunction.java
@@ -0,0 +1,331 @@
+package tim.prune.function.sew;
+
+import java.util.TreeSet;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Track;
+import tim.prune.function.Cancellable;
+import tim.prune.gui.GenericProgressDialog;
+import tim.prune.undo.UndoException;
+import tim.prune.undo.UndoSewSegments;
+
+/**
+ * Function to sew the track segments together if possible,
+ * reversing and moving as required
+ */
+public class SewTrackSegmentsFunction extends GenericFunction implements Runnable, Cancellable
+{
+ /** Set of sorted segment endpoints */
+ private TreeSet<SegmentEnd> _nodes = null;
+ /** Cancel flag */
+ private boolean _cancelled = false;
+
+
+ /** Constructor */
+ public SewTrackSegmentsFunction(App inApp) {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.sewsegments";
+ }
+
+ /**
+ * Execute the function
+ */
+ public void begin()
+ {
+ // Run in separate thread, with progress bar
+ new Thread(this).start();
+ }
+
+ /**
+ * Run the function in a separate thread
+ */
+ public void run()
+ {
+ // Make a progress bar
+ GenericProgressDialog progressDialog = new GenericProgressDialog(getNameKey(), null, _parentFrame, this);
+ progressDialog.show();
+ // Make an undo object to store the current points and sequence
+ UndoSewSegments undo = new UndoSewSegments(_app.getTrackInfo().getTrack());
+
+ // Make list of all the segment ends
+ _nodes = buildNodeList(_app.getTrackInfo().getTrack());
+ final int numNodes = (_nodes == null ? 0 : _nodes.size());
+ if (numNodes < 4)
+ {
+ System.out.println("Can't do anything with this, not enough segments");
+ progressDialog.close();
+ }
+ else
+ {
+ progressDialog.showProgress(10, 100); // Say 10% for building the nodes
+
+ // Disable messaging because we're probably doing a lot of reverses and moves
+ UpdateMessageBroker.enableMessaging(false);
+ // Set now contains all pairs of segment ends, ends at the same location are adjacent
+ // Now we're just interested in pairs of nodes, not three or more at the same location
+ SegmentEnd firstNode = null, secondNode = null;
+ int numJoins = 0, currNode = 0;
+ for (SegmentEnd node : _nodes)
+ {
+ if (!node.isActive()) {continue;}
+ if (firstNode == null)
+ {
+ firstNode = node;
+ }
+ else if (secondNode == null)
+ {
+ if (node.atSamePointAs(firstNode)) {
+ secondNode = node;
+ }
+ else {
+ firstNode = node;
+ }
+ }
+ else if (node.atSamePointAs(secondNode))
+ {
+ // Found three colocated nodes, not interested
+ firstNode = secondNode = null;
+ }
+ else
+ {
+ // Found a pair
+ joinSegments(firstNode, secondNode);
+ numJoins++;
+ firstNode = node; secondNode = null;
+ }
+ if (_cancelled) {break;}
+ final double fractionDone = 1.0 * currNode / numNodes;
+ progressDialog.showProgress(10 + (int) (fractionDone * 80), 100);
+ currNode++;
+ }
+ if (firstNode != null && secondNode != null)
+ {
+ joinSegments(firstNode, secondNode);
+ numJoins++;
+ }
+
+ progressDialog.showProgress(90, 100); // Say 90%, only duplicate point deletion left
+
+ // Delete the duplicate points
+ final int numDeleted = _cancelled ? 0 : deleteSegmentStartPoints(_app.getTrackInfo().getTrack());
+
+ progressDialog.close();
+ // Enable the messaging again
+ UpdateMessageBroker.enableMessaging(true);
+ if (_cancelled) // TODO: Also revert if any of the operations failed
+ {
+ // try to restore using undo object
+ try {
+ undo.performUndo(_app.getTrackInfo());
+ }
+ catch (UndoException ue) {
+ _app.showErrorMessage("oops", "CANNOT UNDO");
+ }
+ }
+ else if (numJoins > 0 || numDeleted > 0)
+ {
+ // Give Undo object back to App to confirm
+ final String confirmMessage = (numJoins > 0 ? I18nManager.getTextWithNumber("confirm.sewsegments", numJoins)
+ : "" + numDeleted + " " + I18nManager.getText("confirm.deletepoint.multi"));
+ _app.completeFunction(undo, confirmMessage);
+ UpdateMessageBroker.informSubscribers();
+ }
+ else
+ {
+ // Nothing done
+ _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getTextWithNumber("error.sewsegments.nothingdone", numNodes/2));
+ }
+ }
+ }
+
+ /**
+ * Build a sorted list of all the segment start points and end points
+ * Creates a TreeSet containing two SegmentEnd objects for each segment
+ * @param inTrack track object
+ * @return sorted list of segment ends
+ */
+ private static TreeSet<SegmentEnd> buildNodeList(Track inTrack)
+ {
+ TreeSet<SegmentEnd> nodes = new TreeSet<SegmentEnd>();
+ final int numPoints = inTrack.getNumPoints();
+ DataPoint prevTrackPoint = null;
+ int prevTrackPointIndex = -1;
+ SegmentEnd segmentStart = null;
+ for (int i=0; i<numPoints; i++)
+ {
+ DataPoint point = inTrack.getPoint(i);
+ if (!point.isWaypoint() && !point.hasMedia())
+ {
+ if (point.getSegmentStart())
+ {
+ // Start of new segment - does previous one need to be saved?
+ if (segmentStart != null && prevTrackPointIndex > 0 && prevTrackPointIndex != segmentStart.getPointIndex())
+ {
+ // Finish previous segment and store in list
+ SegmentEnd segmentEnd = new SegmentEnd(prevTrackPoint, prevTrackPointIndex);
+ segmentStart.setOtherEnd(segmentEnd);
+ segmentEnd.setOtherEnd(segmentStart);
+ // Don't add closed loops
+ if (!segmentStart.atSamePointAs(segmentEnd))
+ {
+ nodes.add(segmentStart);
+ nodes.add(segmentEnd);
+ }
+ }
+ // Remember segment start
+ segmentStart = new SegmentEnd(point, i);
+ }
+ prevTrackPoint = point;
+ prevTrackPointIndex = i;
+ }
+ }
+ // Probably need to deal with segmentStart and prevTrackPoint, prevTrackPointIndex
+ if (segmentStart != null && prevTrackPointIndex > 0 && prevTrackPointIndex != segmentStart.getPointIndex())
+ {
+ // Finish last segment and store in list
+ SegmentEnd segmentEnd = new SegmentEnd(prevTrackPoint, prevTrackPointIndex);
+ segmentStart.setOtherEnd(segmentEnd);
+ segmentEnd.setOtherEnd(segmentStart);
+ // Don't add closed loops
+ if (!segmentStart.atSamePointAs(segmentEnd))
+ {
+ nodes.add(segmentStart);
+ nodes.add(segmentEnd);
+ }
+ }
+ return nodes;
+ }
+
+ /**
+ * Join the two segments together represented by the given nodes
+ * @param inFirstNode first node (order doesn't matter)
+ * @param inSecondNode other node
+ */
+ private void joinSegments(SegmentEnd inFirstNode, SegmentEnd inSecondNode)
+ {
+ final Track track = _app.getTrackInfo().getTrack();
+ // System.out.println("Join: (" + inFirstNode.getPointIndex() + "-" + inFirstNode.getOtherPointIndex() + ") with ("
+ // + inSecondNode.getPointIndex() + "-" + inSecondNode.getOtherPointIndex() + ")");
+ // System.out.println(" : " + (inFirstNode.isStart() ? "start" : "end") + " to " + (inSecondNode.isStart() ? "start" : "end"));
+ final boolean moveSecondBeforeFirst = inFirstNode.isStart();
+ if (inFirstNode.isStart() == inSecondNode.isStart())
+ {
+ if (track.reverseRange(inSecondNode.getEarlierIndex(), inSecondNode.getLaterIndex()))
+ {
+ inSecondNode.reverseSegment();
+ // System.out.println(" : Reverse segment: " + inSecondNode.getEarlierIndex() + " - " + inSecondNode.getLaterIndex());
+ }
+ else {
+ System.err.println("Oops, reverse range didn't work");
+ // TODO: Abort?
+ }
+ }
+ if (moveSecondBeforeFirst)
+ {
+ if ((inSecondNode.getLaterIndex()+1) != inFirstNode.getPointIndex())
+ {
+ // System.out.println(" : Move second segment before first");
+ cutAndMoveSegment(inSecondNode.getEarlierIndex(), inSecondNode.getLaterIndex(), inFirstNode.getPointIndex());
+ }
+ }
+ else if ((inFirstNode.getLaterIndex()+1) != inSecondNode.getPointIndex())
+ {
+ // System.out.println(" : Move first segment before second (because " + (inFirstNode.getLaterIndex()+1) + " isn't " + inSecondNode.getPointIndex() + ")");
+ cutAndMoveSegment(inFirstNode.getEarlierIndex(), inFirstNode.getLaterIndex(), inSecondNode.getPointIndex());
+ }
+ // Now merge the SegmentEnds so that they're not split up again
+ if (inSecondNode.getEarlierIndex() == (inFirstNode.getLaterIndex()+1)) {
+ // System.out.println("second node is now directly after the first node");
+ }
+ else if (inFirstNode.getEarlierIndex() == (inSecondNode.getLaterIndex()+1)) {
+ //System.out.println("first node is now directly after the second node");
+ }
+ else {
+ System.err.println("Why aren't the segments directly consecutive after the join?");
+ }
+ // Find the earliest and latest ends of these two segments
+ SegmentEnd earlierSegmentEnd = (inFirstNode.getEarlierIndex() < inSecondNode.getEarlierIndex() ? inFirstNode : inSecondNode).getEarlierEnd();
+ SegmentEnd laterSegmentEnd = (inFirstNode.getLaterIndex() > inSecondNode.getLaterIndex() ? inFirstNode : inSecondNode).getLaterEnd();
+ // Get rid of the inner two segment ends, join the earliest and latest together
+ earlierSegmentEnd.getOtherEnd().deactivate();
+ laterSegmentEnd.getOtherEnd().deactivate();
+ earlierSegmentEnd.setOtherEnd(laterSegmentEnd);
+ laterSegmentEnd.setOtherEnd(earlierSegmentEnd);
+ }
+
+ /**
+ * Cut and move the segment to a different position
+ * @param inSegmentStart start index of segment
+ * @param inSegmentEnd end index of segment
+ * @param inMoveToPos index before which the segment should be moved
+ */
+ private void cutAndMoveSegment(int inSegmentStart, int inSegmentEnd, int inMoveToPos)
+ {
+ if (!_app.getTrackInfo().getTrack().cutAndMoveSection(inSegmentStart, inSegmentEnd, inMoveToPos))
+ {
+ System.err.println(" Oops, cut and move didn't work");
+ // TODO: Throw exception? Return false?
+ }
+ else
+ {
+ // Loop over each node to inform it of the index changes
+ for (SegmentEnd node : _nodes) {
+ node.adjustPointIndex(inSegmentStart, inSegmentEnd, inMoveToPos);
+ }
+ }
+ }
+
+ /**
+ * The final step of the sewing, removing the duplicate points at the start of each segment
+ * @param inTrack track object
+ * @return number of points deleted
+ */
+ private static int deleteSegmentStartPoints(Track inTrack)
+ {
+ final int numPoints = inTrack.getNumPoints();
+ boolean[] deleteFlags = new boolean[numPoints];
+ // Loop over points in track, setting delete flags
+ int numToDelete = 0;
+ DataPoint prevPoint = null;
+ for (int i=0; i<numPoints; i++)
+ {
+ DataPoint point = inTrack.getPoint(i);
+ if (!point.isWaypoint())
+ {
+ if (prevPoint != null && point.getSegmentStart() && point.isDuplicate(prevPoint))
+ {
+ deleteFlags[i] = true;
+ numToDelete++;
+ }
+ prevPoint = point;
+ }
+ }
+ // Make new datapoint array of the right size
+ DataPoint[] pointCopies = new DataPoint[numPoints - numToDelete];
+ // Loop over points again, keeping the ones we want
+ int copyIndex = 0;
+ for (int i=0; i<numPoints; i++)
+ {
+ if (!deleteFlags[i]) {
+ pointCopies[copyIndex] = inTrack.getPoint(i);
+ copyIndex++;
+ }
+ }
+ // Finally, replace the copied points in the track
+ inTrack.replaceContents(pointCopies);
+ return numToDelete;
+ }
+
+ /** Function cancelled by progress dialog */
+ public void cancel() {
+ _cancelled = true;
+ }
+}
diff --git a/tim/prune/function/sew/SplitPoint.java b/tim/prune/function/sew/SplitPoint.java
new file mode 100644
index 0000000..c482cda
--- /dev/null
+++ b/tim/prune/function/sew/SplitPoint.java
@@ -0,0 +1,101 @@
+package tim.prune.function.sew;
+
+import tim.prune.data.Coordinate;
+import tim.prune.data.DataPoint;
+
+/**
+ * Class to represent a possible split point, including
+ * the distances to the previous and next points
+ */
+public class SplitPoint implements Comparable<SplitPoint>
+{
+ private SplitPoint _nextPoint = null;
+ private Coordinate _longitude = null;
+ private Coordinate _latitude = null;
+ private int _pointIndex = 0;
+ private double _distToPrevPoint = 0.0;
+ private double _distToNextPoint = -1.0;
+
+
+ /**
+ * Constructor
+ * @param inPoint data point
+ * @param inIndex point index within track
+ */
+ public SplitPoint(DataPoint inPoint, int inIndex)
+ {
+ _longitude = inPoint.getLongitude();
+ _latitude = inPoint.getLatitude();
+ _pointIndex = inIndex;
+ }
+
+ /**
+ * @param inDist distance to previous track point
+ */
+ public void setDistanceToPrevPoint(double inDist) {
+ _distToPrevPoint = inDist;
+ }
+ /** @return distance to previous track point */
+ public double getDistanceToPrevPoint() {
+ return _distToPrevPoint;
+ }
+
+ /**
+ * @param inDist distance to next track point, or -1.0
+ */
+ public void setDistanceToNextPoint(double inDist) {
+ _distToNextPoint = inDist;
+ }
+ /** @return distance to next track point */
+ public double getDistanceToNextPoint() {
+ return _distToNextPoint;
+ }
+ /** @return true if this is closer to the next point than to the previous one */
+ public boolean closerToNext() {
+ return _distToNextPoint > 0.0 && _distToNextPoint < _distToPrevPoint;
+ }
+
+ /** @return point index */
+ public int getPointIndex() {
+ return _pointIndex;
+ }
+
+ /**
+ * @param inOther the next point
+ */
+ public void setNextPoint(SplitPoint inOther) {
+ _nextPoint = inOther;
+ }
+
+ /** @return the next point, or null */
+ public SplitPoint getNextPoint() {
+ return _nextPoint;
+ }
+
+ /**
+ * @param inOther other segment end
+ * @return true if the coordinates are identical
+ */
+ public boolean atSamePointAs(SplitPoint inOther)
+ {
+ return inOther != null && _latitude.equals(inOther._latitude) && _longitude.equals(inOther._longitude);
+ }
+
+ /**
+ * Compare two objects for sorting
+ */
+ public int compareTo(SplitPoint o)
+ {
+ if (o == null) return -1;
+ // First, sort by latitude
+ if (!_latitude.equals(o._latitude)) {
+ return (_latitude.getDouble() < o._latitude.getDouble() ? -1 : 1);
+ }
+ // Latitudes same, so sort by longitude
+ if (!_longitude.equals(o._longitude)) {
+ return (_longitude.getDouble() < o._longitude.getDouble() ? -1 : 1);
+ }
+ // Points are identical so just sort by index
+ return _pointIndex - o._pointIndex;
+ }
+}
diff --git a/tim/prune/function/sew/SplitSegmentsFunction.java b/tim/prune/function/sew/SplitSegmentsFunction.java
new file mode 100644
index 0000000..91a7d52
--- /dev/null
+++ b/tim/prune/function/sew/SplitSegmentsFunction.java
@@ -0,0 +1,285 @@
+package tim.prune.function.sew;
+
+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.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Distance;
+import tim.prune.data.Field;
+import tim.prune.data.Unit;
+import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.WholeNumberField;
+import tim.prune.undo.UndoSplitSegments;
+
+/**
+ * Function to split a track into segments using
+ * either a distance limit or a time limit
+ */
+public class SplitSegmentsFunction extends GenericFunction
+{
+ /** Dialog */
+ private JDialog _dialog = null;
+ /** Radio buttons for splitting by distance and time */
+ private JRadioButton _distLimitRadio = null, _timeLimitRadio = null;
+ /** Dropdown for selecting distance units */
+ private JComboBox<String> _distUnitsDropdown = null;
+ /** Text field for entering distance */
+ private WholeNumberField _distanceField = null;
+ /** Text fields for entering distance */
+ private WholeNumberField _limitHourField = null, _limitMinField = null;
+ /** Ok button */
+ private JButton _okButton = null;
+
+
+ /**
+ * React to item changes and key presses
+ */
+ private abstract class ChangeListener extends KeyAdapter implements ItemListener
+ {
+ /** Method to be implemented */
+ public abstract void optionsChanged();
+
+ /** Item changed in ItemListener */
+ public void itemStateChanged(ItemEvent arg0) {
+ optionsChanged();
+ }
+
+ /** Key released in KeyListener */
+ public void keyReleased(KeyEvent arg0) {
+ optionsChanged();
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public SplitSegmentsFunction(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return name key
+ */
+ public String getNameKey() {
+ return "function.splitsegments";
+ }
+
+ /**
+ * Begin the function
+ */
+ public void begin()
+ {
+ 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();
+ }
+ enableOkButton();
+ // TODO: Maybe set distance units according to current Config setting?
+ final boolean hasTimestamps = _app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP);
+ _timeLimitRadio.setEnabled(hasTimestamps);
+ _dialog.setVisible(true);
+ }
+
+ /**
+ * Create dialog components
+ * @return Panel containing all gui elements in dialog
+ */
+ private Component makeDialogComponents()
+ {
+ JPanel dialogPanel = new JPanel();
+ dialogPanel.setLayout(new BorderLayout(5, 5));
+
+ // Make radio buttons for three different options
+ _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit") + ": ");
+ _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + ": ");
+ ButtonGroup radioGroup = new ButtonGroup();
+ radioGroup.add(_distLimitRadio);
+ radioGroup.add(_timeLimitRadio);
+
+ // central panel for limits
+ JPanel limitsPanel = new JPanel();
+ limitsPanel.setLayout(new BoxLayout(limitsPanel, BoxLayout.Y_AXIS));
+ limitsPanel.add(Box.createVerticalStrut(8));
+ ChangeListener optionsChangedListener = new ChangeListener() {
+ public void optionsChanged() {
+ enableOkButton();
+ }
+ };
+ // distance limits
+ JPanel distLimitPanel = new JPanel();
+ distLimitPanel.setLayout(new FlowLayout());
+ _distLimitRadio.setSelected(true);
+ _distLimitRadio.addItemListener(optionsChangedListener);
+ distLimitPanel.add(_distLimitRadio);
+ _distanceField = new WholeNumberField(3);
+ _distanceField.addKeyListener(optionsChangedListener);
+ distLimitPanel.add(_distanceField);
+ String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"),
+ I18nManager.getText("units.miles")};
+ _distUnitsDropdown = new JComboBox<String>(distUnitsOptions);
+ _distUnitsDropdown.addItemListener(optionsChangedListener);
+ distLimitPanel.add(_distUnitsDropdown);
+ distLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ limitsPanel.add(distLimitPanel);
+
+ // time limit panel
+ JPanel timeLimitPanel = new JPanel();
+ timeLimitPanel.setLayout(new FlowLayout());
+ _timeLimitRadio.addItemListener(optionsChangedListener);
+ timeLimitPanel.add(_timeLimitRadio);
+ _limitHourField = new WholeNumberField(2);
+ _limitHourField.addKeyListener(optionsChangedListener);
+ timeLimitPanel.add(_limitHourField);
+ timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.hours")));
+ _limitMinField = new WholeNumberField(3);
+ _limitMinField.addKeyListener(optionsChangedListener);
+ timeLimitPanel.add(_limitMinField);
+ timeLimitPanel.add(new JLabel(I18nManager.getText("units.minutes")));
+ timeLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ limitsPanel.add(timeLimitPanel);
+
+ dialogPanel.add(limitsPanel, BorderLayout.NORTH);
+
+ // button panel at bottom
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ // OK button
+ _okButton = new JButton(I18nManager.getText("button.ok"));
+ _okButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ performSplit();
+ }
+ });
+ buttonPanel.add(_okButton);
+ // Cancel button
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _dialog.dispose();
+ }
+ });
+ cancelButton.addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent inE) {
+ if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
+ }
+ });
+ buttonPanel.add(cancelButton);
+ dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+ return dialogPanel;
+ }
+
+ /**
+ * Enable or disable the OK button according to the inputs
+ */
+ private void enableOkButton()
+ {
+ boolean enabled = false;
+ if (_distLimitRadio.isSelected()) {
+ enabled = _distanceField.getValue() > 0;
+ }
+ else if (_timeLimitRadio.isSelected()) {
+ enabled = _limitHourField.getValue() > 0 || _limitMinField.getValue() > 0;
+ }
+ _okButton.setEnabled(enabled);
+
+ // Also enable/disable the other fields
+ _distanceField.setEnabled(_distLimitRadio.isSelected());
+ _distUnitsDropdown.setEnabled(_distLimitRadio.isSelected());
+ _limitHourField.setEnabled(_timeLimitRadio.isSelected());
+ _limitMinField.setEnabled(_timeLimitRadio.isSelected());
+ }
+
+ /**
+ * The dialog has been completed and OK pressed, so do the split
+ */
+ private void performSplit()
+ {
+ // Split either by distance or time
+ boolean checkTimeLimit = _timeLimitRadio.isSelected()
+ && (_limitHourField.getValue() > 0 || _limitMinField.getValue() > 0);
+ int timeLimitSeconds = 0;
+ if (checkTimeLimit)
+ {
+ timeLimitSeconds = _limitHourField.getValue() * 60 * 60
+ + _limitMinField.getValue() * 60;
+ if (timeLimitSeconds <= 0) {checkTimeLimit = false;}
+ }
+ double distLimitRadians = 0.0;
+ final boolean checkDistLimit = _distLimitRadio.isSelected()
+ && _distanceField.getValue() > 0;
+ if (checkDistLimit)
+ {
+ final Unit[] distUnits = {UnitSetLibrary.UNITS_KILOMETRES,
+ UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_MILES};
+ Unit distUnit = distUnits[_distUnitsDropdown.getSelectedIndex()];
+ distLimitRadians = Distance.convertDistanceToRadians(_distanceField.getValue(), distUnit);
+ }
+ if (!checkTimeLimit && !checkDistLimit) {
+ return; // neither option selected
+ }
+
+ // Make undo object
+ UndoSplitSegments undo = new UndoSplitSegments(_app.getTrackInfo().getTrack());
+ final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
+ DataPoint currPoint = null, prevPoint = null;
+ int numSplitsMade = 0;
+
+ // Now actually do it, looping through the points in the track
+ for (int i=0; i<numPoints; i++)
+ {
+ currPoint = _app.getTrackInfo().getTrack().getPoint(i);
+ if (!currPoint.isWaypoint())
+ {
+ boolean splitHere = (prevPoint != null)
+ && ((checkDistLimit && DataPoint.calculateRadiansBetween(prevPoint, currPoint) > distLimitRadians)
+ || (checkTimeLimit && currPoint.hasTimestamp() && prevPoint.hasTimestamp()
+ && currPoint.getTimestamp().getSecondsSince(prevPoint.getTimestamp()) > timeLimitSeconds));
+ if (splitHere && !currPoint.getSegmentStart())
+ {
+ currPoint.setSegmentStart(true);
+ numSplitsMade++;
+ }
+ prevPoint = currPoint;
+ }
+ }
+
+ if (numSplitsMade > 0)
+ {
+ _app.completeFunction(undo, I18nManager.getTextWithNumber("confirm.splitsegments", numSplitsMade));
+ UpdateMessageBroker.informSubscribers();
+ _dialog.dispose();
+ }
+ else
+ {
+ // Complain that no split was made
+ JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.tracksplit.nosplit"),
+ I18nManager.getText("error.function.noop.title"), JOptionPane.WARNING_MESSAGE);
+ }
+ }
+}
diff --git a/tim/prune/function/srtm/DownloadSrtmFunction.java b/tim/prune/function/srtm/DownloadSrtmFunction.java
new file mode 100644
index 0000000..d0d92bb
--- /dev/null
+++ b/tim/prune/function/srtm/DownloadSrtmFunction.java
@@ -0,0 +1,222 @@
+package tim.prune.function.srtm;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+
+import javax.swing.JOptionPane;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.GpsPrune;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.data.DoubleRange;
+import tim.prune.gui.ProgressDialog;
+
+/**
+ * Class to provide a download function for the Space Shuttle's SRTM data files.
+ * HGT files are downloaded into memory via HTTP and stored in the map cache.
+ */
+public class DownloadSrtmFunction extends GenericFunction implements Runnable
+{
+ /** Progress dialog */
+ private ProgressDialog _progress = null;
+ /** Flag to check whether this function is currently running or not */
+ private boolean _running = false;
+
+
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public DownloadSrtmFunction(App inApp) {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.downloadsrtm";
+ }
+
+ /**
+ * Begin the download
+ */
+ public void begin()
+ {
+ _running = true;
+ if (_progress == null) {
+ _progress = new ProgressDialog(_parentFrame, getNameKey());
+ }
+ _progress.show();
+ // start new thread for time-consuming part
+ new Thread(this).start();
+ }
+
+ /**
+ * Run method using separate thread
+ */
+ public void run()
+ {
+ // Compile list of tiles to get
+ ArrayList<SrtmTile> tileList = new ArrayList<SrtmTile>();
+
+ // First, loop to see which tiles are needed
+ DoubleRange lonRange = _app.getTrackInfo().getTrack().getLonRange();
+ DoubleRange latRange = _app.getTrackInfo().getTrack().getLatRange();
+ final int minLon = (int) Math.floor(lonRange.getMinimum());
+ final int maxLon = (int) Math.floor(lonRange.getMaximum());
+ final int minLat = (int) Math.floor(latRange.getMinimum());
+ final int maxLat = (int) Math.floor(latRange.getMaximum());
+
+ for (int lon=minLon; lon<= maxLon; lon++)
+ {
+ for (int lat=minLat; lat <= maxLat; lat++)
+ {
+ SrtmTile tile = new SrtmTile(lat, lon);
+ boolean alreadyGot = false;
+ for (int t = 0; t < tileList.size(); t++)
+ {
+ if (tileList.get(t).equals(tile)) {
+ alreadyGot = true;
+ }
+ }
+ if (!alreadyGot) {tileList.add(tile);}
+ }
+ }
+
+ downloadTiles(tileList);
+ // Finished
+ _running = false;
+ }
+
+
+ /**
+ * Download the tiles of SRTM data
+ * @param inTileList list of tiles to get
+ */
+ private void downloadTiles(ArrayList<SrtmTile> inTileList)
+ {
+ // Update progress bar
+ if (_progress != null)
+ {
+ _progress.setMaximum(inTileList.size());
+ _progress.setValue(0);
+ }
+
+ String errorMessage = null;
+
+ // Check the cache is ok
+ final String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
+ if (diskCachePath != null)
+ {
+ File srtmDir = new File(diskCachePath, "srtm");
+ if (!srtmDir.exists() && !srtmDir.mkdir()) {
+ // can't create the srtm directory
+ errorMessage = I18nManager.getText("error.downloadsrtm.nocache");
+ }
+ }
+ else {
+ // no cache set up
+ errorMessage = I18nManager.getText("error.downloadsrtm.nocache");
+ }
+
+ // Get urls for each tile
+ URL[] urls = TileFinder.getUrls(inTileList);
+ int numDownloaded = 0;
+ for (int t=0; t<inTileList.size() && !_progress.isCancelled(); t++)
+ {
+ if (urls[t] != null)
+ {
+ // Define streams
+ FileOutputStream outStream = null;
+ InputStream inStream = null;
+ try
+ {
+ // Set progress
+ _progress.setValue(t);
+ // See if we've already got this tile or not
+ File outputFile = getFileToWrite(urls[t]);
+ if (outputFile != null)
+ {
+ // System.out.println("Download: Need to download: " + urls[t]);
+ outStream = new FileOutputStream(outputFile);
+ URLConnection conn = urls[t].openConnection();
+ conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+ inStream = conn.getInputStream();
+ // Copy all the bytes to the file
+ int c;
+ while ((c = inStream.read()) != -1)
+ {
+ outStream.write(c);
+ }
+
+ numDownloaded++;
+ }
+ // else System.out.println("Don't need to download: " + urls[t].getFile());
+ }
+ catch (IOException ioe) {errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
+ }
+ // Make sure streams are closed
+ try {inStream.close();} catch (Exception e) {}
+ try {outStream.close();} catch (Exception e) {}
+ }
+ }
+
+ _progress.dispose();
+ if (_progress.isCancelled()) {
+ return;
+ }
+
+ if (errorMessage != null) {
+ _app.showErrorMessageNoLookup(getNameKey(), errorMessage);
+ }
+ else if (numDownloaded == 1)
+ {
+ JOptionPane.showMessageDialog(_parentFrame, I18nManager.getTextWithNumber("confirm.downloadsrtm.1", numDownloaded),
+ I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+ }
+ else if (numDownloaded > 1)
+ {
+ JOptionPane.showMessageDialog(_parentFrame, I18nManager.getTextWithNumber("confirm.downloadsrtm", numDownloaded),
+ I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+ }
+ else if (inTileList.size() > 0) {
+ _app.showErrorMessage(getNameKey(), "confirm.downloadsrtm.none");
+ }
+ }
+
+ /**
+ * See whether the SRTM file is already available locally
+ * @param inUrl URL for online resource
+ * @return file object to write to, or null if already there
+ */
+ private static File getFileToWrite(URL inUrl)
+ {
+ String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
+ if (diskCachePath != null)
+ {
+ File srtmDir = new File(diskCachePath, "srtm");
+ if (srtmDir.exists() && srtmDir.isDirectory() && srtmDir.canRead())
+ {
+ File srtmFile = new File(srtmDir, new File(inUrl.getFile()).getName());
+ if (!srtmFile.exists() || !srtmFile.canRead() || srtmFile.length() <= 1) {
+ return srtmFile;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return true if a thread is currently running
+ */
+ public boolean isRunning()
+ {
+ return _running;
+ }
+}
diff --git a/tim/prune/function/srtm/LookupSrtmFunction.java b/tim/prune/function/srtm/LookupSrtmFunction.java
index 4d13348..2ed1454 100644
--- a/tim/prune/function/srtm/LookupSrtmFunction.java
+++ b/tim/prune/function/srtm/LookupSrtmFunction.java
@@ -1,5 +1,7 @@
package tim.prune.function.srtm;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
@@ -13,35 +15,44 @@ import tim.prune.DataSubscriber;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
+import tim.prune.data.Altitude;
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
import tim.prune.data.Track;
+import tim.prune.data.UnitSetLibrary;
import tim.prune.gui.ProgressDialog;
+import tim.prune.tips.TipManager;
import tim.prune.undo.UndoLookupSrtm;
/**
- * Class to provide a lookup function for point altitudes
- * using the Space Shuttle's SRTM data files.
- * HGT files are downloaded into memory via HTTP and point altitudes
- * can then be interpolated from the 3m grid data.
+ * Class to provide a lookup function for point altitudes using the Space
+ * Shuttle's SRTM data files. HGT files are downloaded into memory via HTTP and
+ * point altitudes can then be interpolated from the 3m grid data.
*/
public class LookupSrtmFunction extends GenericFunction implements Runnable
{
/** Progress dialog */
- ProgressDialog _progress = null;
+ private ProgressDialog _progress = null;
+ /** Track to process */
+ private Track _track = null;
+ /** Flag for whether this is a real track or a terrain one */
+ private boolean _normalTrack = true;
+ /** Flag set when any tiles had to be downloaded (rather than just loaded locally) */
+ private boolean _hadToDownload = false;
+ /** Flag to check whether this function is currently running or not */
+ private boolean _running = false;
/** Expected size of hgt file in bytes */
private static final long HGT_SIZE = 2884802L;
/** Altitude below which is considered void */
private static final int VOID_VAL = -32768;
-
/**
* Constructor
- * @param inApp App object
+ * @param inApp App object
*/
- public LookupSrtmFunction(App inApp)
- {
+ public LookupSrtmFunction(App inApp) {
super(inApp);
}
@@ -51,36 +62,54 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
}
/**
- * Begin the lookup
+ * Begin the lookup using the normal track
+ */
+ public void begin() {
+ begin(_app.getTrackInfo().getTrack(), true);
+ }
+
+ /**
+ * Begin the lookup with an alternative track
+ * @param inAlternativeTrack
*/
- public void begin()
+ public void begin(Track inAlternativeTrack) {
+ begin(inAlternativeTrack, false);
+ }
+
+ /**
+ * Begin the function with the given parameters
+ * @param inTrack track to process
+ * @param inNormalTrack true if this is a "normal" track, false for an artificially constructed one such as for terrain
+ */
+ private void begin(Track inTrack, boolean inNormalTrack)
{
- if (_progress == null)
- {
+ _running = true;
+ _hadToDownload = false;
+ if (_progress == null) {
_progress = new ProgressDialog(_parentFrame, getNameKey());
}
_progress.show();
+ _track = inTrack;
+ _normalTrack = inNormalTrack;
// start new thread for time-consuming part
new Thread(this).start();
}
-
/**
* Run method using separate thread
*/
public void run()
{
// 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++)
+ for (int i = 0; i < _track.getNumPoints(); i++)
{
- if (track.getPoint(i).hasAltitude())
+ if (_track.getPoint(i).hasAltitude())
{
- if (track.getPoint(i).getAltitude().getValue() == 0) {
+ if (_track.getPoint(i).getAltitude().getValue() == 0) {
hasZeroAltitudePoints = true;
}
else {
@@ -99,14 +128,16 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
}
// Now loop again to extract the required tiles
- for (int i=0; i<track.getNumPoints(); i++)
+ for (int i = 0; i < _track.getNumPoints(); i++)
{
// Consider points which don't have altitudes or have zero values
- if (!track.getPoint(i).hasAltitude() || (overwriteZeros && track.getPoint(i).getAltitude().getValue() == 0))
+ if (!_track.getPoint(i).hasAltitude()
+ || (overwriteZeros && _track.getPoint(i).getAltitude().getValue() == 0))
{
- SrtmTile tile = new SrtmTile(track.getPoint(i));
+ SrtmTile tile = new SrtmTile(_track.getPoint(i));
boolean alreadyGot = false;
- for (int t=0; t<tileList.size(); t++) {
+ for (int t = 0; t < tileList.size(); t++)
+ {
if (tileList.get(t).equals(tile)) {
alreadyGot = true;
}
@@ -115,8 +146,15 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
}
}
lookupValues(tileList, overwriteZeros);
+ // Finished
+ _running = false;
+ // Show tip if lots of online lookups were necessary
+ if (_hadToDownload) {
+ _app.showTip(TipManager.Tip_DownloadSrtm);
+ }
}
+
/**
* Lookup the values from SRTM data
* @param inTileList list of tiles to get
@@ -124,12 +162,14 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
*/
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
- _progress.setMaximum(inTileList.size());
- _progress.setValue(0);
+ if (_progress != null)
+ {
+ _progress.setMaximum(inTileList.size());
+ _progress.setValue(0);
+ }
String errorMessage = null;
// Get urls for each tile
URL[] urls = TileFinder.getUrls(inTileList);
@@ -140,40 +180,49 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
SrtmTile tile = inTileList.get(t);
try
{
+ // Set progress
_progress.setValue(t);
- final int ARRLENGTH = 1201*1201;
+ final int ARRLENGTH = 1201 * 1201;
int[] heights = new int[ARRLENGTH];
// Open zipinputstream on url and check size
- ZipInputStream inStream = new ZipInputStream(urls[t].openStream());
- ZipEntry entry = inStream.getNextEntry();
- boolean entryOk = (entry.getSize() == HGT_SIZE);
- if (entryOk)
+ ZipInputStream inStream = getStreamToHgtFile(urls[t]);
+ boolean entryOk = false;
+ if (inStream != null)
{
- // Read entire file contents into one byte array
- for (int i=0; i<ARRLENGTH; i++) {
- heights[i] = inStream.read()*256 + inStream.read();
- if (heights[i] >= 32768) {heights[i] -= 65536;}
+ ZipEntry entry = inStream.getNextEntry();
+ entryOk = (entry != null && entry.getSize() == HGT_SIZE);
+ if (entryOk)
+ {
+ // Read entire file contents into one byte array
+ for (int i = 0; i < ARRLENGTH; i++)
+ {
+ heights[i] = inStream.read() * 256 + inStream.read();
+ if (heights[i] >= 32768) {heights[i] -= 65536;}
+ }
}
+ // else {
+ // System.out.println("length not ok: " + entry.getSize());
+ // }
+ // Close stream from url
+ inStream.close();
}
- //else {
- // System.out.println("length not ok: " + entry.getSize());
- //}
- // Close stream from url
- inStream.close();
if (entryOk)
{
// Loop over all points in track, try to apply altitude from array
- for (int p=0; p<track.getNumPoints(); p++)
+ for (int p = 0; p < _track.getNumPoints(); p++)
{
- DataPoint point = track.getPoint(p);
- if (!point.hasAltitude() || (inOverwriteZeros && point.getAltitude().getValue() == 0)) {
+ DataPoint point = _track.getPoint(p);
+ if (!point.hasAltitude()
+ || (inOverwriteZeros && point.getAltitude().getValue() == 0))
+ {
if (new SrtmTile(point).equals(tile))
{
double x = (point.getLongitude().getDouble() - tile.getLongitude()) * 1200;
double y = 1201 - (point.getLatitude().getDouble() - tile.getLatitude()) * 1200;
int idx1 = ((int)y)*1201 + (int)x;
- try {
+ try
+ {
int[] fouralts = {heights[idx1], heights[idx1+1], heights[idx1-1201], heights[idx1-1200]};
int numVoids = (fouralts[0]==VOID_VAL?1:0) + (fouralts[1]==VOID_VAL?1:0)
+ (fouralts[2]==VOID_VAL?1:0) + (fouralts[3]==VOID_VAL?1:0);
@@ -186,33 +235,43 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
case 3: altitude = averageNonVoid(fouralts); break;
default: altitude = VOID_VAL;
}
- if (altitude != VOID_VAL) {
+ if (altitude != VOID_VAL)
+ {
point.setFieldValue(Field.ALTITUDE, ""+altitude, false);
+ // depending on settings, this value may have been added as feet, we need to force metres
+ point.getAltitude().reset(new Altitude((int)altitude, UnitSetLibrary.UNITS_METRES));
numAltitudesFound++;
}
}
catch (ArrayIndexOutOfBoundsException obe) {
- //System.err.println("lat=" + point.getLatitude().getDouble() + ", x=" + x + ", y=" + y + ", idx=" + idx1);
+ // System.err.println("lat=" + point.getLatitude().getDouble() + ", x=" + x + ", y=" + y + ", idx=" + idx1);
}
}
}
}
}
}
- catch (IOException ioe) {
- errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
+ catch (IOException ioe) {errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
}
}
}
+
_progress.dispose();
- if (_progress.isCancelled()) {return;}
+ if (_progress.isCancelled()) {
+ return;
+ }
+
if (numAltitudesFound > 0)
{
// Inform app including undo information
- track.requestRescale();
+ _track.requestRescale();
UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_ADDED_OR_REMOVED);
- _app.completeFunction(undo, I18nManager.getText("confirm.lookupsrtm1") + " " + numAltitudesFound
- + " " + I18nManager.getText("confirm.lookupsrtm2"));
+ // Don't update app if we're doing another track
+ if (_normalTrack)
+ {
+ _app.completeFunction(undo,
+ I18nManager.getTextWithNumber("confirm.lookupsrtm", numAltitudesFound));
+ }
}
else if (errorMessage != null) {
_app.showErrorMessageNoLookup(getNameKey(), errorMessage);
@@ -226,6 +285,35 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
}
/**
+ * See whether the SRTM file is already available locally first, then try online
+ * @param inUrl URL for online resource
+ * @return ZipInputStream either on the local file or on the downloaded zip file
+ */
+ private ZipInputStream getStreamToHgtFile(URL inUrl)
+ throws IOException
+ {
+ String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
+ if (diskCachePath != null)
+ {
+ File srtmDir = new File(diskCachePath, "srtm");
+ if (srtmDir.exists() && srtmDir.isDirectory() && srtmDir.canRead())
+ {
+ File srtmFile = new File(srtmDir, new File(inUrl.getFile()).getName());
+ if (srtmFile.exists() && srtmFile.isFile() && srtmFile.canRead())
+ {
+ // System.out.println("Lookup: Using file " + srtmFile.getAbsolutePath());
+ // File found, use this one
+ return new ZipInputStream(new FileInputStream(srtmFile));
+ }
+ }
+ }
+ // System.out.println("Lookup: Trying online: " + inUrl.toString());
+ _hadToDownload = true;
+ // MAYBE: Only download if we're in online mode?
+ return new ZipInputStream(inUrl.openStream());
+ }
+
+ /**
* Perform a bilinear interpolation on the given altitude array
* @param inAltitudes array of four altitude values on corners of square (bl, br, tl, tr)
* @param inX x coordinate
@@ -249,7 +337,8 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
private static int[] fixVoid(int[] inAltitudes)
{
int[] fixed = new int[inAltitudes.length];
- for (int i=0; i<inAltitudes.length; i++) {
+ for (int i = 0; i < inAltitudes.length; i++)
+ {
if (inAltitudes[i] == VOID_VAL) {
fixed[i] = (int) Math.round(averageNonVoid(inAltitudes));
}
@@ -269,8 +358,10 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
{
double totalAltitude = 0.0;
int numAlts = 0;
- for (int i=0; i<inAltitudes.length; i++) {
- if (inAltitudes[i] != VOID_VAL) {
+ for (int i = 0; i < inAltitudes.length; i++)
+ {
+ if (inAltitudes[i] != VOID_VAL)
+ {
totalAltitude += inAltitudes[i];
numAlts++;
}
@@ -278,4 +369,12 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
if (numAlts < 1) {return VOID_VAL;}
return totalAltitude / numAlts;
}
+
+ /**
+ * @return true if a thread is currently running
+ */
+ public boolean isRunning()
+ {
+ return _running;
+ }
}
diff --git a/tim/prune/function/srtm/SrtmTile.java b/tim/prune/function/srtm/SrtmTile.java
index d7ab38f..301bbaf 100644
--- a/tim/prune/function/srtm/SrtmTile.java
+++ b/tim/prune/function/srtm/SrtmTile.java
@@ -10,7 +10,7 @@ public class SrtmTile
{
/** Latitude in degrees north/south */
private int _latitude = 0;
- /** Longitude ini degrees east/west */
+ /** Longitude in degrees east/west */
private int _longitude = 0;
/**
@@ -26,6 +26,17 @@ public class SrtmTile
}
/**
+ * Constructor working out the tile for a single point
+ * @param inLatitude latitude in degrees
+ * @param inLongitude longitude in degrees
+ */
+ public SrtmTile(int inLatitude, int inLongitude)
+ {
+ _latitude = inLatitude;
+ _longitude = inLongitude;
+ }
+
+ /**
* Check for equality
* @param inOther other tile object
* @return true if both represent same tile
diff --git a/tim/prune/function/weather/GetWeatherForecastFunction.java b/tim/prune/function/weather/GetWeatherForecastFunction.java
new file mode 100644
index 0000000..7b6f8c7
--- /dev/null
+++ b/tim/prune/function/weather/GetWeatherForecastFunction.java
@@ -0,0 +1,480 @@
+package tim.prune.function.weather;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.table.TableCellRenderer;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.GpsPrune;
+import tim.prune.I18nManager;
+import tim.prune.data.DataPoint;
+import tim.prune.data.NumberUtils;
+import tim.prune.data.Track;
+import tim.prune.function.browser.BrowserLauncher;
+
+/**
+ * Function to display a weather forecast for the current location
+ * using the services of openweathermap.org
+ */
+public class GetWeatherForecastFunction extends GenericFunction implements Runnable
+{
+ /** Dialog object */
+ private JDialog _dialog = null;
+ /** Label for location */
+ private JLabel _locationLabel = null;
+ /** Label for the forecast update time */
+ private JLabel _updateTimeLabel = null;
+ /** Label for the sunrise and sunset times */
+ private JLabel _sunriseLabel = null;
+ /** Radio button for selecting current weather */
+ private JRadioButton _currentForecastRadio = null;
+ /** Radio button for selecting daily forecasts */
+ private JRadioButton _dailyForecastRadio = null;
+ /** Dropdown for selecting celsius / fahrenheit */
+ private JComboBox<String> _tempUnitsDropdown = null;
+ /** Table to hold the forecasts */
+ private JTable _forecastsTable = null;
+ /** Table model */
+ private WeatherTableModel _tableModel = new WeatherTableModel();
+ /** Set of previously obtained results, to avoid repeating calls */
+ private ResultSet _resultSet = new ResultSet();
+ /** Location id obtained from current forecast */
+ private String _locationId = null;
+ /** Flag to show that forecast is currently running, don't start another */
+ private boolean _isRunning = false;
+
+ /** True to just simulate the calls and read files instead, false to call real API */
+ private static final boolean SIMULATE_WITH_FILES = false;
+
+
+ /**
+ * Inner class to pass results asynchronously to the table model
+ */
+ private class ResultUpdater implements Runnable
+ {
+ private WeatherResults _results;
+ public ResultUpdater(WeatherResults inResults) {
+ _results = inResults;
+ }
+ public void run() {
+ _tableModel.setResults(_results);
+ adjustTable();
+ }
+ }
+
+
+ /** Constructor */
+ public GetWeatherForecastFunction(App inApp)
+ {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.getweatherforecast";
+ }
+
+ /**
+ * 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();
+ }
+ // Clear results
+ _locationId = null;
+ _tableModel.clear();
+ _locationLabel.setText(I18nManager.getText("confirm.running"));
+ _updateTimeLabel.setText("");
+ _sunriseLabel.setText("");
+ _currentForecastRadio.setSelected(true);
+
+ // Start new thread to load list asynchronously
+ new Thread(this).start();
+
+ _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(0, 4));
+
+ JPanel topPanel = new JPanel();
+ topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
+ _locationLabel = new JLabel(I18nManager.getText("confirm.running"));
+ _locationLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ topPanel.add(_locationLabel);
+ _updateTimeLabel = new JLabel(" ");
+ _updateTimeLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ topPanel.add(_updateTimeLabel);
+ _sunriseLabel = new JLabel(" ");
+ _sunriseLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ topPanel.add(_sunriseLabel);
+ JPanel radioPanel = new JPanel();
+ radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.X_AXIS));
+ radioPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
+ ButtonGroup forecastTypeGroup = new ButtonGroup();
+ _currentForecastRadio = new JRadioButton(I18nManager.getText("dialog.weather.currentforecast"));
+ _dailyForecastRadio = new JRadioButton(I18nManager.getText("dialog.weather.dailyforecast"));
+ JRadioButton threeHourlyRadio = new JRadioButton(I18nManager.getText("dialog.weather.3hourlyforecast"));
+ forecastTypeGroup.add(_currentForecastRadio);
+ forecastTypeGroup.add(_dailyForecastRadio);
+ forecastTypeGroup.add(threeHourlyRadio);
+ radioPanel.add(_currentForecastRadio);
+ radioPanel.add(_dailyForecastRadio);
+ radioPanel.add(threeHourlyRadio);
+ _currentForecastRadio.setSelected(true);
+ ActionListener radioListener = new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ if (!_isRunning) new Thread(GetWeatherForecastFunction.this).start();
+ }
+ };
+ _currentForecastRadio.addActionListener(radioListener);
+ _dailyForecastRadio.addActionListener(radioListener);
+ threeHourlyRadio.addActionListener(radioListener);
+ radioPanel.add(Box.createHorizontalGlue());
+ radioPanel.add(Box.createHorizontalStrut(40));
+
+ // Dropdown for temperature units
+ radioPanel.add(new JLabel(I18nManager.getText("dialog.weather.temperatureunits") + ": "));
+ _tempUnitsDropdown = new JComboBox<String>(new String[] {
+ I18nManager.getText("units.degreescelsius"), I18nManager.getText("units.degreesfahrenheit")
+ });
+ _tempUnitsDropdown.setMaximumSize(_tempUnitsDropdown.getPreferredSize());
+ _tempUnitsDropdown.addActionListener(radioListener);
+ radioPanel.add(_tempUnitsDropdown);
+ radioPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ topPanel.add(radioPanel);
+ dialogPanel.add(topPanel, BorderLayout.NORTH);
+
+ final IconRenderer iconRenderer = new IconRenderer();
+ _forecastsTable = new JTable(_tableModel)
+ {
+ public TableCellRenderer getCellRenderer(int row, int column) {
+ if ((row == WeatherTableModel.ROW_ICON)) {
+ return iconRenderer;
+ }
+ return super.getCellRenderer(row, column);
+ }
+ };
+ _forecastsTable.setRowSelectionAllowed(false);
+ _forecastsTable.setRowHeight(2, 55); // make just that row high enough to see icons
+ _forecastsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+ _forecastsTable.getTableHeader().setReorderingAllowed(false);
+ _forecastsTable.setShowHorizontalLines(false);
+
+ JScrollPane scroller = new JScrollPane(_forecastsTable);
+ scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
+ scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
+ scroller.setPreferredSize(new Dimension(500, 210));
+ scroller.getViewport().setBackground(Color.white);
+
+ dialogPanel.add(scroller, BorderLayout.CENTER);
+
+ // button panel at bottom
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ JButton launchButton = new JButton(I18nManager.getText("button.showwebpage"));
+ launchButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ BrowserLauncher.launchBrowser("http://openweathermap.org/city/" + (_locationId == null ? "" : _locationId));
+ }
+ });
+ buttonPanel.add(launchButton);
+ // close
+ JButton closeButton = new JButton(I18nManager.getText("button.close"));
+ closeButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(closeButton);
+ // Add a holder panel with a static label to credit openweathermap
+ JPanel southPanel = new JPanel();
+ southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS));
+ southPanel.add(new JLabel(I18nManager.getText("dialog.weather.creditnotice")));
+ southPanel.add(buttonPanel);
+ dialogPanel.add(southPanel, BorderLayout.SOUTH);
+ dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+ return dialogPanel;
+ }
+
+ /**
+ * Get the weather forecast in a separate thread
+ */
+ public void run()
+ {
+ if (_isRunning) {return;} // don't run twice
+ _isRunning = true;
+
+ // Are we getting the current details, or getting a forecast?
+ final boolean isCurrent = _locationId == null || _currentForecastRadio.isSelected();
+ final boolean isDailyForecast = _dailyForecastRadio.isSelected() && !isCurrent;
+ final boolean isHourlyForecast = !isCurrent && !isDailyForecast;
+ final boolean isUsingCelsius = _tempUnitsDropdown.getSelectedIndex() == 0;
+
+ // Have we got these results already? Look in store
+ WeatherResults results = _resultSet.getWeather(_locationId, isCurrent, isDailyForecast, isHourlyForecast, isUsingCelsius);
+ if (results == null)
+ {
+ if (isCurrent)
+ {
+ // Get the current details using either lat/long or locationId
+ results = getCurrentWeather(isUsingCelsius);
+ // If the current radio isn't selected, select it
+ if (!_currentForecastRadio.isSelected()) {
+ _currentForecastRadio.setSelected(true);
+ }
+ }
+ else
+ {
+ // Get the specified forecast using the retrieved locationId
+ results = getWeatherForecast(isDailyForecast, isUsingCelsius);
+ }
+ // If it's a valid answer, store it for later
+ if (results != null)
+ {
+ _resultSet.setWeather(results, _locationId, isCurrent, isDailyForecast, isHourlyForecast, isUsingCelsius);
+ }
+ }
+
+ // update table contents and labels
+ if (results != null)
+ {
+ SwingUtilities.invokeLater(new ResultUpdater(results));
+ _locationLabel.setText(I18nManager.getText("dialog.weather.location") + ": " + results.getLocationName());
+ final String ut = results.getUpdateTime();
+ _updateTimeLabel.setText(I18nManager.getText("dialog.weather.update") + ": " + (ut == null ? "" : ut));
+ if (results.getSunriseTime() != null && results.getSunsetTime() != null)
+ {
+ _sunriseLabel.setText(I18nManager.getText("dialog.weather.sunrise") + ": " + results.getSunriseTime()
+ + ", " + I18nManager.getText("dialog.weather.sunset") + ": " + results.getSunsetTime());
+ }
+ else {
+ _sunriseLabel.setText("");
+ }
+ }
+
+ // finished running
+ _isRunning = false;
+ }
+
+
+ /**
+ * Adjust the column widths and row heights to fit the displayed data
+ */
+ private void adjustTable()
+ {
+ if (!_tableModel.isEmpty())
+ {
+ // adjust column widths for all columns
+ for (int i=0; i<_forecastsTable.getColumnCount(); i++)
+ {
+ double maxWidth = 0.0;
+ for (int j=0; j<_forecastsTable.getRowCount(); j++)
+ {
+ final String value = _tableModel.getValueAt(j, i).toString();
+ maxWidth = Math.max(maxWidth, _forecastsTable.getCellRenderer(0, 0).getTableCellRendererComponent(
+ _forecastsTable, value, false, false, 0, 0).getPreferredSize().getWidth());
+ }
+ _forecastsTable.getColumnModel().getColumn(i).setMinWidth((int) maxWidth + 2);
+ }
+ // Set minimum row heights
+ final int labelHeight = (int) (_forecastsTable.getCellRenderer(0, 0).getTableCellRendererComponent(
+ _forecastsTable, "M", false, false, 0, 0).getMinimumSize().getHeight() * 1.2f + 4);
+ for (int i=0; i<_forecastsTable.getRowCount(); i++)
+ {
+ if (i == WeatherTableModel.ROW_ICON) {
+ _forecastsTable.setRowHeight(i, 55);
+ }
+ else {
+ _forecastsTable.setRowHeight(i, labelHeight);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the current weather using the lat/long and populate _results
+ * @param inUseCelsius true for celsius, false for fahrenheit
+ * @return weather results
+ */
+ private WeatherResults getCurrentWeather(boolean inUseCelsius)
+ {
+ final Track track = _app.getTrackInfo().getTrack();
+ if (track.getNumPoints() < 1) {return null;}
+ // Get coordinates to lookup
+ double lat = 0.0, lon = 0.0;
+ // See if a point is selected, if so use that
+ DataPoint currPoint = _app.getTrackInfo().getCurrentPoint();
+ if (currPoint != null)
+ {
+ // Use selected point
+ lat = currPoint.getLatitude().getDouble();
+ lon = currPoint.getLongitude().getDouble();
+ }
+ else
+ {
+ lat = track.getLatRange().getMidValue();
+ lon = track.getLonRange().getMidValue();
+ }
+
+ InputStream inStream = null;
+ // Build url either with coordinates or with location id if available
+ final String urlString = "http://api.openweathermap.org/data/2.5/weather?"
+ + (_locationId == null ? ("lat=" + NumberUtils.formatNumberUk(lat, 5) + "&lon=" + NumberUtils.formatNumberUk(lon, 5))
+ : ("id=" + _locationId))
+ + "&lang=" + I18nManager.getText("openweathermap.lang")
+ + "&mode=xml&units=" + (inUseCelsius ? "metric" : "imperial");
+ // System.out.println(urlString);
+
+ // Parse the returned XML with a special handler
+ OWMCurrentHandler xmlHandler = new OWMCurrentHandler();
+ try
+ {
+ URL url = new URL(urlString);
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ // DEBUG: Simulate the call in case of no network connection
+ if (SIMULATE_WITH_FILES)
+ {
+ inStream = new FileInputStream(new File("tim/prune/test/examplecurrentweather.xml"));
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException tie) {}
+ }
+ else
+ {
+ URLConnection conn = url.openConnection();
+ conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+ inStream = conn.getInputStream();
+ }
+
+ saxParser.parse(inStream, xmlHandler);
+ }
+ catch (Exception e)
+ {
+ // Show error message but don't close dialog
+ _app.showErrorMessageNoLookup(getNameKey(), e.getClass().getName() + " - " + e.getMessage());
+ _isRunning = false;
+ return null;
+ }
+ // Close stream and ignore errors
+ try {
+ inStream.close();
+ } catch (Exception e) {}
+
+ // Save the location id
+ if (xmlHandler.getLocationId() != null) {
+ _locationId = xmlHandler.getLocationId();
+ }
+ // Get the results from the handler and return
+ WeatherResults results = new WeatherResults();
+ results.setForecast(xmlHandler.getCurrentWeather());
+ results.setLocationName(xmlHandler.getLocationName());
+ results.setUpdateTime(xmlHandler.getUpdateTime());
+ results.setSunriseSunsetTimes(xmlHandler.getSunriseTime(), xmlHandler.getSunsetTime());
+ results.setTempsCelsius(inUseCelsius);
+ return results;
+ }
+
+
+ /**
+ * Get the weather forecast for the current location id and populate in _results
+ * @param inDaily true for daily, false for 3-hourly
+ * @param inCelsius true for celsius, false for fahrenheit
+ * @return weather results
+ */
+ private WeatherResults getWeatherForecast(boolean inDaily, boolean inCelsius)
+ {
+ InputStream inStream = null;
+ // Build URL
+ final String forecastCount = inDaily ? "8" : "3";
+ final String urlString = "http://api.openweathermap.org/data/2.5/forecast"
+ + (inDaily ? "/daily" : "") + "?id=" + _locationId + "&lang=" + I18nManager.getText("openweathermap.lang")
+ + "&mode=xml&units=" + (inCelsius ? "metric" : "imperial") + "&cnt=" + forecastCount;
+ // System.out.println(urlString);
+
+ // Parse the returned XML with a special handler
+ OWMForecastHandler xmlHandler = new OWMForecastHandler();
+ try
+ {
+ URL url = new URL(urlString);
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ // DEBUG: Simulate the call in case of no network connection
+ if (SIMULATE_WITH_FILES)
+ {
+ inStream = new FileInputStream(new File("tim/prune/test/exampleweatherforecast.xml"));
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException tie) {}
+ }
+ else
+ {
+ URLConnection conn = url.openConnection();
+ conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+ inStream = conn.getInputStream();
+ }
+
+ saxParser.parse(inStream, xmlHandler);
+ }
+ catch (Exception e)
+ {
+ // Show error message but don't close dialog
+ _app.showErrorMessageNoLookup(getNameKey(), e.getClass().getName() + " - " + e.getMessage());
+ _isRunning = false;
+ return null;
+ }
+ // Close stream and ignore errors
+ try {
+ inStream.close();
+ } catch (Exception e) {}
+
+ // Get results from handler, put in model
+ WeatherResults results = new WeatherResults();
+ results.setForecasts(xmlHandler.getForecasts());
+ results.setLocationName(xmlHandler.getLocationName());
+ results.setUpdateTime(xmlHandler.getUpdateTime());
+ results.setTempsCelsius(inCelsius);
+ return results;
+ }
+}
diff --git a/tim/prune/function/weather/IconRenderer.java b/tim/prune/function/weather/IconRenderer.java
new file mode 100644
index 0000000..caeb184
--- /dev/null
+++ b/tim/prune/function/weather/IconRenderer.java
@@ -0,0 +1,37 @@
+package tim.prune.function.weather;
+
+import java.awt.Component;
+import java.awt.Dimension;
+
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.SwingConstants;
+import javax.swing.table.TableCellRenderer;
+
+import tim.prune.gui.IconManager;
+
+/**
+ * Class to render the weather icons in the table
+ */
+public class IconRenderer extends JLabel implements TableCellRenderer
+{
+ /** Get the renderer component for the given row, column and value */
+ public Component getTableCellRendererComponent(JTable inTable, Object inValue, boolean inIsSelected,
+ boolean inHasFocus, int inRow, int inColumn)
+ {
+ if (inValue != null) {
+ setIcon(IconManager.getImageIcon("weather-" + inValue.toString()));
+ setHorizontalAlignment(SwingConstants.CENTER);
+ }
+ else {
+ setIcon(null);
+ setText("");
+ }
+ return this;
+ }
+
+ /** Override the minimum size method */
+ public Dimension getMinimumSize() {
+ return new Dimension(52, 52);
+ }
+}
diff --git a/tim/prune/function/weather/OWMCurrentHandler.java b/tim/prune/function/weather/OWMCurrentHandler.java
new file mode 100644
index 0000000..5380ba7
--- /dev/null
+++ b/tim/prune/function/weather/OWMCurrentHandler.java
@@ -0,0 +1,92 @@
+package tim.prune.function.weather;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * XML handler for dealing with the XML of current weather reports
+ * returned from openweathermap.org (forecasts have different structure)
+ */
+public class OWMCurrentHandler extends DefaultHandler
+{
+ /** The location name */
+ private String _locName = null;
+ /** The location id */
+ private String _locId = null;
+ /** The last update time */
+ private String _updateTime = null;
+ /** Sunrise and sunset times */
+ private String _sunriseTime = null, _sunsetTime = null;
+ /** The currently open forecast */
+ private SingleForecast _forecast = new SingleForecast();
+
+
+ /**
+ * React to the start of an XML tag
+ */
+ public void startElement(String inUri, String inLocalName, String inTagName,
+ Attributes inAttributes) throws SAXException
+ {
+ if (inTagName.equals("city")) {
+ _locName = inAttributes.getValue("name");
+ _locId = inAttributes.getValue("id");
+ }
+ else if (inTagName.equals("weather")) {
+ // numeric code, owm image name, description
+ _forecast.setSymbol(inAttributes.getValue("number"), inAttributes.getValue("icon"), inAttributes.getValue("value"));
+ }
+ else if (inTagName.equals("speed")) {
+ _forecast.setWindDesc(inAttributes.getValue("name"));
+ }
+ else if (inTagName.equals("temperature"))
+ {
+ String currTemp = inAttributes.getValue("value");
+ _forecast.setTemps(currTemp, currTemp);
+ // We can ignore the min and max here
+ }
+ else if (inTagName.equals("humidity")) {
+ _forecast.setHumidity(inAttributes.getValue("value") + inAttributes.getValue("unit"));
+ }
+ else if (inTagName.equals("lastupdate")) {
+ _updateTime = inAttributes.getValue("value");
+ }
+ else if (inTagName.equals("sun"))
+ {
+ _sunriseTime = inAttributes.getValue("rise");
+ _sunsetTime = inAttributes.getValue("set");
+ }
+
+ super.startElement(inUri, inLocalName, inTagName, inAttributes);
+ }
+
+ /** @return location name of forecast */
+ public String getLocationName() {
+ return _locName;
+ }
+
+ /** @return location id of forecast */
+ public String getLocationId() {
+ return _locId;
+ }
+
+ /** @return update time of report */
+ public String getUpdateTime() {
+ return _updateTime;
+ }
+
+ /** @return current weather conditions */
+ public SingleForecast getCurrentWeather() {
+ return _forecast;
+ }
+
+ /** @return sunrise time as 2013-07-25T03:55:14 */
+ public String getSunriseTime() {
+ return _sunriseTime;
+ }
+ /** @return sunset time as 2013-07-25T19:07:25 */
+ public String getSunsetTime() {
+ return _sunsetTime;
+ }
+}
diff --git a/tim/prune/function/weather/OWMForecastHandler.java b/tim/prune/function/weather/OWMForecastHandler.java
new file mode 100644
index 0000000..93bcc12
--- /dev/null
+++ b/tim/prune/function/weather/OWMForecastHandler.java
@@ -0,0 +1,102 @@
+package tim.prune.function.weather;
+
+import java.util.ArrayList;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * XML handler for dealing with the XML of weather forecasts
+ * returned from openweathermap.org (current weather has different structure)
+ */
+public class OWMForecastHandler extends DefaultHandler
+{
+ private String _value = null;
+ /** The location name */
+ private String _locName = null;
+ /** The forecast update time */
+ private String _updateTime = null;
+ /** The currently open forecast */
+ private SingleForecast _forecast = null;
+ /** List of all the forecasts found so far */
+ private ArrayList<SingleForecast> _forecastList = new ArrayList<SingleForecast>();
+
+
+ /**
+ * React to the start of an XML tag
+ */
+ public void startElement(String inUri, String inLocalName, String inTagName,
+ Attributes inAttributes) throws SAXException
+ {
+ if (inTagName.equals("time")) { // start of a new forecast
+ _forecast = new SingleForecast();
+ // date, timefrom, timeto
+ _forecast.setTime(inAttributes.getValue("day"), inAttributes.getValue("from"), inAttributes.getValue("to"));
+ }
+ else if (inTagName.equals("symbol")) {
+ // numeric code, owm image name, description
+ _forecast.setSymbol(inAttributes.getValue("number"), inAttributes.getValue("var"), inAttributes.getValue("name"));
+ }
+ else if (inTagName.equals("windSpeed")) {
+ _forecast.setWindDesc(inAttributes.getValue("name"));
+ }
+ else if (inTagName.equals("temperature")) {
+ _forecast.setTemps(inAttributes.getValue("min"), inAttributes.getValue("max"));
+ }
+ else if (inTagName.equals("humidity")) {
+ _forecast.setHumidity(inAttributes.getValue("value") + inAttributes.getValue("unit"));
+ }
+ _value = null;
+ super.startElement(inUri, inLocalName, inTagName, inAttributes);
+ }
+
+ /**
+ * React to the end of an XML tag
+ */
+ public void endElement(String inUri, String inLocalName, String inTagName)
+ throws SAXException
+ {
+ if (inTagName.equals("name")) {
+ _locName = _value;
+ }
+ else if (inTagName.equals("lastupdate")) {
+ _updateTime = _value;
+ }
+ else if (inTagName.equals("time"))
+ {
+ // End of a time tag, add the current forecast to the list
+ _forecastList.add(_forecast);
+ }
+ super.endElement(inUri, inLocalName, inTagName);
+ }
+
+ /**
+ * React to characters received inside tags
+ */
+ public void characters(char[] inCh, int inStart, int inLength)
+ throws SAXException
+ {
+ String value = new String(inCh, inStart, inLength);
+ _value = (_value==null?value:_value+value);
+ super.characters(inCh, inStart, inLength);
+ }
+
+ /** @return location name of forecast */
+ public String getLocationName() {
+ return _locName;
+ }
+
+ /** @return update time of forecast */
+ public String getUpdateTime() {
+ return _updateTime;
+ }
+
+ /**
+ * @return the list of forecasts
+ */
+ public ArrayList<SingleForecast> getForecasts() {
+ return _forecastList;
+ }
+}
diff --git a/tim/prune/function/weather/ResultSet.java b/tim/prune/function/weather/ResultSet.java
new file mode 100644
index 0000000..664d7ae
--- /dev/null
+++ b/tim/prune/function/weather/ResultSet.java
@@ -0,0 +1,83 @@
+package tim.prune.function.weather;
+
+/**
+ * Class to hold a set of (up to six) weather results,
+ * so that they don't have to be downloaded again
+ */
+public class ResultSet
+{
+ /** Array of six results */
+ private WeatherResults[] _results = new WeatherResults[6];
+ /** Location id for which these results apply */
+ private String _locationId = null;
+
+ /**
+ * Clear the array, forget all results
+ */
+ private void clear()
+ {
+ for (int i=0; i<6; i++) {
+ _results[i] = null;
+ }
+ }
+
+ /**
+ * Get the specified weather results, if available
+ * @param inLocationId location id
+ * @param inCurrent true to get the current weather
+ * @param inDaily true to get the daily forecast
+ * @param inHourly true to get the three-hourly forecast
+ * @param inCelsius true to get celsius
+ * @return weather results, or null if not available
+ */
+ public WeatherResults getWeather(String inLocationId,
+ boolean inCurrent, boolean inDaily, boolean inHourly, boolean inCelsius)
+ {
+ // Check location
+ if (inLocationId == null || _locationId == null || !inLocationId.equals(_locationId)) {
+ return null;
+ }
+ // check forecast type
+ final int numTypesGiven = (inCurrent ? 1 : 0) + (inDaily ? 1 : 0) + (inHourly ? 1 : 0);
+ if (numTypesGiven != 1) {
+ System.err.println("getWeather, numtypesgiven = " + numTypesGiven);
+ return null; // can't ask for more or less than one type
+ }
+ // Pull out from array
+ final int index = (inCurrent ? 0 : (inDaily ? 2 : 4)) + (inCelsius ? 1 : 0);
+ return _results[index];
+ }
+
+ /**
+ * Store the given weather results
+ * @param inResults results object
+ * @param inLocationId location id
+ * @param inCurrent true if this is the current weather
+ * @param inDaily true if this is the daily forecast
+ * @param inHourly true if this is the three-hourly forecast
+ * @param inCelsius true if numbers are celsius
+ */
+ public void setWeather(WeatherResults inResults, String inLocationId,
+ boolean inCurrent, boolean inDaily, boolean inHourly, boolean inCelsius)
+ {
+ // Check location
+ if (inLocationId == null || inLocationId.equals("")) {
+ return;
+ }
+ if (_locationId == null || !inLocationId.equals(_locationId))
+ {
+ // coordinates have changed
+ clear();
+ _locationId = inLocationId;
+ }
+ // check forecast type
+ final int numTypesGiven = (inCurrent ? 1 : 0) + (inDaily ? 1 : 0) + (inHourly ? 1 : 0);
+ if (numTypesGiven != 1) {
+ System.err.println("setWeather, numtypesgiven = " + numTypesGiven);
+ return; // can't set more or less than one type
+ }
+ // Store in array
+ final int index = (inCurrent ? 0 : (inDaily ? 2 : 4)) + (inCelsius ? 1 : 0);
+ _results[index] = inResults;
+ }
+}
diff --git a/tim/prune/function/weather/SingleForecast.java b/tim/prune/function/weather/SingleForecast.java
new file mode 100644
index 0000000..cfc1947
--- /dev/null
+++ b/tim/prune/function/weather/SingleForecast.java
@@ -0,0 +1,185 @@
+package tim.prune.function.weather;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * Class to represent a weather forecast
+ * for a single day or a 3-hour period
+ */
+public class SingleForecast
+{
+ private String _date = null;
+ private String _dayDescKey = null;
+ private String _timeFrom = null, _timeTo = null;
+ private String _imageName = null;
+ private String _desc = null;
+ private String _tempString = null;
+ private String _humidity = null;
+ private String _windDesc = null;
+
+ /** For getting today's and tomorrow's dates */
+ private static SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
+
+ /** Set the time of the forecast */
+ public void setTime(String inDate, String inTimeFrom, String inTimeTo)
+ {
+ _date = inDate;
+ if (inTimeFrom != null && inTimeFrom.length() > 10
+ && inTimeTo != null && inTimeTo.length() > 10)
+ {
+ _date = inTimeFrom.substring(0, 10);
+ _timeFrom = inTimeFrom.substring(11, 16);
+ _timeTo = inTimeTo.substring(11, 16);
+ }
+ _dayDescKey = getDayDescriptionKey(_date);
+ // System.out.println(_date + " is " + _dayDescKey);
+ }
+
+ /**
+ * Set the symbol details
+ */
+ public void setSymbol(String inNumber, String inImageCode, String inDesc)
+ {
+ _imageName = getIconName(inNumber, inImageCode);
+ // System.out.println("For number " + inNumber + "(" + inDesc + ") and code " + inImageCode + ", the symbol is " + _imageName);
+ _desc = inDesc;
+ }
+
+ /**
+ * Set the minimum and maximum temperatures (will be rounded to nearest int)
+ */
+ public void setTemps(String inMin, String inMax)
+ {
+ String tempMin = null, tempMax = null;
+ try {
+ tempMin = "" + Math.round(Double.parseDouble(inMin));
+ } catch (Exception e) {}; // tempMin stays null if temp can't be parsed
+ try {
+ tempMax = "" + Math.round(Double.parseDouble(inMax));
+ } catch (Exception e) {}; // tempMax stays null if temp can't be parsed
+
+ _tempString = tempMin;
+ if (tempMin != null && tempMax != null) {
+ if (!tempMin.equals(tempMax))
+ {
+ if (tempMin.charAt(0) == '-' && tempMax.charAt(0) != '-' && tempMax.charAt(0) != '0') {
+ // min temp is negative, max is positive, so add a + to the max
+ tempMax = "+" + tempMax;
+ }
+ _tempString = tempMin + " — " + tempMax;
+ }
+ }
+ else if (tempMax != null) {
+ _tempString = tempMax;
+ }
+ }
+
+ /** Set humidity */
+ public void setHumidity(String inHumidity) {
+ _humidity = inHumidity;
+ }
+
+ /** Set description of wind */
+ public void setWindDesc(String inDesc) {
+ _windDesc = inDesc;
+ }
+
+ /**
+ * Get the name of the image file for the given weather report
+ * @param inCode numeric three-digit code, as string
+ * @param inImage filename as given by openweather (just used for day/night)
+ * @return image file using GpsPrune's icons
+ */
+ public static String getIconName(String inCode, String inImage)
+ {
+ final boolean daytime = inImage == null || inImage.length() != 3 || inImage.charAt(2) != 'n';
+ final char leadDigit = (inCode == null || inCode.equals("")) ? '0' : inCode.charAt(0);
+ String iconName = null;
+ switch (leadDigit)
+ {
+ case '2': return "storm.png";
+ case '3': return "lightrain.png";
+ case '5':
+ iconName = "rain.png";
+ if (inCode.equals("500")) {iconName = "lightrain.png";}
+ else if (inCode.equals("511")) {iconName = "hail.png";}
+ break;
+ case '6': return "snow.png";
+ case '7': return "fog.png";
+ case '8':
+ iconName = daytime ? "clouds-day.png" : "clouds-night.png";
+ if (inCode.equals("800")) {iconName = daytime ? "clear-day.png" : "clear-night.png";}
+ else if (inCode.equals("804")) {iconName = "clouds.png";}
+ break;
+ case '9':
+ iconName = "extreme.png";
+ if (inCode.equals("906")) {iconName = "hail.png";}
+ break;
+ }
+ return iconName;
+ }
+
+ /**
+ * MAYBE: Maybe split off into separate DateFunctions class?
+ * @param inDate date
+ * @return day description, such as "today" or "saturday"
+ */
+ private static String getDayDescriptionKey(String inDate)
+ {
+ if (inDate == null || inDate.length() != 10) {return null;}
+ Calendar cal = Calendar.getInstance();
+ String todaysDate = DATE_FORMATTER.format(cal.getTime());
+ if (inDate.equals(todaysDate)) {return "today";}
+ cal.add(Calendar.DATE, 1);
+ String tomorrowsDate = DATE_FORMATTER.format(cal.getTime());
+ if (inDate.equals(tomorrowsDate)) {return "tomorrow";}
+ // Construct a date with this string and find out its day
+ try
+ {
+ cal.setTime(DATE_FORMATTER.parse(inDate));
+ switch (cal.get(Calendar.DAY_OF_WEEK))
+ {
+ case Calendar.MONDAY : return "monday";
+ case Calendar.TUESDAY : return "tuesday";
+ case Calendar.WEDNESDAY : return "wednesday";
+ case Calendar.THURSDAY : return "thursday";
+ case Calendar.FRIDAY : return "friday";
+ case Calendar.SATURDAY : return "saturday";
+ case Calendar.SUNDAY : return "sunday";
+ }
+ }
+ catch (ParseException pe) {}
+
+ return "other";
+ }
+
+ /** @return true if there are times present, not just a date */
+ public boolean hasTimes() {
+ return _timeFrom != null && _timeTo != null;
+ }
+ /** @return temperature range */
+ public String getTemps() {
+ return _tempString;
+ }
+
+ /** @return date */
+ public String getDate() {return _date;}
+ /** @return time from */
+ public String getTimeFrom() {return _timeFrom;}
+ /** @return time to */
+ public String getTimeTo() {return _timeTo;}
+ /** @return day description */
+ public String getDayDesc() {return _dayDescKey;}
+
+ /** @return image name */
+ public String getImageName() {return _imageName;}
+ /** @return description */
+ public String getDescription() {return _desc;}
+
+ /** @return humidity */
+ public String getHumidity() {return _humidity;}
+ /** @return wind description */
+ public String getWindDescription() {return _windDesc;}
+}
diff --git a/tim/prune/function/weather/WeatherResults.java b/tim/prune/function/weather/WeatherResults.java
new file mode 100644
index 0000000..10fd363
--- /dev/null
+++ b/tim/prune/function/weather/WeatherResults.java
@@ -0,0 +1,166 @@
+package tim.prune.function.weather;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import tim.prune.gui.DisplayUtils;
+
+
+/**
+ * Model for results of weather forecast from openweathermap.org
+ */
+public class WeatherResults
+{
+ /** List of forecasts */
+ private ArrayList<SingleForecast> _forecastList = new ArrayList<SingleForecast>();
+ /** Flag whether the units are metric (Celsius) or not (Fahrenheit) */
+ private boolean _tempsCelsius = true;
+ /** Location name */
+ private String _locationName = null;
+ /** Last update timestamp */
+ private String _updateTime = null;
+ /** Sunrise and sunset times as HH:MM */
+ private String _sunriseTime = null, _sunsetTime = null;
+
+
+ /**
+ * Add a single forecast to this model (for the current weather)
+ * @param inResults current results
+ */
+ public void setForecast(SingleForecast inResults)
+ {
+ _forecastList.clear();
+ if (inResults != null) {
+ _forecastList.add(inResults);
+ }
+ }
+
+ /**
+ * Add a list of forecasts to this model
+ * @param inList list of forecasts to add
+ */
+ public void setForecasts(ArrayList<SingleForecast> inList)
+ {
+ _forecastList.clear();
+ if (inList != null && inList.size() > 0) {
+ _forecastList.addAll(inList);
+ }
+ }
+
+ /** @return the number of forecasts */
+ public int getNumForecasts()
+ {
+ return _forecastList.size();
+ }
+
+ /**
+ * @param inIndex index of forecast starting from 0
+ * @return the specified forecast
+ */
+ public SingleForecast getForecast(int inIndex)
+ {
+ if (inIndex < 0 || inIndex >= getNumForecasts()) {
+ return null;
+ }
+ return _forecastList.get(inIndex);
+ }
+
+ /**
+ * Clear the list of forecasts
+ */
+ public void clear()
+ {
+ _forecastList.clear();
+ _sunriseTime = _sunsetTime = null;
+ _updateTime = null;
+ }
+
+ /**
+ * @param inCelsius true for celsius, false for fahrenheit
+ */
+ public void setTempsCelsius(boolean inCelsius)
+ {
+ _tempsCelsius = inCelsius;
+ }
+
+ /** @return true if this forecast uses Celsius */
+ public boolean isCelsius() {
+ return _tempsCelsius;
+ }
+
+ /**
+ * Set the sunrise and sunset times (only from current weather, not from forecast)
+ * @param inRiseTime sunrise time as YYYY-MM-DDThh:mm:ss
+ * @param inSetTime sunset time as YYYY-MM-DDThh:mm:ss
+ */
+ public void setSunriseSunsetTimes(String inRiseTime, String inSetTime)
+ {
+ _sunriseTime = _sunsetTime = null;
+ if (inRiseTime != null && inRiseTime.length() == 19
+ && inSetTime != null && inSetTime.length() == 19)
+ {
+ // Convert from UTC to system's time zone (not necessarily target's time zone!)
+ _sunriseTime = convertToLocalTimezone(inRiseTime.substring(11, 16));
+ _sunsetTime = convertToLocalTimezone(inSetTime.substring(11, 16));
+ }
+ }
+
+ /** @return sunrise time as HH:MM */
+ public String getSunriseTime() {
+ return _sunriseTime;
+ }
+ /** @return sunset time as HH:MM */
+ public String getSunsetTime() {
+ return _sunsetTime;
+ }
+
+ /** @param inName location name */
+ public void setLocationName(String inName) {
+ _locationName = inName;
+ }
+
+ /** @return location name */
+ public String getLocationName() {
+ return _locationName;
+ }
+
+ /** @param inTime timestamp of forecast */
+ public void setUpdateTime(String inTime) {
+ _updateTime = inTime;
+ }
+
+ /** @return timestamp of last update */
+ public String getUpdateTime()
+ {
+ return _updateTime;
+ }
+
+ /**
+ * Convert the given UTC time (HH:MM) into current timezone of computer
+ * @param inTimeString sunrise/sunset time in UTC (HH:MM)
+ * @return time in this timezone (HH:MM)
+ */
+ private static String convertToLocalTimezone(String inTimeString)
+ {
+ if (inTimeString != null && inTimeString.length() == 5)
+ {
+ try
+ {
+ final int hour = Integer.parseInt(inTimeString.substring(0, 2));
+ final int min = Integer.parseInt(inTimeString.substring(3));
+ Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+ utcCal.set(Calendar.HOUR_OF_DAY, hour);
+ utcCal.set(Calendar.MINUTE, min);
+ // Make a second calendar in the current time zone and apply values
+ Calendar currCal = Calendar.getInstance();
+ currCal.setTimeInMillis(utcCal.getTimeInMillis());
+ return DisplayUtils.makeTimeString(currCal.get(Calendar.HOUR_OF_DAY),
+ currCal.get(Calendar.MINUTE));
+ }
+ catch (NumberFormatException e) {} // ignore, just drop through
+ }
+ // Couldn't be parsed / converted
+ return inTimeString;
+ }
+}
diff --git a/tim/prune/function/weather/WeatherTableModel.java b/tim/prune/function/weather/WeatherTableModel.java
new file mode 100644
index 0000000..35444b6
--- /dev/null
+++ b/tim/prune/function/weather/WeatherTableModel.java
@@ -0,0 +1,124 @@
+package tim.prune.function.weather;
+
+import javax.swing.table.AbstractTableModel;
+
+import tim.prune.I18nManager;
+
+
+/**
+ * Table model for results of weather forecast
+ */
+public class WeatherTableModel extends AbstractTableModel
+{
+ /** Weather results */
+ private WeatherResults _results;
+
+ /** Row indices */
+ public static final int ROW_DAY = 0;
+ public static final int ROW_DESC = 1;
+ public static final int ROW_WIND = 2;
+ public static final int ROW_ICON = 3;
+ public static final int ROW_TEMP = 4;
+ public static final int ROW_HUMID = 5;
+
+ /** String for degrees Celsius */
+ private static final String UNITS_DEGC = I18nManager.getText("units.degreescelsius.short");
+ /** String for degrees Fahrenheit */
+ private static final String UNITS_DEGF = I18nManager.getText("units.degreesfahrenheit.short");
+
+ /**
+ * @return column count
+ */
+ public int getColumnCount()
+ {
+ if (_results == null) {return 0;}
+ return _results.getNumForecasts();
+ }
+
+ /**
+ * @param inColNum column number
+ * @return column label for given column
+ */
+ public String getColumnName(int inColNum)
+ {
+ if (_results != null && inColNum >= 0 && inColNum < getColumnCount())
+ {
+ SingleForecast forecast = _results.getForecast(inColNum);
+ if (!forecast.hasTimes() || forecast.getTimeFrom().startsWith("00")) {
+ return forecast.getDate();
+ }
+ return forecast.getTimeFrom();
+ }
+ return "";
+ }
+
+ /**
+ * @return number of rows
+ */
+ public int getRowCount()
+ {
+ return 6;
+ }
+
+ /** @return true if there are no columns */
+ public boolean isEmpty()
+ {
+ return getColumnCount() == 0;
+ }
+
+ /**
+ * @param inRowNum row number
+ * @param inColNum column number
+ * @return cell entry at given row and column
+ */
+ public Object getValueAt(int inRowNum, int inColNum)
+ {
+ if (inColNum < 0 || inColNum >= getColumnCount()) {return "";}
+ SingleForecast forecast = _results.getForecast(inColNum);
+ if (forecast != null)
+ {
+ switch (inRowNum)
+ {
+ case ROW_DAY: {
+ final String dayDesc = forecast.getDayDesc() == null ? "now" : forecast.getDayDesc();
+ return buildDisplayString(null, I18nManager.getText("dialog.weather.day." + dayDesc));
+ }
+ case ROW_DESC: return buildDisplayString(null, forecast.getDescription());
+ case ROW_WIND: return buildDisplayString(I18nManager.getText("dialog.weather.wind"), forecast.getWindDescription());
+ case ROW_ICON: return forecast.getImageName();
+ case ROW_TEMP: return buildDisplayString(I18nManager.getText("dialog.weather.temp"), forecast.getTemps()
+ + (_results.isCelsius() ? UNITS_DEGC : UNITS_DEGF));
+ case ROW_HUMID: return buildDisplayString(I18nManager.getText("dialog.weather.humidity"), forecast.getHumidity());
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Build a html string from the given title and value
+ */
+ private static final String buildDisplayString(String inTitle, String inValue)
+ {
+ if (inValue == null) {return null;}
+ return "<html>" + (inTitle == null ? "" : (inTitle + ": "))
+ + "<big>" + inValue.replaceAll(" ", " ") + "</big></html>";
+ }
+
+ /**
+ * Set the results
+ * @param inResults weather results including all forecasts
+ */
+ public void setResults(WeatherResults inResults)
+ {
+ _results = inResults;
+ fireTableStructureChanged();
+ }
+
+ /**
+ * Clear the list of forecasts
+ */
+ public void clear()
+ {
+ setResults(null);
+ }
+}
diff --git a/tim/prune/gui/BaseImageDefinitionPanel.java b/tim/prune/gui/BaseImageDefinitionPanel.java
new file mode 100644
index 0000000..d186bd2
--- /dev/null
+++ b/tim/prune/gui/BaseImageDefinitionPanel.java
@@ -0,0 +1,157 @@
+package tim.prune.gui;
+
+import java.awt.BorderLayout;
+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.JPanel;
+import javax.swing.border.EtchedBorder;
+
+import tim.prune.I18nManager;
+import tim.prune.data.Track;
+import tim.prune.gui.map.MapSource;
+import tim.prune.gui.map.MapSourceLibrary;
+import tim.prune.save.BaseImageConfigDialog;
+import tim.prune.save.BaseImageConsumer;
+import tim.prune.save.MapGrouter;
+import tim.prune.threedee.ImageDefinition;
+
+/**
+ * Panel used to show the current base image details
+ * and an edit button to change the definition
+ */
+public class BaseImageDefinitionPanel extends JPanel implements BaseImageConsumer
+{
+ /** Parent object (if any) */
+ private BaseImageConsumer _parent = null;
+ /** Label to describe the current settings */
+ private JLabel _baseImageLabel = null;
+ /** Button for changing the definition */
+ private JButton _editButton = null;
+ /** Dialog called by the "Edit" button to change the settings */
+ private BaseImageConfigDialog _baseImageConfig = null;
+
+
+ /**
+ * Constructor
+ * @param inParent parent object to inform about changes
+ * @param inParentDialog parent dialog
+ * @param inTrack track object
+ */
+ public BaseImageDefinitionPanel(BaseImageConsumer inParent, JDialog inParentDialog,
+ Track inTrack)
+ {
+ _parent = inParent;
+ _baseImageConfig = new BaseImageConfigDialog(this, inParentDialog, inTrack);
+
+ // Etched border
+ setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
+ );
+ setLayout(new BorderLayout());
+
+ // GUI components
+ JPanel subPanel = new JPanel();
+ subPanel.setLayout(new BorderLayout(10, 4));
+ subPanel.add(new JLabel(I18nManager.getText("dialog.exportpov.baseimage") + ": "), BorderLayout.WEST);
+ _baseImageLabel = new JLabel("Typical sourcename");
+ subPanel.add(_baseImageLabel, BorderLayout.CENTER);
+ _editButton = new JButton(I18nManager.getText("button.edit"));
+ _editButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ changeBaseImage();
+ }
+ });
+ subPanel.add(_editButton, BorderLayout.EAST);
+ add(subPanel, BorderLayout.NORTH);
+ }
+
+ /**
+ * @param inDefinition image definition from interactive step
+ */
+ public void initImageParameters(ImageDefinition inDefinition)
+ {
+ _baseImageConfig.setImageDefinition(inDefinition);
+ }
+
+ /**
+ * Change the base image by calling the BaseImageConfigDialog
+ */
+ private void changeBaseImage()
+ {
+ // Check if there is a cache to use
+ if (BaseImageConfigDialog.isImagePossible())
+ {
+ // Show new dialog to choose image details
+ _baseImageConfig.begin();
+ }
+ // TODO: What if it isn't possible? Should the caller show the error message?
+ //else {
+ // _app.showErrorMessage(getNameKey(), "dialog.exportimage.noimagepossible");
+ //}
+ }
+
+ /**
+ * Callback from base image config dialog
+ */
+ public void baseImageChanged()
+ {
+ updateBaseImageDetails();
+ if (_parent != null) {
+ _parent.baseImageChanged();
+ }
+ }
+
+ /**
+ * Update the description label according to the selected base image details
+ */
+ public void updateBaseImageDetails()
+ {
+ String desc = null;
+ ImageDefinition imageDef = _baseImageConfig.getImageDefinition();
+ // Check if selected zoom level is suitable or not, if not then set image to no
+ if (imageDef.getUseImage() && !_baseImageConfig.isSelectedZoomValid()) {
+ imageDef.setUseImage(false, imageDef.getSourceIndex(), 5);
+ }
+ // Make a description for the label
+ if (imageDef.getUseImage())
+ {
+ MapSource source = MapSourceLibrary.getSource(imageDef.getSourceIndex());
+ if (source != null)
+ {
+ desc = source.getName() + " (" + imageDef.getZoom() + ")";
+ }
+ }
+ if (desc == null) {
+ desc = I18nManager.getText("dialog.about.no");
+ }
+ _baseImageLabel.setText(desc);
+ _editButton.setEnabled(BaseImageConfigDialog.isImagePossible());
+ }
+
+ /**
+ * @return the grouter object for reuse of the prepared images
+ */
+ public MapGrouter getGrouter()
+ {
+ return _baseImageConfig.getGrouter();
+ }
+
+ /**
+ * @return image definition
+ */
+ public ImageDefinition getImageDefinition() {
+ return _baseImageConfig.getImageDefinition();
+ }
+
+ /**
+ * @return true if any tiles were found
+ */
+ public boolean getFoundData() {
+ return _baseImageConfig.getFoundData();
+ }
+}
diff --git a/tim/prune/gui/DecimalNumberField.java b/tim/prune/gui/DecimalNumberField.java
index f1baa2a..39c55d4 100644
--- a/tim/prune/gui/DecimalNumberField.java
+++ b/tim/prune/gui/DecimalNumberField.java
@@ -52,8 +52,7 @@ public class DecimalNumberField extends JTextField
*/
public DecimalNumberField()
{
- super(6);
- setDocument(new DecimalNumberDocument(false));
+ this(false);
}
/**
diff --git a/tim/prune/gui/DetailsDisplay.java b/tim/prune/gui/DetailsDisplay.java
index 13df7cd..f66d99b 100644
--- a/tim/prune/gui/DetailsDisplay.java
+++ b/tim/prune/gui/DetailsDisplay.java
@@ -82,8 +82,8 @@ public class DetailsDisplay extends GenericDisplay
private JPanel _playAudioPanel = null;
// Units
- private JComboBox _coordFormatDropdown = null;
- private JComboBox _distUnitsDropdown = null;
+ private JComboBox<String> _coordFormatDropdown = null;
+ private JComboBox<String> _distUnitsDropdown = null;
// Cached labels
private static final String LABEL_POINT_SELECTED = I18nManager.getText("details.index.selected") + ": ";
@@ -243,7 +243,7 @@ public class DetailsDisplay extends GenericDisplay
lowerPanel.add(coordFormatLabel);
String[] coordFormats = {I18nManager.getText("units.original"), I18nManager.getText("units.degminsec"),
I18nManager.getText("units.degmin"), I18nManager.getText("units.deg")};
- _coordFormatDropdown = new JComboBox(coordFormats);
+ _coordFormatDropdown = new JComboBox<String>(coordFormats);
_coordFormatDropdown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
@@ -256,7 +256,7 @@ public class DetailsDisplay extends GenericDisplay
unitsLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
lowerPanel.add(unitsLabel);
// Make dropdown for distance units
- _distUnitsDropdown = new JComboBox();
+ _distUnitsDropdown = new JComboBox<String>();
final UnitSet currUnits = Config.getUnitSet();
for (int i=0; i<UnitSetLibrary.getNumUnitSets(); i++) {
_distUnitsDropdown.addItem(I18nManager.getText(UnitSetLibrary.getUnitSet(i).getDistanceUnit().getNameKey()));
@@ -394,12 +394,13 @@ public class DetailsDisplay extends GenericDisplay
_rangeLabel.setText(LABEL_RANGE_SELECTED
+ (selection.getStart()+1) + " " + I18nManager.getText("details.range.to")
+ " " + (selection.getEnd()+1));
- _distanceLabel.setText(LABEL_RANGE_DISTANCE + DisplayUtils.roundedNumber(selection.getDistance()) + " " + distUnitsStr);
- if (selection.getNumSeconds() > 0)
+ _distanceLabel.setText(LABEL_RANGE_DISTANCE + DisplayUtils.roundedNumber(selection.getMovingDistance()) + " " + distUnitsStr);
+ final long numMovingSeconds = selection.getMovingSeconds();
+ if (numMovingSeconds > 0L)
{
- _durationLabel.setText(LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(selection.getNumSeconds()));
+ _durationLabel.setText(LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(numMovingSeconds));
_aveSpeedLabel.setText(I18nManager.getText("details.range.avespeed") + ": "
- + DisplayUtils.roundedNumber(selection.getDistance()/selection.getNumSeconds()*3600.0) + " " + speedUnitsStr);
+ + DisplayUtils.roundedNumber(selection.getMovingDistance()/numMovingSeconds*3600.0) + " " + speedUnitsStr);
}
else {
_durationLabel.setText("");
diff --git a/tim/prune/gui/DisplayUtils.java b/tim/prune/gui/DisplayUtils.java
index 49ca387..25640d8 100644
--- a/tim/prune/gui/DisplayUtils.java
+++ b/tim/prune/gui/DisplayUtils.java
@@ -70,4 +70,24 @@ public abstract class DisplayUtils
FORMAT_FLEX.setMinimumFractionDigits(numDigits);
return FORMAT_FLEX.format(inVal);
}
+
+ /**
+ * Convert the given hour and minute values into a string H:MM
+ * @param inHour hour of day, 0-24
+ * @param inMinute minute, 0-59
+ * @return string, either H:MM or HH:MM
+ */
+ public static String makeTimeString(int inHour, int inMinute)
+ {
+ StringBuilder sb = new StringBuilder();
+ final int hour = (inHour >= 0 && inHour <= 24) ? inHour : 0;
+ sb.append(hour);
+
+ sb.append(':');
+
+ final int minute = (inMinute >= 0 && inMinute <= 59) ? inMinute : 0;
+ if (minute <= 9) {sb.append('0');}
+ sb.append(minute);
+ return sb.toString();
+ }
}
diff --git a/tim/prune/load/MediaLoadProgressDialog.java b/tim/prune/gui/GenericProgressDialog.java
similarity index 73%
copy from tim/prune/load/MediaLoadProgressDialog.java
copy to tim/prune/gui/GenericProgressDialog.java
index a052b55..acef147 100644
--- a/tim/prune/load/MediaLoadProgressDialog.java
+++ b/tim/prune/gui/GenericProgressDialog.java
@@ -1,9 +1,10 @@
-package tim.prune.load;
+package tim.prune.gui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
+import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
@@ -16,24 +17,32 @@ import tim.prune.I18nManager;
import tim.prune.function.Cancellable;
/**
- * Class to show a progress dialog for loading media.
- * Used for regular photo / audio loads plus the async
- * loading function.
+ * Class to show a progress dialog for various time-consuming functions
*/
-public class MediaLoadProgressDialog
+public class GenericProgressDialog
{
private JDialog _progressDialog = null;
+ private String _dialogTitleKey = null;
+ private String _labelKey = null;
private JProgressBar _progressBar = null;
- private JFrame _parentFrame = null;
- private Cancellable _function = null;
+ private JFrame _parentFrame = null;
+ private Cancellable _function = null;
/**
* Constructor
+ * @param inTitleKey key for dialog title text
+ * @param inLabelKey key for label text
* @param inParentFrame parent frame for creating dialog
* @param inFunction function which can be cancelled
*/
- public MediaLoadProgressDialog(JFrame inParentFrame, Cancellable inFunction)
+ public GenericProgressDialog(String inTitleKey, String inLabelKey,
+ JFrame inParentFrame, Cancellable inFunction)
{
+ _dialogTitleKey = inTitleKey;
+ _labelKey = inLabelKey;
+ if (_labelKey == null) {
+ _labelKey = "confirm.running";
+ }
_parentFrame = inParentFrame;
_function = inFunction;
}
@@ -43,7 +52,7 @@ public class MediaLoadProgressDialog
*/
private void createProgressDialog()
{
- _progressDialog = new JDialog(_parentFrame, I18nManager.getText("dialog.jpegload.progress.title"));
+ _progressDialog = new JDialog(_parentFrame, I18nManager.getText(_dialogTitleKey));
_progressDialog.setLocationRelativeTo(_parentFrame);
_progressBar = new JProgressBar(0, 100);
_progressBar.setValue(0);
@@ -51,9 +60,10 @@ public class MediaLoadProgressDialog
_progressBar.setString("");
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
- panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
- panel.add(new JLabel(I18nManager.getText("dialog.jpegload.progress")));
+ panel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
+ panel.add(new JLabel(I18nManager.getText(_labelKey)));
panel.add(_progressBar);
+ panel.add(Box.createVerticalStrut(6)); // spacer
JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
diff --git a/tim/prune/gui/MediaListModel.java b/tim/prune/gui/MediaListModel.java
index e2a7307..4de374b 100644
--- a/tim/prune/gui/MediaListModel.java
+++ b/tim/prune/gui/MediaListModel.java
@@ -8,7 +8,7 @@ import tim.prune.data.MediaList;
/**
* Class to act as list model for the photo list and audio list
*/
-public class MediaListModel extends AbstractListModel
+public class MediaListModel extends AbstractListModel<String>
{
/** media list */
MediaList _media = null;
@@ -31,7 +31,7 @@ public class MediaListModel extends AbstractListModel
/**
* @see javax.swing.ListModel#getElementAt(int)
*/
- public Object getElementAt(int inIndex)
+ public String getElementAt(int inIndex)
{
MediaObject m = _media.getMedia(inIndex);
// * means modified since loading
diff --git a/tim/prune/gui/MenuManager.java b/tim/prune/gui/MenuManager.java
index 16a0a66..d2be8ad 100644
--- a/tim/prune/gui/MenuManager.java
+++ b/tim/prune/gui/MenuManager.java
@@ -73,6 +73,8 @@ public class MenuManager implements DataSubscriber
private JMenuItem _addAltitudeOffsetItem = null;
private JMenuItem _mergeSegmentsItem = null;
private JMenu _rearrangeMenu = null;
+ private JMenuItem _splitSegmentsItem = null;
+ private JMenuItem _sewSegmentsItem = null;
private JMenuItem _cutAndMoveItem = null;
private JMenuItem _convertNamesToTimesItem = null;
private JMenuItem _deleteFieldValuesItem = null;
@@ -83,8 +85,10 @@ public class MenuManager implements DataSubscriber
private JMenuItem _getGpsiesItem = null;
private JMenuItem _uploadGpsiesItem = null;
private JMenuItem _lookupSrtmItem = null;
+ private JMenuItem _downloadSrtmItem = null;
private JMenuItem _lookupWikipediaItem = null;
private JMenuItem _downloadOsmItem = null;
+ private JMenuItem _getWeatherItem = null;
private JMenuItem _distanceItem = null;
private JMenuItem _fullRangeDetailsItem = null;
private JMenuItem _estimateTimeItem = null;
@@ -235,6 +239,34 @@ public class MenuManager implements DataSubscriber
fileMenu.add(exitMenuItem);
menubar.add(fileMenu);
+ ////////////////////////////////////////////////////
+ // Online menu
+ JMenu onlineMenu = new JMenu(I18nManager.getText("menu.online"));
+ setAltKey(onlineMenu, "altkey.menu.online");
+ // SRTM
+ _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false);
+ onlineMenu.add(_lookupSrtmItem);
+ _downloadSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_SRTM, false);
+ onlineMenu.add(_downloadSrtmItem);
+ // Get gpsies tracks
+ _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false);
+ onlineMenu.add(_getGpsiesItem);
+ // Upload to gpsies
+ _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES, false);
+ onlineMenu.add(_uploadGpsiesItem);
+ onlineMenu.addSeparator();
+ _lookupWikipediaItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_WIKIPEDIA, false);
+ onlineMenu.add(_lookupWikipediaItem);
+ JMenuItem searchWikipediaNamesItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_WIKIPEDIA);
+ onlineMenu.add(searchWikipediaNamesItem);
+ _downloadOsmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_OSM, false);
+ onlineMenu.add(_downloadOsmItem);
+ onlineMenu.addSeparator();
+ _getWeatherItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_WEATHER_FORECAST, false);
+ onlineMenu.add(_getWeatherItem);
+ menubar.add(onlineMenu);
+
+ ////////////////////////////////////////////////////
// Track menu
JMenu trackMenu = new JMenu(I18nManager.getText("menu.track"));
setAltKey(trackMenu, "altkey.menu.track");
@@ -306,20 +338,12 @@ public class MenuManager implements DataSubscriber
rearrangeNearestItem.setEnabled(true);
_rearrangeMenu.add(rearrangeNearestItem);
trackMenu.add(_rearrangeMenu);
- // Get gpsies tracks
- _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false);
- trackMenu.add(_getGpsiesItem);
- // Upload to gpsies
- _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES, false);
- trackMenu.add(_uploadGpsiesItem);
- _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false);
- trackMenu.add(_lookupSrtmItem);
- _lookupWikipediaItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_WIKIPEDIA, false);
- trackMenu.add(_lookupWikipediaItem);
- JMenuItem searchWikipediaNamesItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_WIKIPEDIA);
- trackMenu.add(searchWikipediaNamesItem);
- _downloadOsmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_OSM, false);
- trackMenu.add(_downloadOsmItem);
+ // Split track segments
+ _splitSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SPLIT_SEGMENTS, false);
+ trackMenu.add(_splitSegmentsItem);
+ // Sew track segments
+ _sewSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SEW_SEGMENTS, false);
+ trackMenu.add(_sewSegmentsItem);
trackMenu.addSeparator();
_learnEstimationParams = makeMenuItem(FunctionLibrary.FUNCTION_LEARN_ESTIMATION_PARAMS, false);
trackMenu.add(_learnEstimationParams);
@@ -854,6 +878,8 @@ public class MenuManager implements DataSubscriber
_markRectangleItem.setEnabled(hasData);
_deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints());
_rearrangeMenu.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
+ _splitSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3);
+ _sewSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3);
_selectAllItem.setEnabled(hasData);
_selectNoneItem.setEnabled(hasData);
_show3dItem.setEnabled(hasMultiplePoints);
@@ -865,7 +891,10 @@ public class MenuManager implements DataSubscriber
_lookupSrtmItem.setEnabled(hasData);
_lookupWikipediaItem.setEnabled(hasData);
_downloadOsmItem.setEnabled(hasData);
+ _getWeatherItem.setEnabled(hasData);
_findWaypointItem.setEnabled(hasData && _track.hasWaypoints());
+ // have we got a cache?
+ _downloadSrtmItem.setEnabled(hasData && Config.getConfigString(Config.KEY_DISK_CACHE) != null);
// is undo available?
boolean hasUndo = !_app.getUndoStack().isEmpty();
diff --git a/tim/prune/gui/ProgressDialog.java b/tim/prune/gui/ProgressDialog.java
index d09087e..363989b 100644
--- a/tim/prune/gui/ProgressDialog.java
+++ b/tim/prune/gui/ProgressDialog.java
@@ -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;
@@ -72,9 +73,14 @@ public class ProgressDialog
JPanel dialogPanel = new JPanel();
dialogPanel.setLayout(new BorderLayout());
dialogPanel.add(new JLabel(I18nManager.getText("confirm.running")), BorderLayout.NORTH);
+ // Centre panel with an empty border
+ JPanel centrePanel = new JPanel();
+ centrePanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+ centrePanel.setLayout(new BorderLayout());
_progressBar = new JProgressBar();
_progressBar.setPreferredSize(new Dimension(250, 30));
- dialogPanel.add(_progressBar, BorderLayout.CENTER);
+ centrePanel.add(_progressBar, BorderLayout.CENTER);
+ dialogPanel.add(centrePanel, BorderLayout.CENTER);
// Cancel button at the bottom
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
diff --git a/tim/prune/gui/SelectorDisplay.java b/tim/prune/gui/SelectorDisplay.java
index 0f2b66b..a4df9cd 100644
--- a/tim/prune/gui/SelectorDisplay.java
+++ b/tim/prune/gui/SelectorDisplay.java
@@ -43,15 +43,15 @@ public class SelectorDisplay extends GenericDisplay
private int _visiblePanels = 1;
// Waypoints
private JPanel _waypointListPanel = null;
- private JList _waypointList = null;
+ private JList<String> _waypointList = null;
private WaypointListModel _waypointListModel = null;
// Photos
private JPanel _photoListPanel = null;
- private JList _photoList = null;
+ private JList<String> _photoList = null;
private MediaListModel _photoListModel = null;
// Audio files
private JPanel _audioListPanel = null;
- private JList _audioList = null;
+ private JList<String> _audioList = null;
private MediaListModel _audioListModel = null;
// scrollbar interval
@@ -106,7 +106,7 @@ public class SelectorDisplay extends GenericDisplay
BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
);
_waypointListModel = new WaypointListModel(_trackInfo.getTrack());
- _waypointList = new JList(_waypointListModel);
+ _waypointList = new JList<String>(_waypointListModel);
_waypointList.setVisibleRowCount(NUM_LIST_ENTRIES);
_waypointList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
@@ -118,7 +118,7 @@ public class SelectorDisplay extends GenericDisplay
_listsPanel.add(_waypointListPanel);
// photo list
_photoListModel = new MediaListModel(_trackInfo.getPhotoList());
- _photoList = new JList(_photoListModel);
+ _photoList = new JList<String>(_photoListModel);
_photoList.setVisibleRowCount(NUM_LIST_ENTRIES);
_photoList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
@@ -132,7 +132,7 @@ public class SelectorDisplay extends GenericDisplay
// List for audio clips
_audioListModel = new MediaListModel(_trackInfo.getAudioList());
- _audioList = new JList(_audioListModel);
+ _audioList = new JList<String>(_audioListModel);
_audioList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
{
@@ -313,7 +313,7 @@ public class SelectorDisplay extends GenericDisplay
* @param inList list object
* @return panel object
*/
- private static JPanel makeListPanel(String inNameKey, JList inList)
+ private static JPanel makeListPanel(String inNameKey, JList<String> inList)
{
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
diff --git a/tim/prune/gui/TerrainDefinitionPanel.java b/tim/prune/gui/TerrainDefinitionPanel.java
new file mode 100644
index 0000000..690c1ae
--- /dev/null
+++ b/tim/prune/gui/TerrainDefinitionPanel.java
@@ -0,0 +1,85 @@
+package tim.prune.gui;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import tim.prune.I18nManager;
+import tim.prune.threedee.TerrainDefinition;
+
+/**
+ * Gui component for defining the 3d terrain,
+ * including whether to use one or not, and if so
+ * what resolution to use for the grid
+ */
+public class TerrainDefinitionPanel extends JPanel
+{
+ /** Checkbox to use a terrain or not */
+ private JCheckBox _useCheckbox = null;
+ /** Field for entering the grid size */
+ private WholeNumberField _gridSizeField = null;
+
+
+ /**
+ * Constructor
+ */
+ public TerrainDefinitionPanel()
+ {
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ // Components
+ _useCheckbox = new JCheckBox(I18nManager.getText("dialog.3d.useterrain"));
+ _useCheckbox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ activateGridField();
+ }
+ });
+ add(_useCheckbox);
+ add(Box.createHorizontalGlue());
+ JLabel label = new JLabel(I18nManager.getText("dialog.3d.terraingridsize") + ": ");
+ add(label);
+ _gridSizeField = new WholeNumberField(4);
+ _gridSizeField.setValue(10);
+ _gridSizeField.setMaximumSize(new Dimension(100, 50));
+ _gridSizeField.setEnabled(false);
+ add(_gridSizeField);
+ }
+
+ /**
+ * @param inDefinition terrain parameters to set
+ */
+ public void initTerrainParameters(TerrainDefinition inDefinition)
+ {
+ _useCheckbox.setSelected(inDefinition != null && inDefinition.getUseTerrain());
+ if (inDefinition != null && inDefinition.getGridSize() > 0) {
+ _gridSizeField.setValue(inDefinition.getGridSize());
+ }
+ activateGridField();
+ }
+
+ /**
+ * @return true if the terrain is selected
+ */
+ public boolean getUseTerrain() {
+ return _useCheckbox.isSelected() && getGridSize() > 2;
+ }
+
+ /**
+ * @return number of nodes along each side of the grid
+ */
+ public int getGridSize() {
+ return _gridSizeField.getValue();
+ }
+
+ /**
+ * Set the grid field to be enabled or not based on the checkbox
+ */
+ private void activateGridField() {
+ _gridSizeField.setEnabled(_useCheckbox.isSelected());
+ }
+}
diff --git a/tim/prune/gui/UndoManager.java b/tim/prune/gui/UndoManager.java
index 1b77fe1..48307c1 100644
--- a/tim/prune/gui/UndoManager.java
+++ b/tim/prune/gui/UndoManager.java
@@ -28,7 +28,7 @@ public class UndoManager
{
private App _app;
private JDialog _dialog;
- private JList _actionList;
+ private JList<String> _actionList;
/**
@@ -52,7 +52,7 @@ public class UndoManager
{
undoActions[i] = undoStack.elementAt(undoStack.size()-1-i).getDescription();
}
- _actionList = new JList(undoActions);
+ _actionList = new JList<String>(undoActions);
_actionList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
_actionList.setSelectedIndex(0);
_actionList.addListSelectionListener(new ListSelectionListener()
diff --git a/tim/prune/gui/WaypointListModel.java b/tim/prune/gui/WaypointListModel.java
index 3508697..3d5dd1f 100644
--- a/tim/prune/gui/WaypointListModel.java
+++ b/tim/prune/gui/WaypointListModel.java
@@ -9,7 +9,7 @@ import tim.prune.data.Track;
/**
* Class to act as list model for the waypoint list
*/
-public class WaypointListModel extends AbstractListModel
+public class WaypointListModel extends AbstractListModel<String>
{
Track _track = null;
ArrayList<DataPoint> _waypoints = null;
@@ -36,7 +36,7 @@ public class WaypointListModel extends AbstractListModel
/**
* @see javax.swing.ListModel#getElementAt(int)
*/
- public Object getElementAt(int inIndex)
+ public String getElementAt(int inIndex)
{
DataPoint p = null;
if (inIndex < 0 || inIndex >= getSize()
diff --git a/tim/prune/gui/WaypointNameMatcher.java b/tim/prune/gui/WaypointNameMatcher.java
index db0f827..83a10bb 100644
--- a/tim/prune/gui/WaypointNameMatcher.java
+++ b/tim/prune/gui/WaypointNameMatcher.java
@@ -10,7 +10,7 @@ import tim.prune.data.Track;
* Class to deal with the matching of waypoint names
* and the representation in a list
*/
-public class WaypointNameMatcher extends AbstractListModel
+public class WaypointNameMatcher extends AbstractListModel<String>
{
private ArrayList<DataPoint> _waypoints = null;
private int _numPoints = 0;
@@ -73,7 +73,7 @@ public class WaypointNameMatcher extends AbstractListModel
/**
* @see javax.swing.ListModel#getElementAt(int)
*/
- public Object getElementAt(int inIndex)
+ public String getElementAt(int inIndex)
{
return _matches.get(inIndex).getWaypointName();
}
diff --git a/tim/prune/gui/images/weather-clear-day.png b/tim/prune/gui/images/weather-clear-day.png
new file mode 100644
index 0000000..d6d011d
Binary files /dev/null and b/tim/prune/gui/images/weather-clear-day.png differ
diff --git a/tim/prune/gui/images/weather-clear-night.png b/tim/prune/gui/images/weather-clear-night.png
new file mode 100644
index 0000000..51580ea
Binary files /dev/null and b/tim/prune/gui/images/weather-clear-night.png differ
diff --git a/tim/prune/gui/images/weather-clouds-day.png b/tim/prune/gui/images/weather-clouds-day.png
new file mode 100644
index 0000000..bebf3fb
Binary files /dev/null and b/tim/prune/gui/images/weather-clouds-day.png differ
diff --git a/tim/prune/gui/images/weather-clouds-night.png b/tim/prune/gui/images/weather-clouds-night.png
new file mode 100644
index 0000000..a6e4bc5
Binary files /dev/null and b/tim/prune/gui/images/weather-clouds-night.png differ
diff --git a/tim/prune/gui/images/weather-clouds.png b/tim/prune/gui/images/weather-clouds.png
new file mode 100644
index 0000000..e4be440
Binary files /dev/null and b/tim/prune/gui/images/weather-clouds.png differ
diff --git a/tim/prune/gui/images/weather-extreme.png b/tim/prune/gui/images/weather-extreme.png
new file mode 100644
index 0000000..48264c7
Binary files /dev/null and b/tim/prune/gui/images/weather-extreme.png differ
diff --git a/tim/prune/gui/images/weather-fog.png b/tim/prune/gui/images/weather-fog.png
new file mode 100644
index 0000000..5df0e1c
Binary files /dev/null and b/tim/prune/gui/images/weather-fog.png differ
diff --git a/tim/prune/gui/images/weather-hail.png b/tim/prune/gui/images/weather-hail.png
new file mode 100644
index 0000000..563e92b
Binary files /dev/null and b/tim/prune/gui/images/weather-hail.png differ
diff --git a/tim/prune/gui/images/weather-lightrain.png b/tim/prune/gui/images/weather-lightrain.png
new file mode 100644
index 0000000..0ba21e2
Binary files /dev/null and b/tim/prune/gui/images/weather-lightrain.png differ
diff --git a/tim/prune/gui/images/weather-rain.png b/tim/prune/gui/images/weather-rain.png
new file mode 100644
index 0000000..35d7b1d
Binary files /dev/null and b/tim/prune/gui/images/weather-rain.png differ
diff --git a/tim/prune/gui/images/weather-snow.png b/tim/prune/gui/images/weather-snow.png
new file mode 100644
index 0000000..286fb28
Binary files /dev/null and b/tim/prune/gui/images/weather-snow.png differ
diff --git a/tim/prune/gui/images/weather-storm.png b/tim/prune/gui/images/weather-storm.png
new file mode 100644
index 0000000..86e4271
Binary files /dev/null and b/tim/prune/gui/images/weather-storm.png differ
diff --git a/tim/prune/gui/map/CloudmadeMapSource.java b/tim/prune/gui/map/CloudmadeMapSource.java
deleted file mode 100644
index 17d4efe..0000000
--- a/tim/prune/gui/map/CloudmadeMapSource.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package tim.prune.gui.map;
-
-/**
- * Class to act as a source for Cloudmade maps with a given style
- */
-public class CloudmadeMapSource extends OsmMapSource
-{
- /** Selected style number */
- private String _style = null;
- /** Server prefix including API-key unique to GpsPrune application */
- private static final String SERVER_PREFIX = "[abc].tile.cloudmade.com/03d86b66f51f4a3b8c236ac06f2a2e57/";
-
- /**
- * Constructor
- * @param inName name to use for map source
- * @param inStyle style, given as integer
- * @param inMaxZoom maximum zoom level, 18 by default
- */
- public CloudmadeMapSource(String inName, String inStyle, int inMaxZoom)
- {
- // Note: Could check style for valid integer value here
- super(inName, SERVER_PREFIX + inStyle + "/256/", null, inMaxZoom);
- _style = inStyle;
- }
-
- /**
- * @return semicolon-separated list of all fields
- */
- public String getConfigString()
- {
- return "c:" + getName() + ";" + _style + ";" + getMaxZoomLevel();
- }
-
- /**
- * Construct a new map source from its config string
- * @param inConfigString string from Config, separated by semicolons
- * @return new map source, or null if not parseable
- */
- public static CloudmadeMapSource fromConfig(String inConfigString)
- {
- CloudmadeMapSource source = null;
- if (inConfigString.startsWith("c:"))
- {
- String[] items = inConfigString.substring(2).split(";");
- try {
- if (items.length == 3) {
- source = new CloudmadeMapSource(items[0], items[1], Integer.parseInt(items[2]));
- }
- } catch (NumberFormatException nfe) {}
- }
- return source;
- }
-
- /**
- * @return style as string, only required to populate edit dialog
- */
- public String getStyle()
- {
- return _style;
- }
-}
diff --git a/tim/prune/gui/map/DiskTileCacher.java b/tim/prune/gui/map/DiskTileCacher.java
index 350b3b4..459b140 100644
--- a/tim/prune/gui/map/DiskTileCacher.java
+++ b/tim/prune/gui/map/DiskTileCacher.java
@@ -63,7 +63,9 @@ public class DiskTileCacher implements Runnable
try {
image = Toolkit.getDefaultToolkit().createImage(tileFile.getAbsolutePath());
}
- catch (Exception e) {}
+ catch (Exception e) {
+ System.err.println("createImage: " + e.getClass().getName() + " _ " + e.getMessage());
+ }
}
}
return image;
@@ -110,7 +112,12 @@ public class DiskTileCacher implements Runnable
private static boolean isBeingLoaded(File inFile)
{
File tempFile = new File(inFile.getAbsolutePath() + ".temp");
- return tempFile.exists();
+ if (!tempFile.exists()) {
+ return false;
+ }
+ // File exists, so check if it was created recently
+ final long fileAge = System.currentTimeMillis() - tempFile.lastModified();
+ return fileAge < 1000000L; // overwrite if the temp file is still there after 1000s
}
/**
@@ -125,7 +132,7 @@ public class DiskTileCacher implements Runnable
// Use a synchronized block across all threads to make sure this url is only fetched once
synchronized (DiskTileCacher.class)
{
- if (tempFile.exists()) {return;}
+ if (tempFile.exists()) {tempFile.delete();}
try {
if (!tempFile.createNewFile()) {return;}
}
diff --git a/tim/prune/gui/map/MapCanvas.java b/tim/prune/gui/map/MapCanvas.java
index a6d5a88..a62b108 100644
--- a/tim/prune/gui/map/MapCanvas.java
+++ b/tim/prune/gui/map/MapCanvas.java
@@ -31,6 +31,7 @@ import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSlider;
+import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
@@ -57,12 +58,13 @@ import tim.prune.function.compress.MarkPointsInRectangleFunction;
import tim.prune.function.edit.FieldEdit;
import tim.prune.function.edit.FieldEditList;
import tim.prune.gui.IconManager;
+import tim.prune.tips.TipManager;
/**
* Class for the map canvas, to display a background map and draw on it
*/
public class MapCanvas extends JPanel implements MouseListener, MouseMotionListener, DataSubscriber,
- KeyListener, MouseWheelListener
+ KeyListener, MouseWheelListener, TileConsumer
{
/** App object for callbacks */
private App _app = null;
@@ -292,6 +294,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
add(_scaleBar, BorderLayout.SOUTH);
// Make popup menu
makePopup();
+ // Get currently selected map from Config, pass to MapTileManager
+ _tileManager.setMapSource(Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX));
}
@@ -508,8 +512,19 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
g.fillRect(0, 0, getWidth(), getHeight());
// Check whether maps are on or not
- boolean showMap = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
+ final boolean showMap = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
_mapCheckBox.setSelected(showMap);
+ // Check whether disk cache is on or not
+ final boolean usingDiskCache = Config.getConfigString(Config.KEY_DISK_CACHE) != null;
+ // Show tip to recommend setting up a cache
+ if (showMap && !usingDiskCache && Config.getConfigBoolean(Config.KEY_ONLINE_MODE))
+ {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ _app.showTip(TipManager.Tip_UseAMapCache);
+ }
+ });
+ }
// reset error message
if (!showMap) {_shownOsmErrorAlready = false;}
@@ -544,7 +559,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
// Loop over layers
for (int l=0; l<numLayers; l++)
{
- Image image = _tileManager.getTile(l, tileX, tileY);
+ Image image = _tileManager.getTile(l, tileX, tileY, true);
if (image != null) {
g.drawImage(image, x, y, 256, 256, null);
}
@@ -860,22 +875,25 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
* Inform that tiles have been updated and the map can be repainted
* @param inIsOk true if data loaded ok, false for error
*/
- public synchronized void tilesUpdated(boolean inIsOk)
+ public void tilesUpdated(boolean inIsOk)
{
- // Show message if loading failed (but not too many times)
- if (!inIsOk && !_shownOsmErrorAlready && _mapCheckBox.isSelected())
+ synchronized(this)
{
- _shownOsmErrorAlready = true;
- // use separate thread to show message about failing to load osm images
- new Thread(new Runnable() {
- public void run() {
- try {Thread.sleep(500);} catch (InterruptedException ie) {}
- _app.showErrorMessage("error.osmimage.dialogtitle", "error.osmimage.failed");
- }
- }).start();
+ // Show message if loading failed (but not too many times)
+ if (!inIsOk && !_shownOsmErrorAlready && _mapCheckBox.isSelected())
+ {
+ _shownOsmErrorAlready = true;
+ // use separate thread to show message about failing to load osm images
+ new Thread(new Runnable() {
+ public void run() {
+ try {Thread.sleep(500);} catch (InterruptedException ie) {}
+ _app.showErrorMessage("error.osmimage.dialogtitle", "error.osmimage.failed");
+ }
+ }).start();
+ }
+ _recalculate = true;
+ repaint();
}
- _recalculate = true;
- repaint();
}
/**
@@ -1168,7 +1186,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
_app.setCurrentMode(App.AppMode.NORMAL);
_drawMode = MODE_DEFAULT;
// Call a function to mark the points
- MarkPointsInRectangleFunction marker = new MarkPointsInRectangleFunction(_app);
+ MarkPointsInRectangleFunction marker = (MarkPointsInRectangleFunction) FunctionLibrary.FUNCTION_MARK_IN_RECTANGLE;
double lon1 = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_dragFromX, getWidth()));
double lat1 = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(_dragFromY, getHeight()));
double lon2 = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_dragToX, getWidth()));
@@ -1298,7 +1316,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
_checkBounds = true;
}
if ((inUpdateType & DataSubscriber.MAPSERVER_CHANGED) > 0) {
- _tileManager.resetConfig();
+ // Get the selected map source index and pass to tile manager
+ _tileManager.setMapSource(Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX));
}
if ((inUpdateType & (DataSubscriber.DATA_ADDED_OR_REMOVED + DataSubscriber.DATA_EDITED)) > 0) {
_midpoints.updateData(_track);
diff --git a/tim/prune/gui/map/MapSourceLibrary.java b/tim/prune/gui/map/MapSourceLibrary.java
index 5c20eac..b82fd4e 100644
--- a/tim/prune/gui/map/MapSourceLibrary.java
+++ b/tim/prune/gui/map/MapSourceLibrary.java
@@ -47,7 +47,6 @@ public abstract class MapSourceLibrary
"http://toolserver.org/~cmarqu/hill/", 18));
_sourceList.add(new OsmMapSource("Openseamap", "http://tile.openstreetmap.org/",
"http://tiles.openseamap.org/seamark/", 18));
- _sourceList.add(new CloudmadeMapSource("Pale Dawn", "998", 18));
}
/**
@@ -64,7 +63,6 @@ public abstract class MapSourceLibrary
{
String sourceString = configString.substring(0, splitPos);
MapSource source = OsmMapSource.fromConfig(sourceString);
- if (source == null) {source = CloudmadeMapSource.fromConfig(sourceString);}
if (source != null) {
_sourceList.add(source);
}
diff --git a/tim/prune/gui/map/MapTileManager.java b/tim/prune/gui/map/MapTileManager.java
index 75f170a..ed8849b 100644
--- a/tim/prune/gui/map/MapTileManager.java
+++ b/tim/prune/gui/map/MapTileManager.java
@@ -7,18 +7,23 @@ import java.net.URL;
import tim.prune.config.Config;
+
/**
* Class responsible for managing the map tiles,
* including invoking the correct memory cacher(s) and/or disk cacher(s)
*/
public class MapTileManager implements ImageObserver
{
- /** Parent object to inform when tiles received */
- private MapCanvas _parent = null;
+ /** Consumer object to inform when tiles received */
+ private TileConsumer _consumer = null;
/** Current map source */
private MapSource _mapSource = null;
/** Array of tile caches, one per layer */
private MemTileCacher[] _tempCaches = null;
+ /** Flag for whether to download any tiles or just pull from disk */
+ private boolean _downloadTiles = true;
+ /** Flag for whether to return incomplete images or just pass to tile cache until they're done */
+ private boolean _returnIncompleteImages = false;
/** Number of layers */
private int _numLayers = -1;
/** Current zoom level */
@@ -29,14 +34,11 @@ public class MapTileManager implements ImageObserver
/**
* Constructor
- * @param inParent parent canvas to be informed of updates
+ * @param inConsumer consumer object to be notified
*/
- public MapTileManager(MapCanvas inParent)
+ public MapTileManager(TileConsumer inConsumer)
{
- _parent = inParent;
- // Adjust the index of the selected map source
- adjustSelectedMap();
- resetConfig();
+ _consumer = inConsumer;
}
/**
@@ -47,9 +49,7 @@ public class MapTileManager implements ImageObserver
*/
public void centreMap(int inZoom, int inTileX, int inTileY)
{
- _zoom = inZoom;
- // Calculate number of tiles = 2^^zoom
- _numTileIndices = 1 << _zoom;
+ setZoom(inZoom);
// Pass params onto all memory cachers
if (_tempCaches != null) {
for (int i=0; i<_tempCaches.length; i++) {
@@ -58,6 +58,14 @@ public class MapTileManager implements ImageObserver
}
}
+ /** @param inZoom zoom level to set */
+ public void setZoom(int inZoom)
+ {
+ _zoom = inZoom;
+ // Calculate number of tiles = 2^^zoom
+ _numTileIndices = 1 << _zoom;
+ }
+
/**
* @return true if zoom is too high for tiles
*/
@@ -69,6 +77,21 @@ public class MapTileManager implements ImageObserver
}
/**
+ * Enable or disable tile downloading
+ * @param inEnabled true to enable downloading, false to just get tiles from disk
+ */
+ public void enableTileDownloading(boolean inEnabled)
+ {
+ _downloadTiles = inEnabled;
+ }
+
+ /** Configure to return incomplete images instead of going via caches (and another call) */
+ public void setReturnIncompleteImages()
+ {
+ _returnIncompleteImages = true;
+ }
+
+ /**
* Clear all the memory caches due to changed config / zoom
*/
public void clearMemoryCaches()
@@ -91,35 +114,22 @@ public class MapTileManager implements ImageObserver
}
/**
- * Reset the map source configuration, apparently it has changed
+ * @param inSourceNum selected map source index
*/
- public void resetConfig()
+ public void setMapSource(int inSourceNum)
{
- int sourceNum = Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX);
- _mapSource = MapSourceLibrary.getSource(sourceNum);
- if (_mapSource == null) {_mapSource = MapSourceLibrary.getSource(0);}
- clearMemoryCaches();
- _numLayers = _mapSource.getNumLayers();
+ setMapSource(MapSourceLibrary.getSource(inSourceNum));
}
/**
- * Adjust the index of the selected map
- * (only required if config was loaded from a previous version of GpsPrune)
+ * @param inMapSource selected map source
*/
- private void adjustSelectedMap()
+ public void setMapSource(MapSource inMapSource)
{
- int sourceNum = Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX);
- int prevNumFixed = Config.getConfigInt(Config.KEY_NUM_FIXED_MAPS);
- // Number of fixed maps not specified in version <=13, default to 6
- if (prevNumFixed == 0) prevNumFixed = 6;
- int currNumFixed = MapSourceLibrary.getNumFixedSources();
- // Only need to do something if the number has changed
- if (currNumFixed != prevNumFixed && (sourceNum >= prevNumFixed || sourceNum >= currNumFixed))
- {
- sourceNum += (currNumFixed - prevNumFixed);
- Config.setConfigInt(Config.KEY_MAPSOURCE_INDEX, sourceNum);
- }
- Config.setConfigInt(Config.KEY_NUM_FIXED_MAPS, currNumFixed);
+ _mapSource = inMapSource;
+ if (_mapSource == null) {_mapSource = MapSourceLibrary.getSource(0);}
+ clearMemoryCaches();
+ _numLayers = _mapSource.getNumLayers();
}
/**
@@ -131,22 +141,29 @@ public class MapTileManager implements ImageObserver
}
/**
+ * Get a tile from the currently selected map source
* @param inLayer layer number, starting from 0
* @param inX x index of tile
* @param inY y index of tile
+ * @param inDownloadIfNecessary true to download the file if it's not available
* @return selected tile if already loaded, or null otherwise
*/
- public Image getTile(int inLayer, int inX, int inY)
+ public Image getTile(int inLayer, int inX, int inY, boolean inDownloadIfNecessary)
{
if (inY < 0 || inY >= _numTileIndices) return null;
// Wrap tile indices which are too big or too small
inX = ((inX % _numTileIndices) + _numTileIndices) % _numTileIndices;
// Check first in memory cache for tile
- MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
- Image tile = tempCache.getTile(inX, inY);
- if (tile != null) {
- return tile;
+ Image tile = null;
+ MemTileCacher tempCache = null;
+ if (_tempCaches != null)
+ {
+ tempCache = _tempCaches[inLayer]; // Should probably guard array indexes here
+ tile = tempCache.getTile(inX, inY);
+ if (tile != null) {
+ return tile;
+ }
}
// Tile wasn't in memory, but maybe it's in disk cache (if there is one)
@@ -158,18 +175,23 @@ public class MapTileManager implements ImageObserver
tile = DiskTileCacher.getTile(diskCachePath, _mapSource.makeFilePath(inLayer, _zoom, inX, inY), onlineMode);
if (tile != null)
{
+ if (_returnIncompleteImages) {return tile;}
// Pass tile to memory cache
- tempCache.setTile(tile, inX, inY, _zoom);
+ if (tempCache != null) {
+ tempCache.setTile(tile, inX, inY, _zoom);
+ }
if (tile.getWidth(this) > 0) {return tile;}
return null;
}
+ // else System.out.println("DTC gave null tile for " + _zoom + ", " + inX + ", " + inY);
}
// Tile wasn't in memory or on disk, so if online let's get it
- if (onlineMode)
+ if (onlineMode && _downloadTiles && inDownloadIfNecessary)
{
try
{
URL tileUrl = new URL(_mapSource.makeURL(inLayer, _zoom, inX, inY));
+ // System.out.println("Going to fetch: " + tileUrl);
if (useDisk && DiskTileCacher.saveTile(tileUrl, diskCachePath,
_mapSource.makeFilePath(inLayer, _zoom, inX, inY), this))
{
@@ -178,7 +200,6 @@ public class MapTileManager implements ImageObserver
else
{
// Load image asynchronously, using observer
- // tile = Toolkit.getDefaultToolkit().createImage(tileUrl);
// In order to set the http user agent, need to use a TileDownloader instead
TileDownloader.triggerLoad(this, tileUrl, inLayer, inX, inY, _zoom);
}
@@ -203,7 +224,7 @@ public class MapTileManager implements ImageObserver
boolean loaded = (infoflags & ImageObserver.ALLBITS) > 0;
boolean error = (infoflags & ImageObserver.ERROR) > 0;
if (loaded || error) {
- _parent.tilesUpdated(loaded);
+ _consumer.tilesUpdated(loaded);
}
return !loaded;
}
@@ -218,7 +239,7 @@ public class MapTileManager implements ImageObserver
*/
public void notifyImageLoaded(Image inTile, int inLayer, int inX, int inY, int inZoom)
{
- if (inTile != null)
+ if (inTile != null && _tempCaches != null)
{
MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
if (tempCache.getTile(inX, inY) == null)
@@ -228,5 +249,8 @@ public class MapTileManager implements ImageObserver
inTile.getWidth(this); // trigger imageUpdate when image is ready
}
}
+ else if (inTile != null) {
+ inTile.getWidth(this);
+ }
}
}
diff --git a/tim/prune/gui/map/TileConsumer.java b/tim/prune/gui/map/TileConsumer.java
new file mode 100644
index 0000000..35a557e
--- /dev/null
+++ b/tim/prune/gui/map/TileConsumer.java
@@ -0,0 +1,10 @@
+package tim.prune.gui.map;
+
+/**
+ * Interface used by the MapTileManager to communicate back to its consumers
+ */
+public interface TileConsumer
+{
+ /** Let the consumer know that the tiles have been updated */
+ public void tilesUpdated(boolean inIsOk);
+}
diff --git a/tim/prune/gui/map/TileDownloader.java b/tim/prune/gui/map/TileDownloader.java
index df820fc..7c75400 100644
--- a/tim/prune/gui/map/TileDownloader.java
+++ b/tim/prune/gui/map/TileDownloader.java
@@ -60,8 +60,10 @@ public class TileDownloader implements Runnable
if (inManager != null && inUrl != null)
{
String url = inUrl.toString();
+ // System.out.println("Trigger load: " + url);
if (!BLOCKED_URLS.contains(url) && !LOADING_URLS.contains(url))
{
+ // System.out.println("Not blocked: " + url);
LOADING_URLS.add(url);
new Thread(new TileDownloader(inManager, inUrl, inLayer, inX, inY, inZoom)).start();
}
@@ -76,6 +78,7 @@ public class TileDownloader implements Runnable
InputStream in = null;
try
{
+ // System.out.println("TD Running thread to get: " + _url.toString());
// Set http user agent on connection
URLConnection conn = _url.openConnection();
conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
diff --git a/tim/prune/jpeg/ExifGateway.java b/tim/prune/jpeg/ExifGateway.java
index 9fefadf..c11ba2e 100644
--- a/tim/prune/jpeg/ExifGateway.java
+++ b/tim/prune/jpeg/ExifGateway.java
@@ -47,7 +47,10 @@ public abstract class ExifGateway
return data;
}
}
- catch (LinkageError nolib) {}
+ catch (LinkageError nolib) {
+ System.err.println("Link: " + nolib.getMessage());
+ nolib.printStackTrace();
+ }
// Not successful - warn if necessary
if (!_exifFailWarned)
{
@@ -69,7 +72,6 @@ public abstract class ExifGateway
}
-
/**
* @param inNumerator numerator from Rational
* @param inDenominator denominator from Rational
@@ -88,4 +90,19 @@ public abstract class ExifGateway
if (inDenominator < 0) denomDbl += correction;
return numeratorDbl / denomDbl;
}
+
+
+ /**
+ * @param inNumerator numerator from Rational
+ * @param inDenominator denominator from Rational
+ * @return the value of the specified number as a positive <code>double</code>.
+ * Forces a positive answer
+ */
+ public static final double convertToPositiveValue(long inNumerator, long inDenominator)
+ {
+ if (inDenominator == 0L) return 0.0;
+ final double numeratorDbl = inNumerator;
+ final double denomDbl = inDenominator;
+ return numeratorDbl / denomDbl;
+ }
}
diff --git a/tim/prune/jpeg/ExternalExifLibrary.java b/tim/prune/jpeg/ExternalExifLibrary.java
index 0d94f03..496646e 100644
--- a/tim/prune/jpeg/ExternalExifLibrary.java
+++ b/tim/prune/jpeg/ExternalExifLibrary.java
@@ -2,12 +2,14 @@ package tim.prune.jpeg;
import java.io.File;
+import com.drew.imaging.ImageMetadataReader;
import com.drew.lang.Rational;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
-import com.drew.metadata.MetadataException;
-import com.drew.metadata.exif.ExifDirectory;
+import com.drew.metadata.exif.ExifSubIFDDirectory;
+import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.exif.ExifReader;
+import com.drew.metadata.exif.ExifThumbnailDirectory;
import com.drew.metadata.exif.GpsDirectory;
/**
@@ -31,8 +33,7 @@ public class ExternalExifLibrary implements ExifLibrary
// Read exif data from picture
try
{
- Metadata metadata = new Metadata();
- new ExifReader(inFile).extract(metadata);
+ Metadata metadata = ImageMetadataReader.readMetadata(inFile);
if (metadata.containsDirectory(GpsDirectory.class))
{
Directory gpsdir = metadata.getDirectory(GpsDirectory.class);
@@ -61,22 +62,18 @@ public class ExternalExifLibrary implements ExifLibrary
data.setAltitudeRef(altRef);
}
- try
+ // Timestamp and datestamp (if present)
+ final int TAG_GPS_DATESTAMP = 0x001d;
+ if (gpsdir.containsTag(GpsDirectory.TAG_GPS_TIME_STAMP) && gpsdir.containsTag(TAG_GPS_DATESTAMP))
{
- // Timestamp and datestamp (if present)
- final int TAG_GPS_DATESTAMP = 0x001d;
- if (gpsdir.containsTag(GpsDirectory.TAG_GPS_TIME_STAMP) && gpsdir.containsTag(TAG_GPS_DATESTAMP))
- {
- Rational[] times = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_TIME_STAMP);
- data.setGpsTimestamp(new int[] {times[0].intValue(), times[1].intValue(),
- times[2].intValue()});
- Rational[] dates = gpsdir.getRationalArray(TAG_GPS_DATESTAMP);
- if (dates != null) {
- data.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()});
- }
+ Rational[] times = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_TIME_STAMP);
+ data.setGpsTimestamp(new int[] {times[0].intValue(), times[1].intValue(),
+ times[2].intValue()});
+ Rational[] dates = gpsdir.getRationalArray(TAG_GPS_DATESTAMP);
+ if (dates != null) {
+ data.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()});
}
}
- catch (MetadataException me) {} // ignore, use other tags instead
// Image bearing (if present)
if (gpsdir.containsTag(GpsDirectory.TAG_GPS_IMG_DIRECTION) && gpsdir.containsTag(GpsDirectory.TAG_GPS_IMG_DIRECTION_REF))
@@ -89,30 +86,39 @@ public class ExternalExifLibrary implements ExifLibrary
}
// Tags from Exif directory
- if (metadata.containsDirectory(ExifDirectory.class))
+ if (metadata.containsDirectory(ExifSubIFDDirectory.class))
{
- Directory exifdir = metadata.getDirectory(ExifDirectory.class);
+ Directory exifdir = metadata.getDirectory(ExifSubIFDDirectory.class);
// Take time and date from exif tags
- if (exifdir.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) {
- data.setOriginalTimestamp(exifdir.getString(ExifDirectory.TAG_DATETIME_ORIGINAL));
+ if (exifdir.containsTag(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL)) {
+ data.setOriginalTimestamp(exifdir.getString(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL));
}
// Also take "digitized" timestamp
- if (exifdir.containsTag(ExifDirectory.TAG_DATETIME_DIGITIZED)) {
- data.setDigitizedTimestamp(exifdir.getString(ExifDirectory.TAG_DATETIME_DIGITIZED));
+ if (exifdir.containsTag(ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED)) {
+ data.setDigitizedTimestamp(exifdir.getString(ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED));
}
+ }
+ if (metadata.containsDirectory(ExifIFD0Directory.class))
+ {
+ Directory exifdir = metadata.getDirectory(ExifIFD0Directory.class);
// Photo rotation code
- if (exifdir.containsTag(ExifDirectory.TAG_ORIENTATION)) {
- data.setOrientationCode(exifdir.getInt(ExifDirectory.TAG_ORIENTATION));
+ if (exifdir.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
+ data.setOrientationCode(exifdir.getInt(ExifIFD0Directory.TAG_ORIENTATION));
// NOTE: this presumably takes the _last_ orientation value found, not the first.
}
+ }
+
+ if (metadata.containsDirectory(ExifThumbnailDirectory.class))
+ {
+ ExifThumbnailDirectory exifdir = metadata.getDirectory(ExifThumbnailDirectory.class);
- // Thumbnail
- if (exifdir.containsTag(ExifDirectory.TAG_THUMBNAIL_DATA))
+ // TODO: Check this thumbnail stuff
+ if (exifdir.hasThumbnailData())
{
- // Make a copy of the byte data rather than keeping a reference to extracted array
- byte[] tdata = exifdir.getByteArray(ExifDirectory.TAG_THUMBNAIL_DATA);
+ // Make a copy of the byte data
+ byte[] tdata = exifdir.getThumbnailData();
byte[] thumb = new byte[tdata.length];
System.arraycopy(tdata, 0, thumb, 0, tdata.length);
data.setThumbnailImage(thumb);
diff --git a/tim/prune/lang/prune-texts_af.properties b/tim/prune/lang/prune-texts_af.properties
index cc8f6b5..f208cf0 100644
--- a/tim/prune/lang/prune-texts_af.properties
+++ b/tim/prune/lang/prune-texts_af.properties
@@ -29,7 +29,7 @@ menu.point=Punt
menu.point.editpoint=Redigeer Punt
menu.point.deletepoint=Punt Uitvee
menu.photo=Foto
-menu.photo.saveexif=Stoor na EXIF
+menu.photo.saveexif=Stoor na Exif
menu.audio=Audio
menu.view=Kyk
menu.view.showsidebars=Wys kantstawe
@@ -109,7 +109,7 @@ function.correlatephotos=Korreleer Fotos
function.rearrangephotos=Herrangskik fotos
function.rotatephotoleft=Roteer foto links
function.rotatephotoright=Roteer foto regs
-function.ignoreexifthumb=Ignoreer EXIF thumbnail
+function.ignoreexifthumb=Ignoreer Exif thumbnail
function.help=Hulp
function.showkeys=Wys Kortpad sleutels
function.about=Omtrent GpsPrune
@@ -254,7 +254,8 @@ dialog.correlate.photoselect.intro=Selekteer een van die gekorreleerde fotos om
dialog.correlate.select.photoname=Foto naam
dialog.correlate.select.timediff=Tyd verskil
dialog.correlate.select.photolater=Foto later
-dialog.correlate.options.tip=Wenk: Deur een item te verbind, kan die tyd afset bereken word vir jou.
+tip.title=Wenk
+tip.manuallycorrelateone=Deur een item te verbind, kan die tyd afset bereken word vir jou.
dialog.correlate.options.intro=Seleketeer die opsies vir automatiese korrelasie
dialog.correlate.options.offsetpanel=Tyd afset
dialog.correlate.options.offset=Afset
@@ -291,7 +292,7 @@ dialog.deletemarked.nonefound=Geen data punt kon verwyder word
dialog.pastecoordinates.desc=Sleutel of plak die koordinate hier
dialog.pastecoordinates.coords=Koordinate
dialog.pastecoordinates.nothingfound=Gaan asseblief koordinate na en probeer weer
-dialog.help.help=Sien asseblief\n http://activityworkshop.net/software/gpsprune/\n vir meer inligting en gebruikers handleidings.
+dialog.help.help=Sien asseblief\n http://gpsprune.activityworkshop.net/\n vir meer inligting en gebruikers handleidings.
dialog.about.version=Weergawe
dialog.about.build=Bou
dialog.about.summarytext1=GpsPrune is 'n program vir die laai, vertoon en wysiging van data vanaf GPS ontvangers.
@@ -334,7 +335,6 @@ fieldname.newsegment=Segment
fieldname.custom=Persoonlike
fieldname.prefix=Veld
fieldname.distance=Afstand
-fieldname.movingdistance=Beweeg afstand
fieldname.duration=Tydperk
fieldname.speed=Spoed
fieldname.verticalspeed=Vertikale spoed
@@ -394,9 +394,8 @@ error.save.failed=Stoor van data na l\u00eaer het misluk
error.saveexif.filenotfound=Find van foto het misluk
error.saveexif.cannotoverwrite1=Foto leer
error.saveexif.cannotoverwrite2=is lees-alleen en kan nie oorskruif word nie. Skyf na kopie?
-error.saveexif.failed1=Stoor het misluk
-error.saveexif.failed2=van die beelde
-error.saveexif.forced2=van die beelde vereis vorsering
+error.saveexif.failed=Stoor het misluk %d van die beelde
+error.saveexif.forced=%d van die beelde vereis vorsering
error.load.dialogtitle=Fout met laai van data
error.load.noread=Kan nie leer lees
error.load.nopoints=Geen koordinaat informasie gevind in the leer
diff --git a/tim/prune/lang/prune-texts_cz.properties b/tim/prune/lang/prune-texts_cz.properties
index 931a00d..4d7a661 100644
--- a/tim/prune/lang/prune-texts_cz.properties
+++ b/tim/prune/lang/prune-texts_cz.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=P\u0159idat fotografie
menu.file.recentfiles=Naposledy otev\u0159en\u00e9
menu.file.save=Ulo\u017eit jako text
menu.file.exit=Konec
+menu.online=Online
menu.track=Stopa
menu.track.undo=Undo
menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo
@@ -57,6 +58,7 @@ menu.map.editmode=Edita\u010dn\u00ed m\u00f3d
# Alt keys for menus
altkey.menu.file=S
+altkey.menu.online=O
altkey.menu.track=T
altkey.menu.range=R
altkey.menu.point=B
@@ -104,9 +106,12 @@ function.estimatetime=Odhad \u010dasu
function.learnestimationparams=Anal\u00fdza stopy pro odhad \u010dasu
function.setmapbg=Nastavit pozad\u00ed
function.setpaths=Nastavit cestu k program\u016fm
+function.splitsegments=Rozd\u011blit stopu na \u010d\u00e1sti
+function.sewsegments=Spojit \u010d\u00e1sti stopy
function.getgpsies=St\u00e1hnout stopy z Gpsies
function.uploadgpsies=Nahr\u00e1t stopu na Gpsies
function.lookupsrtm=Na\u010d\u00edst nadm. v\u00fd\u0161ku ze SRTM
+function.downloadsrtm=St\u00e1hnout dla\u017edice ze SRTM
function.getwikipedia=Hledat na Wikipedii podle vzd\u00e1lenosti
function.searchwikipedianames=Hledat na Wikipedii podle jm\u00e9na
function.downloadosm=St\u00e1hnout data OSM pro oblast
@@ -135,6 +140,7 @@ function.checkversion=Zkontrolovat existenci nov\u00e9 verze
function.saveconfig=Ulo\u017eit nastaven\u00ed
function.diskcache=Ulo\u017eit mapy na disk
function.managetilecache=Upravit cache map
+function.getweatherforecast=St\u00e1hnout p\u0159edpov\u011b\u010f po\u010das\u00ed
# Dialogs
dialog.exit.confirm.title=Ukon\u010dit GpsPrune
@@ -242,6 +248,8 @@ dialog.exportpov.modelstyle=Model
dialog.exportpov.ballsandsticks=Koule a ty\u010dky
dialog.exportpov.tubesandwalls=Roury a st\u011bny
dialog.3d.warningtracksize=Tato stopa sest\u00e1v\u00e1 z mnoha bod\u016f, kter\u00e9 mo\u017en\u00e1 Java3D nebude um\u011bt zobrazit.\nOpravdu chcete pokra\u010dovat?
+dialog.3d.useterrain=Zobrazit ter\u00e9n
+dialog.3d.terraingridsize=Rozli\u0161en\u00ed ter\u00e9nu
dialog.exportpov.baseimage=Obr\u00e1zek jako podklad
dialog.exportpov.cannotmakebaseimage=Nelze zapsat podklad
dialog.baseimage.title=Podklad
@@ -256,7 +264,8 @@ 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.exportimage.noimagepossible=Aby bylo mo\u017en\u00e9 mapu ulo\u017eit jako obr\u00e1zek, je t\u0159eba st\u00e1hnout a ulo\u017eit dla\u017edice
-dialog.exportimage.drawtrack=Nakreslit stopu na mapu
+dialog.exportimage.drawtrack=Vykreslit linii stopy
+dialog.exportimage.drawtrackpoints=Vykreslit body stopy
dialog.exportimage.textscalepercent=Zv\u011bt\u0161en\u00ed fontu (%)
dialog.pointtype.desc=Ulo\u017eit body n\u00e1sleduj\u00edc\u00edch typ\u016f:
dialog.pointtype.track=Body stopy
@@ -378,7 +387,6 @@ dialog.correlate.photoselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fd
dialog.correlate.select.photoname=N\u00e1zev fotografie
dialog.correlate.select.timediff=\u010casov\u00fd rozd\u00edl
dialog.correlate.select.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
@@ -421,8 +429,7 @@ dialog.compress.duplicates.title=Odstran\u011bn\u00ed zdvojen\u00fdch bod\u016f
dialog.compress.douglaspeucker.title=Douglasova-Peuckerova komprese
dialog.compress.douglaspeucker.paramdesc=Povolen\u00e1 odchylka
dialog.compress.summarylabel=Bod\u016f ke smaz\u00e1n\u00ed
-dialog.compress.confirm1=Bylo ozna\u010deno celkem
-dialog.compress.confirm2=bod\u016f.\nBody je mo\u017en\u00e9 smazat volbou Stopa->Smazat ozna\u010den\u00e9 body
+dialog.compress.confirm=Bylo ozna\u010deno celkem %d bod\u016f.\nStopa->Smazat ozna\u010den\u00e9 body?
dialog.compress.confirmnone=Nebyly vybr\u00e1ny \u017e\u00e1dn\u00e9 body.
dialog.deletemarked.nonefound=Nemohou b\u00fdt odstran\u011bny \u017e\u00e1dn\u00e9 body stopy
dialog.pastecoordinates.desc=Zadejte sou\u0159adnice
@@ -467,7 +474,7 @@ dialog.checkversion.newversion1=Nov\u00e1 verze GpsPrune je u\u017e dostupn\u00e
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/gpsprune/download.html.
+dialog.checkversion.download=Novou verzi m\u016f\u017eete st\u00e1hnout na adrese http://gpsprune.activityworkshop.net/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 stopy</td></tr><tr><td>Ctrl + Home, End</td><td>Vybrat p [...]
dialog.keys.normalmodifier=Ctrl
@@ -530,13 +537,31 @@ dialog.diskcache.tileset.multiple=v\u00edce sad
dialog.diskcache.deleteold=Smazat star\u00e9 soubory
dialog.diskcache.maximumage=Ponechat maxim\u00e1ln\u011b dn\u00ed
dialog.diskcache.deleteall=Smazat v\u0161echny soubory
-dialog.diskcache.deleted1=Smaz\u00e1no
-dialog.diskcache.deleted2=soubor\u016f z cache
+dialog.diskcache.deleted=Smaz\u00e1no %d soubor\u016f z cache
dialog.deletefieldvalues.intro=Vyberte pole, kter\u00e9 se m\u00e1 z aktu\u00e1ln\u00edho rozmez\u00ed odstranit
dialog.deletefieldvalues.nofields=V tomto rozmez\u00ed nelze smazat \u017e\u00e1dn\u00e9 pole
dialog.setlinewidth.text=Zvolte tlou\u0161\u0165ku \u010d\u00e1ry, kterou se nakresl\u00ed stopa (1-4)
dialog.downloadosm.desc=Potvr\u010fte, \u017ee se maj\u00ed k dan\u00e9 oblasti st\u00e1hnout data OSM:
dialog.searchwikipedianames.search=Vyhledat:
+dialog.weather.location=M\u00edsto
+dialog.weather.update=Posledn\u00ed aktualizace
+dialog.weather.sunrise=V\u00fdchod slunce
+dialog.weather.sunset=Z\u00e1pad slunce
+dialog.weather.temperatureunits=Stupn\u011b
+dialog.weather.currentforecast=Aktu\u00e1ln\u00ed po\u010das\u00ed
+dialog.weather.dailyforecast=P\u0159edpov\u011b\u010f co den
+dialog.weather.3hourlyforecast=P\u0159edpov\u011b\u010f co t\u0159i hodiny
+dialog.weather.day.now=Te\u010f
+dialog.weather.day.today=Dnes
+dialog.weather.day.tomorrow=Z\u00edtra
+dialog.weather.day.monday=Pond\u011bl\u00ed
+dialog.weather.day.tuesday=\u00dater\u00fd
+dialog.weather.day.wednesday=St\u0159eda
+dialog.weather.day.thursday=\u010ctvrtek
+dialog.weather.day.friday=P\u00e1tek
+dialog.weather.day.saturday=Sobota
+dialog.weather.day.sunday=Ned\u011ble
+dialog.weather.creditnotice=Data poskytuje slu\u017eba openweathermap.org, v\u00edce informac\u00ed na t\u00e9to adrese.
# 3d window
dialog.3d.title=Trojrozm\u011brn\u00e9 zobrazen\u00ed GpsPrune
@@ -555,11 +580,12 @@ 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.splitsegments=\u00dasp\u011b\u0161n\u011b rozd\u011bleno v %d bodech
+confirm.sewsegments=\u00dasp\u011b\u0161n\u011b spojeno v %d bodech
confirm.cutandmove=V\u00fdb\u011br p\u0159esunut
confirm.interpolate=Body p\u0159id\u00e1ny
confirm.convertnamestotimes=N\u00e1zvy bod\u016f p\u0159evedeny
-confirm.saveexif.ok1=Ulo\u017eeno
-confirm.saveexif.ok2=fotografi\u00ed
+confirm.saveexif.ok=Ulo\u017eeno %d fotografi\u00ed
confirm.undo.single=operace vr\u00e1cena
confirm.undo.multi=operac\u00ed vr\u00e1ceno
confirm.jpegload.single=fotografie p\u0159id\u00e1na
@@ -573,13 +599,23 @@ confirm.correlatephotos.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.lookupsrtm=Nalezeno %d v\u00fd\u0161kov\u00fdch hodnot
+confirm.downloadsrtm=Do cache bylo sta\u017eeno %d soubor\u016f
+confirm.downloadsrtm.1=Do cache bylo sta\u017eeno %d soubor\u016f
+confirm.downloadsrtm.none=\u017d\u00e1dn\u00fd soubor nebylo t\u0159eba stahovat, v\u0161e u\u017e je v cachi.
confirm.deletefieldvalues=Hodnoty pole smaz\u00e1ny
confirm.audioload=Audionahr\u00e1vky p\u0159id\u00e1ny
confirm.correlateaudios.single=Audionahr\u00e1vka slad\u011bna
confirm.correlateaudios.multi=Audionahr\u00e1vky slad\u011bny
+# Tips, shown just once when appropriate
+tip.title=Tip
+tip.useamapcache=Kdy\u017e nastav\u00edte odkl\u00e1dac\u00ed prostor na disku \u010dili cache (Nastaven\u00ed -> Ulo\u017eit mapy na disk),\nzrychl\u00ed se zobrazov\u00e1n\u00ed a zmen\u0161\u00ed se mno\u017estv\u00ed p\u0159en\u00e1\u0161en\u00fdch dat.
+tip.learntimeparams=V\u00fdsledky budou p\u0159esn\u011bj\u0161\u00ed, kdy\u017e na na\u010dten\u00e9 stopy pou\u017eijete funkci\nAnal\u00fdza stopy pro odhad \u010dasu.
+tip.downloadsrtm=Na\u010d\u00edt\u00e1n\u00ed nadmo\u0159sk\u00fdch v\u00fd\u0161ek bude rychlej\u0161\u00ed, pokud st\u00e1hnete dla\u017edice do cache pomoc\u00ed\nOnline -> St\u00e1hnout dla\u017edice ze SRTM.
+tip.usesrtmfor3d=Tato stopa neobsahuje informaci o nadmo\u0159sk\u00e9 v\u00fd\u0161ce.\nPro na\u010dten\u00ed p\u0159ibli\u017en\u00fdch nadmo\u0159sk\u00fdch v\u00fd\u0161ek pot\u0159ebn\u00fdch\nna trojrozm\u011brn\u00e9 zobrazen\u00ed m\u016f\u017eete pou\u017e\u00edt funkce SRTM.
+tip.manuallycorrelateone=Kdy\u017e ru\u010dn\u011b slad\u00edte aspo\u0148 jednu fotografii, \u010dasov\u00fd posun bude vypo\u010d\u00edtat za v\u00e1s.
+
# Buttons
button.ok=OK
button.back=Zp\u011bt
@@ -597,6 +633,7 @@ button.yes=Ano
button.no=Ne
button.yestoall=Ano u v\u0161eho
button.notoall=Ne u v\u0161eho
+button.always=V\u017edy
button.select=Vybrat
button.selectall=Vybrat v\u0161e
button.selectnone=Zru\u0161it v\u00fdb\u011br
@@ -685,7 +722,6 @@ 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
@@ -720,6 +756,10 @@ units.degminsec=Deg-min-sec
units.degmin=Deg-min
units.deg=Stupn\u011b
units.iso8601=ISO 8601
+units.degreescelsius=Celsia
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheita
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=a
@@ -728,6 +768,7 @@ logic.or=nebo
# External urls
url.googlemaps=maps.google.cz
wikipedia.lang=cs
+openweathermap.lang=en
# Cardinals for 3d plots
cardinal.n=N
@@ -749,6 +790,8 @@ undo.deletemarked=zkomprimovat stopu
undo.insert=vlo\u017eit body
undo.reverse=obr\u00e1tit rozmez\u00ed
undo.mergetracksegments=slou\u010dit \u010d\u00e1sti stopy
+undo.splitsegments=rozd\u011blit stopu na \u010d\u00e1sti
+undo.sewsegments=spojit \u010d\u00e1sti stopy
undo.addtimeoffset=p\u0159idat \u010dasov\u00fd posun
undo.addaltitudeoffset=p\u0159idat v\u00fd\u0161kov\u00fd posun
undo.rearrangewaypoints=p\u0159euspo\u0159\u00e1dat body
@@ -771,10 +814,8 @@ 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.saveexif.failed=Nepoda\u0159ilo se ulo\u017eit %d fotografi\u00ed
+error.saveexif.forced=P\u0159i ukl\u00e1d\u00e1n\u00ed %d 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
@@ -785,7 +826,7 @@ error.jpegload.dialogtitle=Chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed fotografi
error.jpegload.nofilesfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory
error.jpegload.nojpegsfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory jpeg
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.jpegload.exifreadfailed=Nepoda\u0159ilo se na\u010d\u00edst informaci Exif. Tu nelze na\u010d\u00edst\nbez intern\u00ed nebo extern\u00ed knihovny.
error.audioload.nofilesfound=Nebyly nalezeny \u017e\u00e1dn\u00e9 zvukov\u00e9 soubory.
error.gpsload.unknown=Nezn\u00e1m\u00e1 chyba
error.undofailed.title=Selhalo undo
@@ -811,3 +852,6 @@ error.cache.empty=Adres\u00e1\u0159 s cache map je pr\u00e1zdn\u00fd.
error.cache.cannotdelete=Nelze smazat soubory map.
error.interpolate.invalidparameter=Po\u010det bod\u016f mus\u00ed b\u00fdt mezi 1 a 1000
error.learnestimationparams.failed=Na z\u00e1klad\u011b t\u00e9to stopy nelze vypo\u010d\u00edtat parametr.\nZkuste jin\u00e9 stopy.
+error.tracksplit.nosplit=Stopu nen\u00ed mo\u017en\u00e9 rozd\u011blit
+error.downloadsrtm.nocache=Nepoda\u0159ilo se ulo\u017eit soubory.\nPros\u00edm ov\u011b\u0159te diskovou cache.
+error.sewsegments.nothingdone=Nelze spojit \u010d\u00e1sti stopy dohromady.\nStopa se skl\u00e1d\u00e1 z %d \u010d\u00e1st\u00ed.
diff --git a/tim/prune/lang/prune-texts_da.properties b/tim/prune/lang/prune-texts_da.properties
new file mode 100644
index 0000000..ea394b9
--- /dev/null
+++ b/tim/prune/lang/prune-texts_da.properties
@@ -0,0 +1,84 @@
+# Text entries for the GpsPrune application
+# Danish translations
+
+# Menu entries
+menu.file=Fil
+menu.file.addphotos=Tilf\u00f8j billeder
+menu.file.recentfiles=Seneste filer
+menu.file.save=Gem som tekst
+menu.file.exit=Afslut
+menu.track.undo=Fortryd
+menu.track.clearundo=Nulstil fortrydelsesliste
+menu.track.deletemarked=Slet markerede punkter
+menu.track.rearrange=Omorganis\u00e9r waypoints
+menu.track.rearrange.nearest=Hvert waypoint til n\u00e6rmeste nabo
+menu.range=Omr\u00e5de
+menu.range.all=V\u00e6lg alle
+menu.range.none=Ingen valgt
+menu.range.start=V\u00e6lg omr\u00e5des startpunkt
+menu.range.end=V\u00e6lg omr\u00e5des slutpunkt
+menu.range.average=Dan gennemsnit af valgte omr\u00e5de
+menu.range.reverse=Dan omvendt r\u00e6kkef\u00f8lge
+menu.range.mergetracksegments=Sammensmelt sporsegmenter
+menu.range.cutandmove=Afsk\u00e6r og flyt valgte omr\u00e5de
+menu.point=Punkt
+menu.point.editpoint=Redig\u00e8r punkt
+menu.point.deletepoint=Fjern punkt
+menu.photo=Foto
+menu.photo.saveexif=Gem Exif-data
+menu.audio=Lyd
+menu.view=Udseende
+menu.view.showsidebars=Vis sidepanel
+menu.view.browser=Kort i browser
+menu.settings=Indstillinger
+menu.settings.onlinemode=Hent kort fra Internettet
+menu.settings.autosave=Gem indstillinger automatisk ved aflutning
+menu.help=Hj\u00e6lp
+# Popup menu for map
+menu.map.zoomin=Zoom ind
+menu.map.zoomout=Zoom ud
+menu.map.newpoint=Skab nyt punkt
+menu.map.drawpoints=Skab serie af punkter
+menu.map.connect=Saml punkter p\u00e5 linie
+menu.map.autopan=Autocentrering
+menu.map.showmap=Vis kort
+menu.map.showscalebar=Vis m\u00e5lestok
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=S
+altkey.menu.range=O
+altkey.menu.point=P
+altkey.menu.view=U
+altkey.menu.photo=T
+altkey.menu.audio=L
+altkey.menu.settings=I
+altkey.menu.help=H
+
+# Functions
+function.open=\u00c5bn fil
+function.importwithgpsbabel=Import\u00e9r fil med GPSBabel
+function.loadfromgps=Hent data fra GPS
+function.sendtogps=Overf\u00f8r data til GPS
+function.exportkml=Eksport\u00e9r KML
+function.exportgpx=Eksport\u00e9r GPX
+function.exportpov=Eksport\u00e9r POV
+function.exportsvg=Eksport\u00e9r SVG
+function.editwaypointname=Ret waypoint-navn
+function.compress=Komprim\u00e9r spor
+function.deleterange=Fjern det valgte omr\u00e5de
+function.croptrack=Afgr\u00e6ns spor
+function.interpolate=Interpol\u00e9r
+function.addtimeoffset=Tilf\u00f8j offset p\u00e5 tiden
+function.addaltitudeoffset=Tilf\u00f8j offset p\u00e5 h\u00f8jde
+function.convertnamestotimes=Ret waypoint-navne til tidspunkter
+function.deletefieldvalues=Fjern feltv\u00e6rdier
+function.findwaypoint=Find waypoint
+function.pastecoordinates=Indf\u00f8j nye koordinater
+function.charts=Kort
+function.show3d=3-D view
+function.distances=Afstande
+function.fullrangedetails=Vis alle detaljer
+function.setmapbg=V\u00e6lg kort som baggrund
+function.setpaths=V\u00e6lg sti til programmer
+function.getgpsies=Se liste af GPS-spor
diff --git a/tim/prune/lang/prune-texts_de.properties b/tim/prune/lang/prune-texts_de.properties
index deb44ed..021a5f0 100644
--- a/tim/prune/lang/prune-texts_de.properties
+++ b/tim/prune/lang/prune-texts_de.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=Fotos laden
menu.file.recentfiles=Zuletzt verwendete Dateien
menu.file.save=Als Text speichern
menu.file.exit=Beenden
+menu.online=Online
menu.track=Track
menu.track.undo=R\u00fcckg\u00e4ngig
menu.track.clearundo=Liste der letzten \u00c4nderungen l\u00f6schen
@@ -57,6 +58,7 @@ menu.map.editmode=Punkte verschieben
# Alt keys for menus
altkey.menu.file=D
+altkey.menu.online=O
altkey.menu.track=T
altkey.menu.range=B
altkey.menu.point=P
@@ -104,9 +106,12 @@ function.estimatetime=Zeit absch\u00e4tzen
function.learnestimationparams=Zeitparameter erlernen
function.setmapbg=Karte Hintergrund setzen
function.setpaths=Programmpfade setzen
+function.splitsegments=In Trackabschnitte schneiden
+function.sewsegments=Trackabschnitte zusammenf\u00fcgen
function.getgpsies=Tracks bei GPSies.com herunterladen
function.uploadgpsies=Track zu GPSies.com hochladen
-function.lookupsrtm=H\u00f6hendaten von SRTM herunterladen
+function.lookupsrtm=H\u00f6hendaten von SRTM nachschlagen
+function.downloadsrtm=SRTM Dateien herunterladen
function.getwikipedia=Wikipediaartikel in der N\u00e4he nachschlagen
function.searchwikipedianames=Wikipedia mit Name durchsuchen
function.downloadosm=OSM-Daten f\u00fcr dieses Gebiet herunterladen
@@ -135,6 +140,7 @@ function.checkversion=Nach neuen Versionen suchen
function.saveconfig=Einstellungen speichern
function.diskcache=Karten auf Festplatte speichern
function.managetilecache=Kartenkacheln verwalten
+function.getweatherforecast=Wettervorhersage herunterladen
# Dialogs
dialog.exit.confirm.title=GpsPrune beenden
@@ -242,6 +248,8 @@ dialog.exportpov.modelstyle=Modellstil
dialog.exportpov.ballsandsticks=B\u00e4lle und Stangen
dialog.exportpov.tubesandwalls=R\u00f6hren und W\u00e4nde
dialog.3d.warningtracksize=Dieser Track hat sehr viele Punkte, die Java3D vielleicht nicht bearbeiten kann.\nM\u00f6chten Sie den Vorgang trotzdem fortsetzen?
+dialog.3d.useterrain=Gel\u00e4nde anzeigen
+dialog.3d.terraingridsize=Gittergr\u00f6\u00dfe
dialog.exportpov.baseimage=Grundbild
dialog.exportpov.cannotmakebaseimage=Bild kann nicht gespeichert werden
dialog.baseimage.title=Kartenbild
@@ -257,6 +265,7 @@ dialog.exportsvg.theta=Neigungswinkel \u03b8
dialog.exportsvg.gradients=Farbverl\u00e4ufe verwenden
dialog.exportimage.noimagepossible=Kartenbilder m\u00fcssen schon gespeichert werden bevor sie in einem Export verwendet werden k\u00f6nnen
dialog.exportimage.drawtrack=Track auf der Karte zeichnen
+dialog.exportimage.drawtrackpoints=Trackpunkte zeichnen
dialog.exportimage.textscalepercent=Text Skalierung (%)
dialog.pointtype.desc=Folgende Punkttypen speichern:
dialog.pointtype.track=Trackpunkte
@@ -378,7 +387,6 @@ dialog.correlate.photoselect.intro=W\u00e4hlen Sie eines dieser Fotos aus, um di
dialog.correlate.select.photoname=Bezeichnung des Fotos
dialog.correlate.select.timediff=Zeitdifferenz
dialog.correlate.select.photolater=Foto sp\u00e4ter
-dialog.correlate.options.tip=Tipp: Mit mindestens einem manuell verbundenen Element 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
@@ -421,14 +429,13 @@ dialog.compress.duplicates.title=Duplikate entfernen
dialog.compress.douglaspeucker.title=Douglas-Peucker-Komprimierung
dialog.compress.douglaspeucker.paramdesc=Span-Faktor
dialog.compress.summarylabel=Zu entfernende Punkte
-dialog.compress.confirm1=Es wurden
-dialog.compress.confirm2=Punkte markiert.\nMit Track->Markierte Punkte l\u00f6schen werden sie gel\u00f6scht
+dialog.compress.confirm=Es wurden %d Punkte markiert.\nWollen Sie die Punkte sofort l\u00f6schen?
dialog.compress.confirmnone=es wurden keine Punkte markiert
dialog.deletemarked.nonefound=Es konnten keine Punkte entfernt werden
dialog.pastecoordinates.desc=Koordinaten eingeben oder einf\u00fcgen
dialog.pastecoordinates.coords=Koordinaten
dialog.pastecoordinates.nothingfound=Bitte pr\u00fcfen Sie die Koordinaten und versuchen Sie es nochmals
-dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n http://activityworkshop.net/software/gpsprune/
+dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n http://gpsprune.activityworkshop.net/
dialog.about.version=Version
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune ist ein Programm zum Laden, Darstellen und Editieren der Daten von GPS- Ger\u00e4ten.
@@ -467,7 +474,7 @@ dialog.checkversion.newversion1=Eine neue Version von GpsPrune ist jetzt verf\u0
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Diese neue Version ist am
dialog.checkversion.releasedate2=ver\u00f6ffentlicht worden.
-dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=Anstelle der Maus k\u00f6nnen Sie folgende Tastenkombinationen nutzen
dialog.keys.keylist=<table><tr><td>Pfeil Tasten</td><td>Karte verschieben</td></tr><tr><td>Strg + Links-, Rechts-Pfeil</td><td>Vorherigen oder n\u00e4chsten Punkt markieren</td></tr><tr><td>Strg + Auf-, Abw\u00e4rts-Pfeil</td><td>Ein- oder Auszoomen</td></tr><tr><td>Strg + Bild auf, ab</td><td>Vorheriges oder n\u00e4chstes Segment markieren</td></tr><tr><td>Strg + Pos1, Ende</td><td>Ersten oder letzten Punkt markieren</td></tr><tr><td>Entf</td><td>Aktuellen Punkt entfernen</td></tr></table>
dialog.keys.normalmodifier=Strg
@@ -530,13 +537,34 @@ dialog.diskcache.tileset.multiple=mehrere
dialog.diskcache.deleteold=Veraltete Kacheln l\u00f6schen
dialog.diskcache.maximumage=Maximales Alter (Tage)
dialog.diskcache.deleteall=Alle Kacheln l\u00f6schen
-dialog.diskcache.deleted1=Es wurden
-dialog.diskcache.deleted2=Dateien aus dem Ordner gel\u00f6scht
+dialog.diskcache.deleted=Es wurden %d Dateien aus dem Ordner gel\u00f6scht
dialog.deletefieldvalues.intro=W\u00e4hlen Sie das Feld aus, das Sie l\u00f6schen m\u00f6chten
dialog.deletefieldvalues.nofields=Es sind keine Felder zu l\u00f6schen f\u00fcr diesen Bereich
dialog.setlinewidth.text=Geben Sie die Dicke der Linien ein (1-4)
dialog.downloadosm.desc=Die OpenStreetMap-Daten f\u00fcr das folgende Gebiet werden heruntergeladen (.osm-Datei):
dialog.searchwikipedianames.search=Suche nach:
+dialog.weather.location=Ort
+dialog.weather.update=Vorhersage aktualisiert
+dialog.weather.sunrise=Sonnenaufgang
+dialog.weather.sunset=Sonnenuntergang
+dialog.weather.temperatureunits=Temperaturen
+dialog.weather.currentforecast=Aktuell
+dialog.weather.dailyforecast=T\u00e4gliche Vorhersage
+dialog.weather.3hourlyforecast=Drei-st\u00fcndliche Vorhersage
+dialog.weather.day.now=Aktuell
+dialog.weather.day.today=Heute
+dialog.weather.day.tomorrow=Morgen
+dialog.weather.day.monday=Montag
+dialog.weather.day.tuesday=Dienstag
+dialog.weather.day.wednesday=Mittwoch
+dialog.weather.day.thursday=Donnerstag
+dialog.weather.day.friday=Freitag
+dialog.weather.day.saturday=Samstag
+dialog.weather.day.sunday=Sonntag
+dialog.weather.wind=Wind
+dialog.weather.temp=Temp
+dialog.weather.humidity=R.L.
+dialog.weather.creditnotice=Diese Daten wurden von openweathermap.org zur Verf\u00fcgung gestellt. Die Webseite hat mehr Information.
# 3d window
dialog.3d.title=GpsPrune-3D-Ansicht
@@ -555,11 +583,12 @@ confirm.addtimeoffset=Zeitverschiebung aufgerechnet
confirm.addaltitudeoffset=H\u00f6henverschiebung aufgerechnet
confirm.rearrangewaypoints=Wegpunkte neu angeordnet
confirm.rearrangephotos=Fotos neu angeordnet
+confirm.splitsegments=Es wurden %d Schnitte gemacht
+confirm.sewsegments=Es wurden %d Verbindungen gemacht
confirm.cutandmove=Bereich verschoben
confirm.interpolate=Punkte eingef\u00fcgt
confirm.convertnamestotimes=Wegpunktnamen umgewandelt
-confirm.saveexif.ok1=Es wurden
-confirm.saveexif.ok2=Fotodateien geschrieben
+confirm.saveexif.ok=Es wurden %d Fotodateien geschrieben
confirm.undo.single=Operation r\u00fcckg\u00e4ngig gemacht
confirm.undo.multi=Operationen r\u00fcckg\u00e4ngig gemacht
confirm.jpegload.single=Foto wurde geladen
@@ -573,13 +602,23 @@ confirm.correlatephotos.multi=Fotos wurden korreliert
confirm.createpoint=Punkt erzeugt
confirm.rotatephoto=Foto gedreht
confirm.running=In Bearbeitung ...
-confirm.lookupsrtm1=Es wurden
-confirm.lookupsrtm2=H\u00f6henwerte gefunden
+confirm.lookupsrtm=Es wurden %d H\u00f6henwerte gefunden
+confirm.downloadsrtm=Es wurden %d Dateien heruntergeladen
+confirm.downloadsrtm.1=Es wurde %d Datei heruntergeladen
+confirm.downloadsrtm.none=Keine Dateien heruntergeladen, alle waren schon gespeichert.
confirm.deletefieldvalues=Feldwerte gel\u00f6scht
confirm.audioload=Audiodateien geladen
confirm.correlateaudios.single=Audio wurde korreliert
confirm.correlateaudios.multi=Audios wurden korreliert
+# Tips
+tip.title=Tipp
+tip.useamapcache=Mit lokal-gespeicherten Kartenkacheln (Einstellungen -> Karten auf Festplatte speichern)\nk\u00f6nnen Sie die Darstellung beschleunigen und Netzwerkverkehr reduzieren.
+tip.learntimeparams=Wenn Sie Track -> Zeitparameter erlernen mit Ihren Tracks benutzen\ndann werden die berechneten Werten genauer.
+tip.downloadsrtm=Sie k\u00f6nnen diese Funktion beschleunigen indem Sie\nOnline -> SRTM Dateien herunterladen aufrufen\num die Daten lokal zu speichern.
+tip.usesrtmfor3d=Dieser Track hat keine H\u00f6heninformation.\nSie k\u00f6nnen die SRTM Funktionen verwenden, um\nH\u00f6henwerte abzusch\u00e4tzen.
+tip.manuallycorrelateone=Mit mindestens einem manuell verbundenen Element kann die Zeitdifferenz automatisch berechnet werden.
+
# Buttons
button.ok=OK
button.back=Zur\u00fcck
@@ -597,6 +636,7 @@ button.yes=Ja
button.no=Nein
button.yestoall=Ja f\u00fcr alle
button.notoall=Nein f\u00fcr alle
+button.always=Ja, immer
button.select=Ausw\u00e4hlen
button.selectall=Alle ausw\u00e4hlen
button.selectnone=Nichts ausw\u00e4hlen
@@ -685,7 +725,6 @@ fieldname.newsegment=Segment
fieldname.custom=Custom
fieldname.prefix=Feld
fieldname.distance=L\u00e4nge
-fieldname.movingdistance=Wegstrecke
fieldname.duration=Zeitdauer
fieldname.speed=Geschwindigkeit
fieldname.verticalspeed=Vertikale Geschwindigkeit
@@ -726,6 +765,7 @@ logic.or=oder
# External urls
url.googlemaps=maps.google.de
wikipedia.lang=de
+openweathermap.lang=de
# Cardinals for 3d plots
cardinal.n=N
@@ -747,6 +787,8 @@ undo.deletemarked=Punkte l\u00f6schen
undo.insert=Punkte hinzuf\u00fcgen
undo.reverse=Bereich umdrehen
undo.mergetracksegments=Trackabschnitte verbinden
+undo.splitsegments=in Trackabschnitte schneiden
+undo.sewsegments=Trackabschnitte zusammenf\u00fcgen
undo.addtimeoffset=Zeitverschiebung aufrechnen
undo.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
undo.rearrangewaypoints=Wegpunkte neu anordnen
@@ -769,10 +811,8 @@ error.save.failed=Speichern von Daten in Datei fehlgeschlagen
error.saveexif.filenotfound=Bilddatei nicht gefunden
error.saveexif.cannotoverwrite1=Bilddatei
error.saveexif.cannotoverwrite2=ist schreibgesch\u00fctzt. Als Kopie speichern?
-error.saveexif.failed1=
-error.saveexif.failed2=Bilder konnten nicht gespeichert werden
-error.saveexif.forced1=Bei
-error.saveexif.forced2=der Bilder musste das Speichern erzwungen werden
+error.saveexif.failed=%d Bilder konnten nicht gespeichert werden
+error.saveexif.forced=Bei %d der Bilder musste das Speichern erzwungen werden
error.load.dialogtitle=Fehler beim Laden
error.load.noread=Datei konnte nicht gelesen werden
error.load.nopoints=Keine g\u00fcltigen Daten in Datei gefunden
@@ -809,3 +849,6 @@ error.cache.empty=Der Ordner ist leer
error.cache.cannotdelete=Es konnte keine Kacheln gel\u00f6scht werden
error.interpolate.invalidparameter=Die Anzahl der Punkte muss zwischen 1 und 1000 liegen
error.learnestimationparams.failed=Mit diesem Track k\u00f6nnen die Parameter nicht berechnet werden.\nVersuchen Sie mit mehreren Tracks.
+error.tracksplit.nosplit=Der Track konnte nicht aufgesplittet werden.
+error.downloadsrtm.nocache=Die Dateien konnten nicht gespeichert werden.\nBitte pr\u00fcfen Sie den Kartenordner nach.
+error.sewsegments.nothingdone=Es wurden keine Verbindungen gemacht.\nEs gibt jetzt %d Trackabschnitte.
diff --git a/tim/prune/lang/prune-texts_de_CH.properties b/tim/prune/lang/prune-texts_de_CH.properties
index 808bfae..0c192e8 100644
--- a/tim/prune/lang/prune-texts_de_CH.properties
+++ b/tim/prune/lang/prune-texts_de_CH.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=F\u00f6telis inn\u00e4tue
menu.file.recentfiles=Letzschti aagluegte Files
menu.file.save=Als Text Speichere
menu.file.exit=Be\u00e4nde
+menu.online=Online
menu.track=Track
menu.track.undo=Undo
menu.track.clearundo=Undo-Liste l\u00f6sche
@@ -14,7 +15,7 @@ menu.track.markrectangle=P\u00fcnkte inem Viereck markiere
menu.track.deletemarked=Markierte P\u00fcnkte l\u00f6sche
menu.track.rearrange=Waypoints reorganisiere
menu.track.rearrange.start=Alli zum Aafang
-menu.track.rearrange.end=Alli zum �nde
+menu.track.rearrange.end=Alli zum \u00c4nde
menu.track.rearrange.nearest=Jede zum n\u00f6chsti Trackpunkt
menu.range=Beriich
menu.range.all=Alles selektiere
@@ -56,6 +57,7 @@ menu.map.editmode=P\u00fcnkte verschiebe
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=O
altkey.menu.track=T
altkey.menu.range=B
altkey.menu.point=P
@@ -101,9 +103,12 @@ function.fullrangedetails=Zues\u00e4tzlichi Beriichinfos
function.estimatetime=Ziit absch\u00e4tze
function.learnestimationparams=Ziitparameter erlerne
function.setmapbg=Karte Hintegrund setz\u00e4
+function.splitsegments=In Tracksegm\u00e4nte schniide
+function.sewsegments=Tracksegm\u00e4nte z\u00e4mef\u00fcge
function.getgpsies=Gpsies Tracks hol\u00e4
function.uploadgpsies=Date zum Gpsies uufalad\u00e4
function.lookupsrtm=H\u00f6hendate vonem SRTM hole
+function.downloadsrtm=SRTM Files abalade
function.getwikipedia=Im Wikipedia in dr N\u00f6chi naaluege
function.searchwikipedianames=Wikipedia mit Name durasueche
function.downloadosm=OSM-Date f\u00fcr dere Gebiet abalad\u00e4
@@ -125,11 +130,12 @@ function.setlinewidth=Liniedicke setz\u00e4
function.setlanguage=Sproch setz\u00e4
function.help=Hilfe
function.showkeys=Tastekombinatione aazeige
-function.about=�ber GpsPrune
+function.about=\u00dcber GpsPrune
function.checkversion=Pruef nach ne noie Version
function.saveconfig=Iistellige speichere
function.diskcache=Karten uufem Disk speichere
function.managetilecache=Kartebildli verwolte
+function.getweatherforecast=W\u00e4tterprognose abalade
# Dialogs
dialog.exit.confirm.title=GpsPrune be\u00e4nde
@@ -141,7 +147,7 @@ dialog.deletepoint.deletephoto=s F\u00f6teli vonem Punkt au l\u00f6sch\u00e4?
dialog.deletephoto.title=F\u00f6teli entfern\u00e4
dialog.deletephoto.deletepoint=Punkt vonem F\u00f6teli au l\u00f6sch\u00e4?
dialog.deleteaudio.deletepoint=Punkt vonem Audio au l\u00f6sch\u00e4?
-dialog.openoptions.title=�ffne Optionen
+dialog.openoptions.title=\u00d6ffne Optionen
dialog.openoptions.filesnippet=Extrakt vom File
dialog.load.table.field=F\u00e4ld
dialog.load.table.datatype=Date Typ
@@ -214,7 +220,7 @@ dialog.save.overwrite.title=s'File existiert scho
dialog.save.overwrite.text=s'File existiert scho. Sind Sie sicher, Sie wend s'File \u00fcberschriibe?
dialog.save.notypesselected=Kei Punktetype sin uusgew\u00e4hlt worde
dialog.exportkml.text=Titel f\u00fcr die Date
-dialog.exportkml.altitude=Absolut H\u00f6chiinformation (f\u00fcrs Fliege)
+dialog.exportkml.altitude=Absolut H\u00f6chiinformation (f\u00fcrs Fl\u00fc\u00fcge)
dialog.exportkml.kmz=Date ins kmz File komprimier\u00e4
dialog.exportkml.exportimages=Bildli ins Kmz exportier\u00e4
dialog.exportkml.imagesize=Bildligr\u00f6sse
@@ -237,6 +243,8 @@ dialog.exportpov.modelstyle=Modellstil
dialog.exportpov.ballsandsticks=B\u00e4lle und Schtange
dialog.exportpov.tubesandwalls=R\u00f6hre und W\u00e4nde
dialog.3d.warningtracksize=Dieser Track h\u00e4t mega viele P\u00fcnkte, die Java3D villiicht n\u00f6d chann bearbeite.\nSind Sie sicher, Sie wend trotzdem fortsetze?
+dialog.3d.useterrain=Gel\u00e4nde aazeige
+dialog.3d.terraingridsize=Gittergr\u00f6sse
dialog.exportpov.baseimage=Grundbild
dialog.exportpov.cannotmakebaseimage=Bild chann n\u00f6d gspeicheret werde
dialog.baseimage.title=Kartenbild
@@ -252,6 +260,7 @@ dialog.exportsvg.theta=Neigigswinkel \u03B8
dialog.exportsvg.gradients=Farbeverl\u00e4ufe verw\u00e4nde
dialog.exportimage.noimagepossible=Kartebilder m\u00fcsset scho gspeicheret werde, bevor sie bim Export verwendet werde k\u00f6nne
dialog.exportimage.drawtrack=Track uf d Karte zeichne
+dialog.exportimage.drawtrackpoints=Trackp\u00fcnkte au zeichne
dialog.exportimage.textscalepercent=Text Skalierig (%)
dialog.pointtype.desc=Folgende Punkttype speichere:
dialog.pointtype.track=Trackp\u00fcnkte
@@ -373,7 +382,6 @@ dialog.correlate.photoselect.intro=W\u00e4hlet Sie eini vo deren F\u00f6teli uus
dialog.correlate.select.photoname=F\u00f6teli Name
dialog.correlate.select.timediff=Ziitdiffer\u00e4nz
dialog.correlate.select.photolater=F\u00f6teli sp\u00f6ter
-dialog.correlate.options.tip=Tipp: Mit mindeschtens einem verbundenen Elem\u00e4nt kann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
dialog.correlate.options.intro=W\u00e4hlet Sie die Optione uus f\u00fcr die Korrelierig
dialog.correlate.options.offsetpanel=Ziitunterschied
dialog.correlate.options.offset=Unterschied
@@ -396,13 +404,13 @@ dialog.correlate.filetimes2=der Tonspuren
dialog.correlate.correltimes=F\u00fcrs Korreliere, folgendes verw\u00e4nde:
dialog.correlate.timestamp.beginning=Aafang
dialog.correlate.timestamp.middle=Mitti
-dialog.correlate.timestamp.end=�nde
+dialog.correlate.timestamp.end=\u00c4nde
dialog.correlate.audioselect.intro=W\u00e4hlet Sie eini vo deren Audios uus, um die Ziitdiffer\u00e4nz zu ber\u00e4chn\u00e4
dialog.correlate.select.audioname=Audio Name
dialog.correlate.select.audiolater=Audio sp\u00f6ter
dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von d P\u00fcnkte setze
dialog.rearrangephotos.tostart=zum Aafang
-dialog.rearrangephotos.toend=zum �nde
+dialog.rearrangephotos.toend=zum \u00c4nde
dialog.rearrangephotos.nosort=N\u00f6d sortiere
dialog.rearrangephotos.sortbyfilename=per Filename sortiere
dialog.rearrangephotos.sortbytime=per Ziit sortiere
@@ -416,21 +424,20 @@ dialog.compress.singletons.paramdesc=Distanz faktor
dialog.compress.douglaspeucker.title=Douglas-Peucker Komprimierig
dialog.compress.douglaspeucker.paramdesc=Span Faktor
dialog.compress.summarylabel=P\u00fcnkte zu entf\u00e4rn\u00e4
-dialog.compress.confirm1=Es sin
-dialog.compress.confirm2=P\u00fcnkt markiert.\nMit Track->Markierte P\u00fcnkte l\u00f6sche werdet sie gl\u00f6scht
+dialog.compress.confirm=Es sin %s P\u00fcnkt markiert worde.\nWend Sie die jetz l\u00f6sche?
dialog.compress.confirmnone=es sin kei P\u00fcnkte markiert worde
dialog.deletemarked.nonefound=Kei P\u00fcnkte h\u00e4tte gel\u00f6scht werde k\u00f6nne
dialog.pastecoordinates.desc=G\u00e4bet Sie hier die Koordinaten inn\u00e4
dialog.pastecoordinates.coords=Koordinate
dialog.pastecoordinates.nothingfound=Pr\u00fcefet Sie die Koordinate und versuechet nomal
-dialog.help.help=Bitte lueg na\n http://activityworkshop.net/software/gpsprune/\nf\u00fcr wiitere Information und Benutzeraaleitige.
+dialog.help.help=Bitte lueg na\n http://gpsprune.activityworkshop.net/\nf\u00fcr wiitere Information und Benutzeraaleitige.
dialog.about.version=Version
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune isch s Programm f\u00fcrs Lade, Darstelle und Editiere vo Date von GPS Ger\u00e4te.
dialog.about.summarytext2=Es isch unter den Gnu GPL zur Verf\u00fcegig gstellt,f\u00fcr frei, gratis und offen Gebruuch und Wiiterentwicklig.<br>Kopiere, Wiiterverbreitig und Ver\u00e4nderige sin erlaubt und willkomme<br>unter die Bedingige im enthaltene <code>license.txt</code> File.
dialog.about.summarytext3=Bitte lueget Sie na <code style="font-weight:bold">http://activityworkshop.net/</code> f\u00fcr wiitere Informatione und Benutzeraaleitige.
dialog.about.languages=Verf\u00fcegbare Sproche
-dialog.about.translatedby=Schwiizerd\u00fc\u00fctschi �bersetzig vo activityworkshop.
+dialog.about.translatedby=Schwiizerd\u00fc\u00fctschi \u00dcbersetzig vo activityworkshop.
dialog.about.systeminfo=Syschtem Info
dialog.about.systeminfo.os=Betriebsyschtem
dialog.about.systeminfo.java=Version vonem Java
@@ -451,7 +458,7 @@ dialog.about.credits.code=GpsPrune Code gschrieb\u00e4 vo
dialog.about.credits.exifcode=Exif Code vo
dialog.about.credits.icons=Einigi Bilder vo
dialog.about.credits.translators=Dolm\u00e4tscher
-dialog.about.credits.translations=�bersetzige mit dr Hilfe vo
+dialog.about.credits.translations=\u00dcbersetzige mit dr Hilfe vo
dialog.about.credits.devtools=Entwicklungsw\u00e4rkz\u00fc\u00fcge
dialog.about.credits.othertools=Anderi W\u00e4rkz\u00fc\u00fcge
dialog.about.credits.thanks=Danke an
@@ -462,9 +469,9 @@ dialog.checkversion.newversion1=Ne noii Version vonem GpsPrune isch jetzt usse!
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Die noii Version isch am
dialog.checkversion.releasedate2=ussecho.
-dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=Aastatt d'Muus k\u00f6nnet Sie diese Tastekombinationen nutze
-dialog.keys.keylist=<table><tr><td>Pfiil Taste</td><td>Karte verschiebe</td></tr><tr><td>Strg + links, r\u00e4chts Pfiil</td><td>Vorherigi oder n\u00f6chsti Punkt markiere</td></tr><tr><td>Strg + uuf, aba Pfiil</td><td>Ii- oder Uusezoome</td></tr><tr><td>Strg + Bild uuf, ab</td><td>Vorherigi oder n\u00f6chsti Segm\u00e4nt markiere</td></tr><tr><td>Strg + Pos1, �nde</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt l\u00f6sche</td></tr></table>
+dialog.keys.keylist=<table><tr><td>Pfiil Taste</td><td>Karte verschiebe</td></tr><tr><td>Strg + links, r\u00e4chts Pfiil</td><td>Vorherigi oder n\u00f6chsti Punkt markiere</td></tr><tr><td>Strg + uuf, aba Pfiil</td><td>Ii- oder Uusezoome</td></tr><tr><td>Strg + Bild uuf, ab</td><td>Vorherigi oder n\u00f6chsti Segm\u00e4nt markiere</td></tr><tr><td>Strg + Pos1, \u00c4nde</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt l\u00f6sche</td></tr></table>
dialog.keys.normalmodifier=Strg
dialog.keys.macmodifier=Kommando
dialog.saveconfig.desc=Die folgendi Iinstellige k\u00f6nne gspeicheret werde :
@@ -525,13 +532,34 @@ dialog.diskcache.tileset.multiple=mehreri
dialog.diskcache.deleteold=Uualti Kachle l\u00f6sche
dialog.diskcache.maximumage=Maximali Alter (Tag)
dialog.diskcache.deleteall=Alli Kachle l\u00f6sche
-dialog.diskcache.deleted1=Es sin
-dialog.diskcache.deleted2=Files uusem Ordner gl\u00f6scht worde
+dialog.diskcache.deleted=Es sin %d Files uusem Ordner gl\u00f6scht worde
dialog.deletefieldvalues.intro=W\u00e4hlet Sie s F\u00e4ld uus zum l\u00f6sche
dialog.deletefieldvalues.nofields=Es sin kei F\u00e4lder z'l\u00f6sche f\u00fcr dere Beriich
dialog.setlinewidth.text=G\u00e4bet Sie die Dicke vonen Linien ii (1-4)
dialog.downloadosm.desc=Best\u00e4tige um rohi OSM Date f\u00fcrn Gebiet aba zlade:
dialog.searchwikipedianames.search=Sueche na:
+dialog.weather.location=Ort
+dialog.weather.update=Prognose aktualisiert
+dialog.weather.sunrise=Sonnenufgang
+dialog.weather.sunset=Sonnenuntergang
+dialog.weather.temperatureunits=Temperature
+dialog.weather.currentforecast=Aktuell
+dialog.weather.dailyforecast=T\u00e4glichi Prognose
+dialog.weather.3hourlyforecast=Dr\u00fc\u00fc-st\u00fcndlichi Prognose
+dialog.weather.day.now=Jetz\u00e4
+dialog.weather.day.today=H\u00fc\u00fct
+dialog.weather.day.tomorrow=Morn
+dialog.weather.day.monday=M\u00e4ntig
+dialog.weather.day.tuesday=Ziischtig
+dialog.weather.day.wednesday=Mittwuch
+dialog.weather.day.thursday=Duunschtig
+dialog.weather.day.friday=Friitig
+dialog.weather.day.saturday=Samschtig
+dialog.weather.day.sunday=Sunntig
+dialog.weather.wind=Wind
+dialog.weather.temp=Temp
+dialog.weather.humidity=R.L.
+dialog.weather.creditnotice=Diese Date sin vo openweathermap.org zur Verf\u00fcegig gestellt worde. Uf d Websiite h\u00e4ts no meh Infos.
# 3d window
dialog.3d.title=GpsPrune Dr\u00fc\u00fc-d Aasicht
@@ -550,11 +578,12 @@ confirm.addtimeoffset=Ziitverschiebig zutue
confirm.addaltitudeoffset=H\u00f6chiverschiebig zutue
confirm.rearrangewaypoints=Waypoints umorganisiert
confirm.rearrangephotos=Fotos umorganisiert
+confirm.splitsegments=Es sin %d Schnitte gmacht worde
+confirm.sewsegments=Es sin %d Verbindige gemacht worde
confirm.cutandmove=Beriich gmoved
confirm.interpolate=P\u00fcnkte iigf\u00fcgt worde
confirm.convertnamestotimes=Waypointname verwondlet
-confirm.saveexif.ok1=Es sin
-confirm.saveexif.ok2=F\u00f6telis gschriebe worde
+confirm.saveexif.ok=Es sin %d F\u00f6telis gschriebe worde
confirm.undo.single=Operation r\u00fcckg\u00e4ngig gmacht worde.
confirm.undo.multi=Operatione r\u00fcckg\u00e4ngig gmacht worde.
confirm.jpegload.single=F\u00f6teli isch glade worde
@@ -568,21 +597,31 @@ confirm.correlatephotos.multi=F\u00f6telis sin korreliert worde
confirm.createpoint=Punkt kreiert worde
confirm.rotatephoto=F\u00f6teli umgedr\u00e4it worde
confirm.running=Am Laufe ...
-confirm.lookupsrtm1=Es sin
-confirm.lookupsrtm2=H\u00f6henwerte gfunde
+confirm.lookupsrtm=Es sin %d H\u00f6henwerte gfunde
+confirm.downloadsrtm=Es sin %d Files abeglade
+confirm.downloadsrtm.1=Sisch %d File abeglade
+confirm.downloadsrtm.none=Kei Files abeglade, die sin alli scho da gsi.
confirm.deletefieldvalues=Feldw\u00e4rte gl\u00f6scht worde
confirm.audioload=Audiofiles glade worde
confirm.media.removed=entf\u00e4rnt
confirm.correlateaudios.single=Audiofile isch korreliert worde
confirm.correlateaudios.multi=Audiofiles sin korreliert worde
+# Tips
+tip.title=Tipp
+tip.useamapcache=Mit lokali Kartekachle (Iistellige -> Karten uufem Disk speichere)\nk\u00f6nnet Sie d Darstellig bschleunige und Netzwerkverkehr reduziere.
+tip.learntimeparams=Wenn Sie Track -> Ziitparameter erlerne mit Ihren Tracks benutze\ndann werdet d ber\u00e4chneti Werte gnauer.
+tip.downloadsrtm=Sie k\u00f6nnet d Funktion beschleunige indem Sie\nOnline -> SRTM Files abalade uufrufe\num d Date lokal z'speichere.
+tip.usesrtmfor3d=Dere Track h\u00e4t kei H\u00f6chiinformation.\nSie k\u00f6nnet d SRTM Funktione verw\u00e4nde, um\nH\u00f6chiwerte abz'sch\u00e4tze.
+tip.manuallycorrelateone=Mit mindeschtens einem verbundenen Elem\u00e4nt kann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
+
# Buttons
button.ok=OK
button.back=Zrugg
button.next=N\u00f6chst\u00e4
button.finish=Fertig
button.cancel=Abbr\u00e4ch\u00e4
-button.overwrite=�berschriib\u00e4
+button.overwrite=\u00dcberschriib\u00e4
button.moveup=Uuf\u00e4 schieb\u00e4
button.movedown=Aba schieb\u00e4
button.edit=Editier\u00e4
@@ -593,6 +632,7 @@ button.yes=Ja
button.no=Nei
button.yestoall=Ja f\u00fcr alli
button.notoall=Nei f\u00fcr alli
+button.always=Ja, immer
button.select=Uusw\u00e4hle
button.selectall=Alli uusw\u00e4hle
button.selectnone=N\u00fc\u00fct uusw\u00e4hle
@@ -680,7 +720,6 @@ fieldname.newsegment=Segm\u00e4nt
fieldname.custom=Custom
fieldname.prefix=F\u00e4ld
fieldname.distance=L\u00e4ngi
-fieldname.movingdistance=Wegl\u00e4ngi
fieldname.duration=Ziitl\u00e4ngi
fieldname.speed=Gschwindikeit
fieldname.verticalspeed=Uf/Ab Gschwindikeit
@@ -721,6 +760,7 @@ logic.or=oder
# External urls
url.googlemaps=maps.google.ch
wikipedia.lang=als
+openweathermap.lang=de
# Cardinals for 3d plots
cardinal.n=N
@@ -742,6 +782,8 @@ undo.deletemarked=P\u00fcnkte l\u00f6sch\u00e4
undo.insert=P\u00fcnkte inn\u00e4tu\u00e4
undo.reverse=Beriich umdr\u00e4hie
undo.mergetracksegments=Tracksegm\u00e4nte merge
+undo.splitsegments=in Tracksegm\u00e4nte schniide
+undo.sewsegments=Tracksegm\u00e4nte z\u00e4mef\u00fcge
undo.addtimeoffset=Ziitverschiebig zutue
undo.addaltitudeoffset=H\u00f6chiverschiebig zutue
undo.rearrangewaypoints=Waypoints reorganisier\u00e4
@@ -764,10 +806,8 @@ error.save.failed=Speichere vom File fehlgschlage
error.saveexif.filenotfound=F\u00f6teli File n\u00f6d gfunde
error.saveexif.cannotoverwrite1=F\u00f6teli File
error.saveexif.cannotoverwrite2=isch n\u00f6d schriibbar. Speichere na einer Kopie?
-error.saveexif.failed1=
-error.saveexif.failed2=von d Bilder han i n\u00f6d k\u00f6nne speichere
-error.saveexif.forced1=
-error.saveexif.forced2=von d Bilder han i m\u00fcsse forziere
+error.saveexif.failed=%d von d Bilder han i n\u00f6d k\u00f6nne speichere
+error.saveexif.forced=%d von d Bilder han i m\u00fcsse forziere
error.load.dialogtitle=F\u00e4hle bim Lade
error.load.noread=File cha n\u00f6d glase werde
error.load.nopoints=Kei g\u00fcltigi Information inem File gfunde
@@ -778,7 +818,7 @@ error.jpegload.dialogtitle=F\u00e4hle bim Lade von F\u00f6telis
error.jpegload.nofilesfound=Kei Files gfunde
error.jpegload.nojpegsfound=Kei Jpegs gfunde
error.jpegload.nogpsfound=Kei GPS Information gfunde
-error.jpegload.exifreadfailed=EXIF Uufruef isch fehlgschlage. Kei EXIF Infos k\u00f6nnet gl\u00e4se werde\nohni nen interni oder ext\u00e4rni Bibliothek.
+error.jpegload.exifreadfailed=Exif Uufruef isch fehlgschlage. Kei Exif Infos k\u00f6nnet gl\u00e4se werde\nohni nen interni oder ext\u00e4rni Bibliothek.
error.audioload.nofilesfound=Kei Audiofiles gfunde
error.gpsload.unknown=Unbekannts F\u00e4hler
error.undofailed.title=Undo isch fehlgschlage worde
@@ -804,3 +844,6 @@ error.cache.empty=D Ordner h\u00e4t n\u00fc\u00fct drinne
error.cache.cannotdelete=Es sin kei Kachle gl\u00f6scht worde
error.interpolate.invalidparameter=D'Aazahl P\u00fcnkt muess zw\u00fcschet 1 und 1000 sii
error.learnestimationparams.failed=Mit dere Track k\u00f6nnet die Parameter n\u00f6d br\u00e4chnet werde.\nVersuechet Sie mit mehreri Tracks.
+error.tracksplit.nosplit=Es isch n\u00f6d m\u00f6glech gsi, den Track uufz'schniide.
+error.downloadsrtm.nocache=Die Files k\u00f6nnet n\u00f6d gspeicheret werde.\nBitte pr\u00fcefet Sie den Kartenordner na.
+error.sewsegments.nothingdone=Es sin kei Verbindige gmacht worde.\nEs h\u00e4t jetzt %d Tracksegm\u00e4nte.
diff --git a/tim/prune/lang/prune-texts_en.properties b/tim/prune/lang/prune-texts_en.properties
index 0102075..0e8976c 100644
--- a/tim/prune/lang/prune-texts_en.properties
+++ b/tim/prune/lang/prune-texts_en.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=Add photos
menu.file.recentfiles=Recent files
menu.file.save=Save as text
menu.file.exit=Exit
+menu.online=Online
menu.track=Track
menu.track.undo=Undo
menu.track.clearundo=Clear undo list
@@ -57,6 +58,7 @@ menu.map.editmode=Edit mode
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=N
altkey.menu.range=R
altkey.menu.track=T
altkey.menu.point=P
@@ -102,9 +104,12 @@ function.distances=Distances
function.fullrangedetails=Full range details
function.estimatetime=Estimate time
function.learnestimationparams=Learn time estimation parameters
+function.splitsegments=Split track into segments
+function.sewsegments=Sew track segments together
function.getgpsies=Get Gpsies tracks
function.uploadgpsies=Upload track to Gpsies
function.lookupsrtm=Get altitudes from SRTM
+function.downloadsrtm=Download SRTM tiles
function.getwikipedia=Get nearby Wikipedia articles
function.searchwikipedianames=Search Wikipedia by name
function.downloadosm=Download OSM data for area
@@ -135,6 +140,7 @@ function.checkversion=Check for new version
function.saveconfig=Save settings
function.diskcache=Save maps to disk
function.managetilecache=Manage tile cache
+function.getweatherforecast=Get weather forecast
# Dialogs
dialog.exit.confirm.title=Exit GpsPrune
@@ -242,6 +248,8 @@ dialog.exportpov.modelstyle=Model style
dialog.exportpov.ballsandsticks=Balls and sticks
dialog.exportpov.tubesandwalls=Tubes and walls
dialog.3d.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.3d.useterrain=Show terrain
+dialog.3d.terraingridsize=Grid size
dialog.exportpov.baseimage=Base image
dialog.exportpov.cannotmakebaseimage=Cannot write base image
dialog.baseimage.title=Map image
@@ -257,6 +265,7 @@ dialog.exportsvg.theta=Elevation angle \u03B8
dialog.exportsvg.gradients=Use gradients for shading
dialog.exportimage.noimagepossible=Map images need to be cached to disk in order to use them for an export.
dialog.exportimage.drawtrack=Draw track on map
+dialog.exportimage.drawtrackpoints=Draw track points
dialog.exportimage.textscalepercent=Text scale factor (%)
dialog.pointtype.desc=Save the following point types:
dialog.pointtype.track=Track points
@@ -378,7 +387,6 @@ dialog.correlate.photoselect.intro=Select one of these correlated photos to use
dialog.correlate.select.photoname=Photo name
dialog.correlate.select.timediff=Time difference
dialog.correlate.select.photolater=Photo later
-dialog.correlate.options.tip=Tip: By manually connecting at least one item, the time offset can be calculated for you.
dialog.correlate.options.intro=Select the options for automatic correlation
dialog.correlate.options.offsetpanel=Time offset
dialog.correlate.options.offset=Offset
@@ -421,8 +429,7 @@ dialog.compress.singletons.paramdesc=Distance factor
dialog.compress.douglaspeucker.title=Douglas-Peucker compression
dialog.compress.douglaspeucker.paramdesc=Span factor
dialog.compress.summarylabel=Points to delete
-dialog.compress.confirm1=
-dialog.compress.confirm2=points have been marked.\nUse Track->Delete marked points to delete them
+dialog.compress.confirm=%d points have been marked.\nDelete these marked points now?
dialog.compress.confirmnone=no points have been marked
dialog.deletemarked.nonefound=No data points could be removed
dialog.pastecoordinates.desc=Enter or paste the coordinates here
@@ -530,13 +537,34 @@ dialog.diskcache.tileset.multiple=multiple
dialog.diskcache.deleteold=Delete old tiles
dialog.diskcache.maximumage=Maximum age (days)
dialog.diskcache.deleteall=Delete all tiles
-dialog.diskcache.deleted1=Deleted
-dialog.diskcache.deleted2=files from the cache
+dialog.diskcache.deleted=Deleted %d files from the cache
dialog.deletefieldvalues.intro=Select the field to delete for the current range
dialog.deletefieldvalues.nofields=There are no fields to delete for this range
dialog.setlinewidth.text=Enter the thickness of lines to draw for the tracks (1-4)
dialog.downloadosm.desc=Confirm to download the raw OSM data for the specified area:
dialog.searchwikipedianames.search=Search for:
+dialog.weather.location=Location
+dialog.weather.update=Forecast updated
+dialog.weather.sunrise=Sunrise
+dialog.weather.sunset=Sunset
+dialog.weather.temperatureunits=Temperatures
+dialog.weather.currentforecast=Current weather
+dialog.weather.dailyforecast=Daily forecast
+dialog.weather.3hourlyforecast=Three-hourly forecast
+dialog.weather.day.now=Current weather
+dialog.weather.day.today=Today
+dialog.weather.day.tomorrow=Tomorrow
+dialog.weather.day.monday=Monday
+dialog.weather.day.tuesday=Tuesday
+dialog.weather.day.wednesday=Wednesday
+dialog.weather.day.thursday=Thursday
+dialog.weather.day.friday=Friday
+dialog.weather.day.saturday=Saturday
+dialog.weather.day.sunday=Sunday
+dialog.weather.wind=Wind
+dialog.weather.temp=Temp
+dialog.weather.humidity=Humidity
+dialog.weather.creditnotice=This data is made available by openweathermap.org. Their website has more details.
# 3d window
dialog.3d.title=GpsPrune Three-d view
@@ -555,11 +583,12 @@ confirm.addtimeoffset=Time offset added
confirm.addaltitudeoffset=Altitude offset added
confirm.rearrangewaypoints=Waypoints rearranged
confirm.rearrangephotos=Photos rearranged
+confirm.splitsegments=%d segment splits were made
+confirm.sewsegments=%d segment joins were made
confirm.cutandmove=Selection moved
confirm.interpolate=Points added
confirm.convertnamestotimes=Waypoint names converted
-confirm.saveexif.ok1=Saved
-confirm.saveexif.ok2=photo files
+confirm.saveexif.ok=Saved %d photo files
confirm.undo.single=operation undone
confirm.undo.multi=operations undone
confirm.jpegload.single=photo was added
@@ -573,13 +602,23 @@ confirm.correlatephotos.multi=photos were correlated
confirm.rotatephoto=photo rotated
confirm.createpoint=point created
confirm.running=Running ...
-confirm.lookupsrtm1=Found
-confirm.lookupsrtm2=altitude values
+confirm.lookupsrtm=Found %d altitude values
+confirm.downloadsrtm=Downloaded %d files to the cache
+confirm.downloadsrtm.1=Downloaded %d file to the cache
+confirm.downloadsrtm.none=No files downloaded, they were already in the cache
confirm.deletefieldvalues=Field values deleted
confirm.audioload=Audio files added
confirm.correlateaudios.single=audio was correlated
confirm.correlateaudios.multi=audios were correlated
+# Tips, shown just once when appropriate
+tip.title=Tip
+tip.useamapcache=By setting up a disk cache (Settings -> Save maps to disk)\nyou can speed up the display and reduce network traffic.
+tip.learntimeparams=The results will be more accurate if you use\nTrack -> Learn time estimation parameters\non your recorded tracks.
+tip.downloadsrtm=You can speed this up by calling\nOnline -> Download SRTM tiles\nto save the data in your map cache.
+tip.usesrtmfor3d=This track doesn't have altitudes.\nYou can use the SRTM functions to get approximate\naltitudes for the 3d view.
+tip.manuallycorrelateone=By manually connecting at least one item, the time offset can be calculated for you.
+
# Buttons
button.ok=OK
button.back=Back
@@ -597,6 +636,7 @@ button.yes=Yes
button.no=No
button.yestoall=Yes to all
button.notoall=No to all
+button.always=Always
button.select=Select
button.selectall=Select all
button.selectnone=Select none
@@ -685,7 +725,6 @@ fieldname.newsegment=Segment
fieldname.custom=Custom
fieldname.prefix=Field
fieldname.distance=Distance
-fieldname.movingdistance=Moving distance
fieldname.duration=Duration
fieldname.speed=Speed
fieldname.verticalspeed=Vertical speed
@@ -720,6 +759,10 @@ units.degminsec=Deg-min-sec
units.degmin=Deg-min
units.deg=Degrees
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=and
@@ -728,6 +771,7 @@ logic.or=or
# External urls
url.googlemaps=maps.google.co.uk
wikipedia.lang=en
+openweathermap.lang=en
# Cardinals for 3d plots
cardinal.n=N
@@ -749,6 +793,8 @@ undo.deletemarked=delete points
undo.insert=insert points
undo.reverse=reverse range
undo.mergetracksegments=merge track segments
+undo.splitsegments=split track segments
+undo.sewsegments=sew track segments
undo.addtimeoffset=add time offset
undo.addaltitudeoffset=add altitude offset
undo.rearrangewaypoints=rearrange waypoints
@@ -771,10 +817,8 @@ error.save.failed=Failed to save the data to file
error.saveexif.filenotfound=Failed to find photo file
error.saveexif.cannotoverwrite1=Photo file
error.saveexif.cannotoverwrite2=is read-only and can't be overwritten. Write to copy?
-error.saveexif.failed1=Failed to save
-error.saveexif.failed2=of the images
-error.saveexif.forced1=
-error.saveexif.forced2=of the images required forcing
+error.saveexif.failed=Failed to save %d of the images
+error.saveexif.forced=%d of the images required forcing
error.load.dialogtitle=Error loading data
error.load.noread=Cannot read file
error.load.nopoints=No coordinate information found in the file
@@ -785,7 +829,7 @@ error.jpegload.dialogtitle=Error loading photos
error.jpegload.nofilesfound=No files found
error.jpegload.nojpegsfound=No jpeg files found
error.jpegload.nogpsfound=No GPS information found
-error.jpegload.exifreadfailed=Failed to read EXIF information. No EXIF information can be read\nwithout either an internal or external library.
+error.jpegload.exifreadfailed=Failed to read Exif information. No Exif information can be read\nwithout either an internal or external library.
error.audioload.nofilesfound=No audio clips found
error.gpsload.unknown=Unknown error
error.undofailed.title=Undo failed
@@ -811,3 +855,6 @@ error.cache.empty=The tile cache directory is empty
error.cache.cannotdelete=No tiles could be deleted
error.interpolate.invalidparameter=The number of points must be between 1 and 1000
error.learnestimationparams.failed=Cannot learn the parameters from this track.\nTry loading more tracks.
+error.tracksplit.nosplit=The track could not be split
+error.downloadsrtm.nocache=The files could not be saved.\nPlease check the disk cache.
+error.sewsegments.nothingdone=No segments could be sewn together.\nThere are now %d segments in the track.
diff --git a/tim/prune/lang/prune-texts_en_US.properties b/tim/prune/lang/prune-texts_en_US.properties
index 928d027..a28dd50 100644
--- a/tim/prune/lang/prune-texts_en_US.properties
+++ b/tim/prune/lang/prune-texts_en_US.properties
@@ -8,6 +8,7 @@ function.setcolours=Set colors
dialog.exportkml.trackcolour=Track color
dialog.saveconfig.prune.languagecode=Language code (EN_US)
dialog.setcolours.intro=Click on a color patch to change the color
+dialog.colourchooser.title=Choose color
# Measurement units
units.metres=Meters
diff --git a/tim/prune/lang/prune-texts_es.properties b/tim/prune/lang/prune-texts_es.properties
index ffe8f1f..491134d 100644
--- a/tim/prune/lang/prune-texts_es.properties
+++ b/tim/prune/lang/prune-texts_es.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=Cargar fotos
menu.file.recentfiles=Archivos recientes
menu.file.save=Guardar
menu.file.exit=Salir
+menu.online=Online
menu.track=Track
menu.track.undo=Deshacer
menu.track.clearundo=Despejar la lista de deshacer
@@ -23,7 +24,7 @@ menu.range.start=Fijar comienzo
menu.range.end=Fijar final
menu.range.average=Crear punto a la media del rango
menu.range.reverse=Invertir rango
-menu.range.mergetracksegments=Unir los segmentos de track
+menu.range.mergetracksegments=Unir los segmentos del track
menu.range.cutandmove=Cortar y mover selecci\u00f3n
menu.point=Punto
menu.point.editpoint=Editar punto
@@ -49,7 +50,7 @@ menu.map.zoomout=Reducir zoom
menu.map.zoomfull=Mostrar todo
menu.map.newpoint=Crear un punto nuevo
menu.map.drawpoints=Crear series de puntos
-menu.map.connect=Conectar puntos de track
+menu.map.connect=Conectar puntos del track
menu.map.autopan=Posicionar autom\u00e1ticamente
menu.map.showmap=Mostrar el mapa
menu.map.showscalebar=Mostrar barra de escala
@@ -57,6 +58,7 @@ menu.map.editmode=Editar puntos
# Alt keys for menus
altkey.menu.file=A
+altkey.menu.online=O
altkey.menu.track=T
altkey.menu.range=R
altkey.menu.point=U
@@ -88,6 +90,7 @@ function.exportimage=Exportar imagen
function.editwaypointname=Editar nombre de waypoint
function.compress=Comprimir track
function.deleterange=Eliminar rango
+function.croptrack=Truncar track
function.interpolate=Interpolar puntos
function.addtimeoffset=A\u00f1adir compensar tiempo
function.addaltitudeoffset=A\u00f1adir compensar altitud
@@ -99,11 +102,13 @@ function.charts=Diagramas
function.show3d=Mostrar en 3-D
function.distances=Distancias
function.fullrangedetails=Detalles adicionales de rango
+function.estimatetime=Estimar duraci\u00f3n
function.setmapbg=Configurar fondo de mapa
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.downloadsrtm=Descargar datos de SRTM
function.getwikipedia=Obtener art\u00edculos de Wikipedia cercanos
function.searchwikipedianames=Buscar en Wikipedia por nombre
function.downloadosm=Descargar datos OSM del \u00e1rea
@@ -132,6 +137,7 @@ function.checkversion=Buscar una nueva versi\u00f3n
function.saveconfig=Guardar preferencias
function.diskcache=Guardar mapas en disco
function.managetilecache=Administrar cache de mapas
+function.getweatherforecast=Obtener pron\u00f3stico del tiempo
# Dialogs
dialog.exit.confirm.title=Salir de GpsPrune
@@ -179,10 +185,19 @@ dialog.gpssend.sendtracks=Enviar tracks
dialog.gpssend.trackname=Nombre del track
dialog.gpsbabel.filters=Filtros
dialog.addfilter.title=A\u00f1adir filtro
+dialog.gpsbabel.filter.discard=Desechar
dialog.gpsbabel.filter.simplify=Simplificar
dialog.gpsbabel.filter.distance=Distancia
dialog.gpsbabel.filter.interpolate=Interpolar
+dialog.gpsbabel.filter.discard.intro=Desechar puntos si
+dialog.gpsbabel.filter.discard.hdop=Hdop >
+dialog.gpsbabel.filter.discard.vdop=Vdop >
dialog.gpsbabel.filter.discard.numsats=N\u00famero de sat\u00e9lites <
+dialog.gpsbabel.filter.simplify.maxpoints=Numero de puntos <
+dialog.gpsbabel.filter.distance.distance=Si distancia <
+dialog.gpsbabel.filter.distance.time=y differencia horaria <
+dialog.gpsbabel.filter.interpolate.distance=Si distancia >
+dialog.gpsbabel.filter.interpolate.time=o differencia horaria >
dialog.saveoptions.title=Guardar archivo
dialog.save.fieldstosave=Campos a guardar
dialog.save.table.field=Campo
@@ -210,7 +225,8 @@ dialog.exportgpx.copysource=Copiar la fuente
dialog.exportgpx.encoding=Codificaci\u00f3n
dialog.exportgpx.encoding.system=Sistema
dialog.exportgpx.encoding.utf8=UTF-8
-dialog.exportpov.text=Introdzca los Parametros para exportar
+dialog.3d.useterrain=Terreno
+dialog.exportpov.text=Introduca los par\u00e1metros para exportar
dialog.exportpov.font=Fuente
dialog.exportpov.camerax=C\u00e1mara X
dialog.exportpov.cameray=C\u00e1mara Y
@@ -219,15 +235,21 @@ dialog.exportpov.modelstyle=Estilo
dialog.exportpov.ballsandsticks=Balas en palos
dialog.exportpov.tubesandwalls=Tubos y paredes
dialog.3d.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.baseimage.title=Imagen de mapa
+dialog.baseimage.mapsource=Proveedor de mapas
+dialog.baseimage.useimage=Usar imagen
dialog.baseimage.zoom=Zoom
+dialog.baseimage.incomplete=Imagen incompleta
dialog.baseimage.tiles=Recuadros
dialog.baseimage.size=Tama\u00f1o de la imagen
dialog.exportsvg.text=Seleccione los par\u00e1metros para exportar a SVG
dialog.exportsvg.phi=\u00c1ngulo de azimuth \u03d5
dialog.exportsvg.theta=\u00c1ngulo de elevaci\u00f3n
dialog.exportsvg.gradients=Usar degradado para sombras
+dialog.exportimage.drawtrack=Dibujar track
+dialog.exportimage.drawtrackpoints=Dibujar puntos del track
dialog.pointtype.desc=Salvar los siguientes tipos de puntos:
-dialog.pointtype.track=Puntos de track
+dialog.pointtype.track=Puntos del track
dialog.pointtype.waypoint=Waypoints
dialog.pointtype.photo=Puntos de foto
dialog.pointtype.audio=Puntos de audio
@@ -242,10 +264,11 @@ 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=\u00bfEsta seguro que desea despejar la lista de deshacer?, �se perder\u00e1 toda la informaci\u00f3n!
+dialog.clearundo.text=\u00bfEsta seguro que desea despejar la lista de deshacer?, \u00a1se perder\u00e1 toda la informaci\u00f3n!
dialog.pointedit.title=Editar punto
dialog.pointedit.intro=Seleccione cada campo para modificar el valor
dialog.pointedit.table.field=Campo
+dialog.pointedit.nofield=Ning\u00fan campo seleccionado
dialog.pointedit.table.value=Valor
dialog.pointnameedit.name=Nombre de waypoint
dialog.pointnameedit.uppercase=May\u00fasculas
@@ -289,7 +312,10 @@ dialog.fullrangedetails.intro=Aqui estan los detalles para la selecci\u00f3n de
dialog.estimatetime.details=Detalles
dialog.estimatetime.climb=Ascenso
dialog.estimatetime.descent=Descenso
+dialog.estimatetime.parameters=Par\u00e1metros
dialog.estimatetime.results=Resultados
+dialog.estimatetime.results.estimatedtime=Duraci\u00f3n estimada
+dialog.estimatetime.results.actualtime=Duraci\u00f3n real
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
@@ -325,7 +351,6 @@ dialog.correlate.photoselect.intro=Seleccione una de estas fotos correlacionadas
dialog.correlate.select.photoname=Nombre de la foto
dialog.correlate.select.timediff=Diferencia de tiempo
dialog.correlate.select.photolater=Foto m\u00e1s adelante
-dialog.correlate.options.tip=Sugerencia: Correlacionando al menos una foto manualmente, el margen de tiempo se calcula autom\u00e1ticamente.
dialog.correlate.options.intro=Seleccionar las opciones para correlaci\u00f3n autom\u00e1tica
dialog.correlate.options.offsetpanel=Margen de tiempo
dialog.correlate.options.offset=Margen
@@ -368,6 +393,7 @@ dialog.compress.duplicates.title=Eliminar duplicados
dialog.compress.douglaspeucker.title=Compresion Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=Factor de extensi\u00f3n
dialog.compress.summarylabel=Puntos para eliminar
+dialog.compress.confirm=%d puntos marcados. \u00bfDesea eliminar los puntos?
dialog.compress.confirmnone=Ning\u00fan punto marcado
dialog.deletemarked.nonefound=Ning\u00fan punto eliminado
dialog.pastecoordinates.desc=Ingresar o pegar las coordenadas aqu\u00ed
@@ -408,7 +434,7 @@ 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.uptodate=Esta usted utilizando la \u00faltima versi\u00f3n de GpsPrune
-dialog.checkversion.newversion1=�Una nueva versi\u00f3n de GpsPrune est\u00e1 disponible! La \u00faltima es ahora la versi\u00f3n
+dialog.checkversion.newversion1=\u00a1Una nueva versi\u00f3n de GpsPrune 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=.
@@ -474,12 +500,25 @@ dialog.diskcache.tileset.multiple=varios
dialog.diskcache.deleteold=Borrar recuadros antiguos
dialog.diskcache.maximumage=Edad m\u00e1xima (dias)
dialog.diskcache.deleteall=Borrar todos los recuadros
-dialog.diskcache.deleted1=Borrado
-dialog.diskcache.deleted2=Archivos del cache
+dialog.diskcache.deleted=Borrado %d archivos del cache
dialog.deletefieldvalues.intro=Seleccionar el campo a eliminar para el rango actual
+dialog.deletefieldvalues.nofields=No hay campos a eliminar para el rango actual
dialog.setlinewidth.text=Introduzca la anchura de las l\u00edneas a dibujar para los recorridos (1-4)
dialog.downloadosm.desc=Confirmar la descarga de datos en bruto de OSM para el \u00e1rea especificada.
dialog.searchwikipedianames.search=Buscar:
+dialog.weather.day.now=Tiempo actual
+dialog.weather.day.today=Hoy
+dialog.weather.day.tomorrow=Ma\u00f1ana
+dialog.weather.day.monday=Lunes
+dialog.weather.day.tuesday=Martes
+dialog.weather.day.wednesday=Mi\u00e9rcoles
+dialog.weather.day.thursday=Jueves
+dialog.weather.day.friday=Viernes
+dialog.weather.day.saturday=S\u00e1bado
+dialog.weather.day.sunday=Domingo
+dialog.weather.wind=Viento
+dialog.weather.temp=Temp
+dialog.weather.humidity=Humedad
# 3d window
dialog.3d.title=GpsPrune vista 3-D
@@ -501,8 +540,7 @@ confirm.rearrangephotos=Fotos reacomodadas
confirm.cutandmove=Mover Selecci\u00f3n
confirm.interpolate=Puntos insertados
confirm.convertnamestotimes=Nombres de "waypoint" convertidos
-confirm.saveexif.ok1=Guardado
-confirm.saveexif.ok2=fotos
+confirm.saveexif.ok=Guardado %d fotos
confirm.undo.single=operaci\u00f3n deshecha
confirm.undo.multi=operaci\u00f3n(es) deshechas(s)
confirm.jpegload.single=Foto incluida
@@ -516,13 +554,16 @@ confirm.correlatephotos.multi=fotos fueron correlacionadas
confirm.createpoint=punto creado
confirm.rotatephoto=foto rotada
confirm.running=Trabajando ...
-confirm.lookupsrtm1=Encontrados
-confirm.lookupsrtm2=valor de altitud para la funci\u00f3n de b\u00fasqueda SRTM
+confirm.lookupsrtm=Encontrados %d valor de altitud para la funci\u00f3n de b\u00fasqueda SRTM
confirm.deletefieldvalues=Valores del campo eliminados
confirm.audioload=A\u00f1adidos archivos de audio
confirm.correlateaudios.single=El audio fue correlacionado
confirm.correlateaudios.multi=Los audios fueron correlacionados
+# Tips
+tip.title=Sugerencia
+tip.manuallycorrelateone=Correlacionando al menos una foto manualmente, el margen de tiempo se calcula autom\u00e1ticamente.
+
# Buttons
button.ok=Aceptar
button.back=Anterior
@@ -572,6 +613,7 @@ filetype.audio=Archivos MP3, OGG, WAV
display.nodata=Ning\u00fan dato cargado
display.noaltitudes=Los datos del track no incluyen altitudes
display.notimestamps=Los datos de recorrido no incluyen marcas de tiempo
+display.novalues=Los datos de recorrido no incluyen valores para este campo
details.trackdetails=Detalles del track
details.notrack=Ning\u00fan track cargado
details.track.points=Puntos
@@ -627,7 +669,6 @@ fieldname.newsegment=Segmento
fieldname.custom=Personalizado
fieldname.prefix=Campo
fieldname.distance=Distancia
-fieldname.movingdistance=Distancia en movimiento
fieldname.duration=Duraci\u00f3n
fieldname.speed=Velocidad
fieldname.verticalspeed=Velocidad vertical
@@ -662,6 +703,10 @@ units.degminsec=Gra-min-seg
units.degmin=Gra-min
units.deg=Grados
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=y
@@ -670,6 +715,7 @@ logic.or=o
# External urls
url.googlemaps=maps.google.es
wikipedia.lang=es
+openweathermap.lang=sp
# Cardinals for 3d plots
cardinal.n=N
@@ -686,10 +732,11 @@ undo.deletepoint=eliminar punto
undo.removephoto=eliminar foto
undo.removeaudio=eliminar archivos de audio
undo.deleterange=eliminar rango
+undo.croptrack=truncar track
undo.deletemarked=eliminar puntos
undo.insert=insertar puntos
undo.reverse=invertir rango
-undo.mergetracksegments=unir los segmentos de track
+undo.mergetracksegments=unir los segmentos del track
undo.addtimeoffset=a\u00f1adir margen de tiempo
undo.addaltitudeoffset=a\u00f1adir margen de altitud
undo.rearrangewaypoints=reordenar waypoints
@@ -712,10 +759,8 @@ 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=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.saveexif.failed=Fall\u00f3 al guardar %d de las im\u00e1genes
+error.saveexif.forced=%d 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=No se encuentra ninguna informaci\u00f3n de coordenadas en el archivo
@@ -726,7 +771,7 @@ error.jpegload.dialogtitle=Error cargando fotos
error.jpegload.nofilesfound=No se encuentra ning\u00fan archivo
error.jpegload.nojpegsfound=No se encuentra ning\u00fan archivo jpeg
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.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.audioload.nofilesfound=No se encontraron archivos de audio
error.gpsload.unknown=Error desconocido
error.undofailed.title=Fallo al deshacer
diff --git a/tim/prune/lang/prune-texts_fa.properties b/tim/prune/lang/prune-texts_fa.properties
new file mode 100644
index 0000000..960330d
--- /dev/null
+++ b/tim/prune/lang/prune-texts_fa.properties
@@ -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
+function.deleterange=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0686\u064a\u0646\u0634
+function.interpolate=\u062f\u0631\u0648\u0646\u064a\u0627\u0628\u06cc
+menu.range.average=\u0645\u06cc\u0627\u0646\u06af\u064a\u0646 \u0627\u0646\u062a\u062e\u0627\u0628 \u0634\u062f\u0647 \u0647\u0627
+menu.range.reverse=\u0686\u064a\u0646\u0634 \u0645\u0639\u06a9\u0648\u0633
+menu.range.mergetracksegments=\u0627\u062a\u0635\u0627\u0644 \u0642\u0633\u0645\u062a \u0647\u0627\u06cc \u0645\u0633\u064a\u0631
+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
+function.connecttopoint=\u0627\u062a\u0635\u0627\u0644 \u0628\u0647 \u0646\u0642\u0637\u0647
+function.disconnectfrompoint=\u0642\u0637\u0639 \u0627\u062a\u0635\u0627\u0644 \u0627\u0632 \u0646\u0642\u0637\u0647
+function.removephoto=\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
diff --git a/tim/prune/lang/prune-texts_fr.properties b/tim/prune/lang/prune-texts_fr.properties
index 1cfdf1e..477b832 100644
--- a/tim/prune/lang/prune-texts_fr.properties
+++ b/tim/prune/lang/prune-texts_fr.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=Ajouter photos
menu.file.recentfiles=Fichiers r\u00e9cents
menu.file.save=Enregistrer
menu.file.exit=Quitter
+menu.online=En ligne
menu.track=Trace
menu.track.undo=Annuler
menu.track.clearundo=Purger la liste d'annulation
@@ -57,6 +58,7 @@ menu.map.editmode=Mode \u00e9dition
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=L
altkey.menu.track=T
altkey.menu.range=E
altkey.menu.point=P
@@ -103,9 +105,12 @@ function.fullrangedetails=Montrer tous les d\u00e9tails
function.estimatetime=Temps estim\u00e9
function.setmapbg=D\u00e9finir le fond de carte
function.setpaths=D\u00e9finir les chemins des programmes
+function.splitsegments=S\u00e9pare les segments
+function.sewsegments=R\u00e9unis les segments
function.getgpsies=R\u00e9cup\u00e9rer les traces Gpsies
function.uploadgpsies=T\u00e9l\u00e9charger la trace sur Gpsies
function.lookupsrtm=R\u00e9cup\u00e9rer les altitudes depuis SRTM
+function.downloadsrtm=T\u00e9l\u00e9charger les donn\u00e9es SRTM
function.getwikipedia=Obtenir les articles de Wikip\u00e9dia \u00e0 proximit\u00e9
function.searchwikipedianames=Rechercher dans Wikip\u00e9dia par nom
function.downloadosm=T\u00e9l\u00e9charger les donn\u00e9es OSM de la zone
@@ -134,6 +139,7 @@ function.checkversion=Chercher une mise \u00e0 jour
function.saveconfig=Enregistrer les pr\u00e9f\u00e9rences
function.diskcache=Enregistrer les cartes sur le disque
function.managetilecache=Gestion du cache des dalles de cartes
+function.getweatherforecast=Obtenir une pr\u00e9vision m\u00e9t\u00e9orologique
# Dialogs
dialog.exit.confirm.title=Quitter GpsPrune
@@ -162,6 +168,8 @@ dialog.openoptions.deliminfo.norecords=Pas d'enregistrements
dialog.openoptions.altitudeunits=Unit\u00e9s d'altitude
dialog.openoptions.speedunits=Unit\u00e9s de vitesse
dialog.openoptions.vertspeedunits=Unit\u00e9s de vitesse verticale
+dialog.openoptions.vspeed.positiveup=Vitesse positive en montant
+dialog.openoptions.vspeed.positivedown=Vitesse positive en bas
dialog.open.contentsdoubled=Ce fichier contient deux copies de chaque point,\nune fois comme waypoint, une autre comme point de trace.
dialog.selecttracks.intro=S\u00e9lectionner la ou les traces \u00e0 charger
dialog.selecttracks.noname=Sans titre
@@ -179,6 +187,19 @@ dialog.gpsload.save=Enregistrer dans un fichier
dialog.gpssend.sendwaypoints=Envoyer les waypoints
dialog.gpssend.sendtracks=Envoyer les traces
dialog.gpssend.trackname=Nom de la trace
+dialog.gpsbabel.filters=Filtres
+dialog.addfilter.title=Ajouter un filtre
+dialog.gpsbabel.filter.discard=Jeter
+dialog.gpsbabel.filter.simplify=Simplifier
+dialog.gpsbabel.filter.interpolate=Interpoler
+dialog.gpsbabel.filter.discard.intro=Jeter les points si
+dialog.gpsbabel.filter.discard.hdop=Hdop >
+dialog.gpsbabel.filter.discard.vdop=Vdop >
+dialog.gpsbabel.filter.distance=Distance
+dialog.gpsbabel.filter.distance.distance=Si la distance <
+dialog.gpsbabel.filter.distance.time=et difference de temps <
+dialog.gpsbabel.filter.interpolate.distance=Si la distance >
+dialog.gpsbabel.filter.interpolate.time=ou difference de temps >
dialog.saveoptions.title=Enregistrer le fichier
dialog.save.fieldstosave=Champs \u00e0 enregistrer
dialog.save.table.field=Champ
@@ -197,6 +218,8 @@ dialog.exportkml.kmz=Compresser au format kmz
dialog.exportkml.exportimages=Exporter les vignettes au format kmz
dialog.exportkml.imagesize=Taille des images
dialog.exportkml.trackcolour=Couleur de la trace
+dialog.exportkml.standardkml=KML standard
+dialog.exportkml.extendedkml=KML enrichi avec l'heure
dialog.exportgpx.name=Nom
dialog.exportgpx.desc=L\u00e9gende
dialog.exportgpx.includetimestamps=Inclure l'heure pour chaque point
@@ -243,8 +266,9 @@ dialog.undo.none.text=Pas d'op\u00e9ration \u00e0 annuler !
dialog.clearundo.title=Purger la liste d'annulation
dialog.clearundo.text=\u00cates-vous s\u00fbr de vouloir effacer la liste d'annulation ?\nToutes les informations d'annulation seront perdues !
dialog.pointedit.title=\u00c9diter le point
-dialog.pointedit.intro=S\u00e9lectionner chaque champ pour changer la valeur
+dialog.pointedit.intro=S\u00e9lectionner chaque champ pour voire et changer la valeur
dialog.pointedit.table.field=Champ
+dialog.pointedit.nofield=Aucun champ choisi
dialog.pointedit.table.value=Valeur
dialog.pointnameedit.name=Nom de waypoint
dialog.pointnameedit.uppercase=CASSE MAJUSCULES
@@ -324,7 +348,6 @@ dialog.correlate.photoselect.intro=S\u00e9lectionner une de ces photos corr\u00e
dialog.correlate.select.photoname=Nom de la photo
dialog.correlate.select.timediff=Diff\u00e9rence de temps
dialog.correlate.select.photolater=Photo prise plus tard
-dialog.correlate.options.tip=Astuce : En corr\u00e9lant manuellement au moins une photo, le d\u00e9calage de temps peut \u00eatre calcul\u00e9 pour vous.
dialog.correlate.options.intro=S\u00e9lectionner les options pour la corr\u00e9lation automatique
dialog.correlate.options.offsetpanel=D\u00e9calage de temps
dialog.correlate.options.offset=D\u00e9calage
@@ -367,8 +390,7 @@ dialog.compress.duplicates.title=Suppression des doublons
dialog.compress.douglaspeucker.title=Compression Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=Taille du voisinage
dialog.compress.summarylabel=Points \u00e0 supprimer
-dialog.compress.confirm1=
-dialog.compress.confirm2=point(s) marqu\u00e9(s).\nTrace->Supprimer les points marqu\u00e9s pour les supprimer
+dialog.compress.confirm=%d point(s) marqu\u00e9(s).\nSupprimer les points?
dialog.compress.confirmnone=Pas de points marqu\u00e9s
dialog.deletemarked.nonefound=Pas de donn\u00e9es \u00e0 effacer
dialog.pastecoordinates.desc=Entrez ou collez les coordonn\u00e9es ici
@@ -415,7 +437,7 @@ dialog.checkversion.releasedate1=La nouvelle version est sortie le
dialog.checkversion.releasedate2=.
dialog.checkversion.download=Pour t\u00e9l\u00e9charger la nouvelle version, aller \u00e0 http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=Vous pouvez utiliser ces raccourcis clavier \u00e0 la place de la souris
-dialog.keys.keylist=<table><tr><td>Touches-fl\u00e8ches</td><td>Faire d\u00e9filer la carte horizontalement et verticalement</td></tr><tr><td>Ctrl + gauche, Ctrl + droite</td><td>Choisir le point pr\u00e9c\u00e9dent ou suivant</td></tr><tr><td>Ctrl + haut, Ctrl + bas</td><td>Zoomer, s'\u00e9loigner</td></tr><tr><td>Suppr</td><td>Effacer le point courant</td></tr></table>
+dialog.keys.keylist=<table><tr><td>Touches-fl\u00e8ches</td><td>Faire d\u00e9filer la carte horizontalement et verticalement</td></tr><tr><td>Ctrl + gauche, Ctrl + droite</td><td>Choisir le point pr\u00e9c\u00e9dent ou suivant</td></tr><tr><td>Ctrl + haut, Ctrl + bas</td><td>Zoomer, s'\u00e9loigner</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Choisir le segment pr\u00e9c\u00e9dent ou suivant</td></tr><tr><td>Ctrl + Home, End</td><td>Choisir le point premier, dernier</td></tr><tr><td>Supp [...]
dialog.keys.normalmodifier=Ctrl
dialog.keys.macmodifier=Command
dialog.saveconfig.desc=Les param\u00e8tres suivants peuvent \u00eatre sauvegard\u00e9s dans un fichier de configuration:
@@ -476,13 +498,34 @@ dialog.diskcache.tileset.multiple=multiple
dialog.diskcache.deleteold=Efface vieilles dalles
dialog.diskcache.maximumage=\u00e2ge maxi (jours)
dialog.diskcache.deleteall=Efface toutes les dalles
-dialog.diskcache.deleted1=Effac\u00e9
-dialog.diskcache.deleted2=dalles en cache
+dialog.diskcache.deleted=Effac\u00e9 %d dalles en cache
dialog.deletefieldvalues.intro=Choisir le champ \u00e0 effacer pour l'\u00e9tendue actuelle
dialog.deletefieldvalues.nofields=L'\u00e9tendue actuelle n'a pas de champs \u00e0 effacer
dialog.setlinewidth.text=Entrer l'\u00e9paisseur des lignes des traces (1-4)
dialog.downloadosm.desc=Confirmer le t\u00e9l\u00e9chargement des donn\u00e9es OSM brutes pour la zone indiqu\u00e9e :
dialog.searchwikipedianames.search=Chercher :
+dialog.weather.location=Location
+dialog.weather.update=Mise \u00e0 jour
+dialog.weather.sunrise=Lever du soleil
+dialog.weather.sunset=Coucher du soleil
+dialog.weather.temperatureunits=Temp\u00e9ratures
+dialog.weather.currentforecast=Temps actuel
+dialog.weather.dailyforecast=Pr\u00e9vision par jour
+dialog.weather.3hourlyforecast=Pr\u00e9vision par 3 heures
+dialog.weather.day.now=Temps actuel
+dialog.weather.day.today=Aujourd'hui
+dialog.weather.day.tomorrow=Demain
+dialog.weather.day.monday=Lundi
+dialog.weather.day.tuesday=Mardi
+dialog.weather.day.wednesday=Mercredi
+dialog.weather.day.thursday=Jeudi
+dialog.weather.day.friday=Vendredi
+dialog.weather.day.saturday=Samedi
+dialog.weather.day.sunday=Dimanche
+dialog.weather.wind=Vent
+dialog.weather.temp=Temp
+dialog.weather.humidity=Humidit\u00e9
+dialog.weather.creditnotice=Ces donn\u00e9es sont fournies par openweathermap.org. Consultez la page pour plus de d\u00e9tails.
# 3d window
dialog.3d.title=Vue 3D de GpsPrune
@@ -504,8 +547,7 @@ confirm.rearrangephotos=Photos r\u00e9arrang\u00e9es
confirm.cutandmove=S\u00e9lection d\u00e9plac\u00e9e
confirm.interpolate=Points ajout\u00e9s
confirm.convertnamestotimes=Noms de waypoints convertis
-confirm.saveexif.ok1=Enregistr\u00e9
-confirm.saveexif.ok2=fichiers photo
+confirm.saveexif.ok=Enregistr\u00e9 %d fichiers photo
confirm.undo.single=op\u00e9ration annul\u00e9e
confirm.undo.multi=op\u00e9rations annul\u00e9es
confirm.jpegload.single=la photo a \u00e9t\u00e9 ajout\u00e9e
@@ -519,13 +561,23 @@ confirm.correlatephotos.multi=photos ont \u00e9t\u00e9 corr\u00e9l\u00e9es
confirm.createpoint=Point cr\u00e9\u00e9
confirm.rotatephoto=Photo tourn\u00e9e
confirm.running=En cours...
-confirm.lookupsrtm1=Trouv\u00e9
-confirm.lookupsrtm2=valeurs d'altitude
+confirm.lookupsrtm=Trouv\u00e9 %d valeurs d'altitude
+confirm.downloadsrtm=%d fichiers ont \u00e9t\u00e9 t\u00e9l\u00e9charg\u00e9s
+confirm.downloadsrtm.1=%d fichier a \u00e9t\u00e9 t\u00e9l\u00e9charg\u00e9
+confirm.downloadsrtm.none=Pas de fichiers ont \u00e9t\u00e9 t\u00e9l\u00e9charg\u00e9s, ils sont d\u00e9j\u00e0 l\u00e0
confirm.deletefieldvalues=Valeurs effac\u00e9es
confirm.audioload=Fichiers audio ajout\u00e9s
confirm.correlateaudios.single=fichier audio a \u00e9t\u00e9 corr\u00e9l\u00e9
confirm.correlateaudios.multi=fichiers audio ont \u00e9t\u00e9 corr\u00e9l\u00e9s
+# Tips
+tip.title=Astuce
+tip.useamapcache=By setting up a disk cache (Pr\u00e9f\u00e9rences -> Enregistrer les cartes sur le disque)\nyou can speed up the display and reduce network traffic.
+tip.learntimeparams=The results will be more accurate if you use\nTrace -> Learn time estimation parameters\non your recorded tracks.
+tip.downloadsrtm=You can speed this up by calling\nEn ligne -> T\u00e9l\u00e9charger les donn\u00e9es SRTM\nto save the data in your map cache.
+tip.usesrtmfor3d=This track doesn't have altitudes.\nYou can use the SRTM functions to get approximate\naltitudes for the 3d view.
+tip.manuallycorrelateone=En corr\u00e9lant manuellement au moins une photo, le d\u00e9calage de temps peut \u00eatre calcul\u00e9 pour vous.
+
# Buttons
button.ok=OK
button.back=Retour
@@ -543,6 +595,7 @@ button.yes=Oui
button.no=Non
button.yestoall=Oui pour tous
button.notoall=Non pour tous
+button.always=Oui, toujours
button.select=S\u00e9lectionner
button.selectall=Tout s\u00e9lectionner
button.selectnone=Ne rien s\u00e9lectionner
@@ -557,6 +610,7 @@ button.browse=Naviguer...
button.addnew=Ajouter nouveau...
button.delete=Supprimer
button.manage=G\u00e9rer
+button.combine=Combiner
# File types
filetype.txt=Fichiers TXT
@@ -574,6 +628,7 @@ filetype.audio=Fichiers MP3, OGG, WAV
display.nodata=Pas de donn\u00e9es charg\u00e9es
display.noaltitudes=La trace ne comporte pas d'information d'altitude
display.notimestamps=La trace ne comporte pas d'information de temps
+display.novalues=La trace ne comporte pas d'information pour ce champ
details.trackdetails=D\u00e9tails de la trace
details.notrack=Pas de trace charg\u00e9e
details.track.points=Points
@@ -629,7 +684,6 @@ fieldname.newsegment=Segment
fieldname.custom=Personnalis\u00e9
fieldname.prefix=Champ
fieldname.distance=Distance
-fieldname.movingdistance=Distance continue
fieldname.duration=Dur\u00e9e
fieldname.speed=Vitesse
fieldname.verticalspeed=Vitesse verticale
@@ -672,6 +726,7 @@ logic.or=ou
# External urls
url.googlemaps=maps.google.fr
wikipedia.lang=fr
+openweathermap.lang=fr
# Cardinals for 3d plots
cardinal.n=N
@@ -693,6 +748,8 @@ undo.deletemarked=effacer les points
undo.insert=ins\u00e9rer les points
undo.reverse=inverser l'\u00e9tendue
undo.mergetracksegments=fusionner les segments de trace
+undo.splitsegments=s\u00e9parer les segments de trace
+undo.sewsegments=r\u00e9unir les segments de trace
undo.addtimeoffset=ajouter d\u00e9calage d'heure
undo.addaltitudeoffset=ajouter d\u00e9calage d'altitude
undo.rearrangewaypoints=r\u00e9arranger les waypoints
@@ -715,10 +772,8 @@ error.save.failed=\u00c9chec de l'enregistrement des donn\u00e9es dans le fichie
error.saveexif.filenotfound=Fichier photo introuvable
error.saveexif.cannotoverwrite1=Le fichier photo
error.saveexif.cannotoverwrite2=est en lecture seule et ne peut pas \u00eatre \u00e9craser. Enregistrer sur une copie ?
-error.saveexif.failed1=\u00c9chec de la sauvegarde de
-error.saveexif.failed2=images
-error.saveexif.forced1=Enregistrement forc\u00e9 pour
-error.saveexif.forced2=images
+error.saveexif.failed=\u00c9chec de la sauvegarde de %d images
+error.saveexif.forced=Enregistrement forc\u00e9 pour %d images
error.load.dialogtitle=Erreur au chargement des donn\u00e9es
error.load.noread=Fichier illisible
error.load.nopoints=Aucune coordonn\u00e9e trouv\u00e9e dans le fichier
@@ -729,7 +784,7 @@ error.jpegload.dialogtitle=Erreur au chargement des photos
error.jpegload.nofilesfound=Aucun fichier trouv\u00e9
error.jpegload.nojpegsfound=Aucun fichier jpeg trouv\u00e9
error.jpegload.nogpsfound=Aucune information GPS trouv\u00e9e
-error.jpegload.exifreadfailed=Information EXIF illisible. Aucune information EXIF ne peut \u00eatre lue\nsans une librairie interne ou externe.
+error.jpegload.exifreadfailed=Information Exif illisible. Aucune information Exif ne peut \u00eatre lue\nsans une librairie interne ou externe.
error.audioload.nofilesfound=Aucun fichier audio trouv\u00e9
error.gpsload.unknown=Erreur inconnue
error.undofailed.title=\u00c9chec de l'annulation
@@ -754,3 +809,4 @@ error.cache.notthere=Le dossier du cache n'a pas \u00e9t\u00e9 trouv\u00e9
error.cache.empty=Le dossier du cache est vide
error.cache.cannotdelete=Effacement des dalles impossible
error.interpolate.invalidparameter=Le nombre de points doit \u00eatre compris entre 1 et 1000
+error.tracksplit.nosplit=Impossible de s\u00e9parer les segments
diff --git a/tim/prune/lang/prune-texts_hu.properties b/tim/prune/lang/prune-texts_hu.properties
index ea53142..58359f8 100644
--- a/tim/prune/lang/prune-texts_hu.properties
+++ b/tim/prune/lang/prune-texts_hu.properties
@@ -1,5 +1,5 @@
# Text entries for the GpsPrune application
-# Hungarian entries thanks to Gy\u00f6rgy Ball\u00f3
+# Hungarian entries thanks to Gy\u00f6rgy Ball\u00f3 and Peter Bathory
# Menu entries
menu.file=F\u00e1jl
@@ -7,9 +7,11 @@ menu.file.addphotos=F\u00e9nyk\u00e9pek hozz\u00e1ad\u00e1sa
menu.file.recentfiles=Legut\u00f3bbi f\u00e1jlok
menu.file.save=Ment\u00e9s sz\u00f6vegk\u00e9nt
menu.file.exit=Kil\u00e9p\u00e9s
+menu.online=Online
menu.track=Nyomvonal
menu.track.undo=Visszavon\u00e1s
menu.track.clearundo=Visszavon\u00e1si lista t\u00f6rl\u00e9se
+menu.track.markrectangle=N\u00e9gyzeten bel\u00fcli pontok megjel\u00f6l\u00e9se
menu.track.deletemarked=Jel\u00f6lt pontok t\u00f6rl\u00e9se
menu.track.rearrange=\u00datpontok \u00fajrarendez\u00e9se
menu.track.rearrange.start=\u00d6sszes a f\u00e1jl elej\u00e9re
@@ -20,8 +22,6 @@ menu.range.all=Mindet kijel\u00f6l
menu.range.none=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
menu.range.start=Tartom\u00e1ny kezdet\u00e9nek be\u00e1ll\u00edt\u00e1sa
menu.range.end=Tartom\u00e1ny v\u00e9g\u00e9nek be\u00e1ll\u00edt\u00e1sa
-function.deleterange=Tartom\u00e1ny t\u00f6rl\u00e9se
-function.interpolate=Pontok interpol\u00e1l\u00e1sa
menu.range.average=Kijel\u00f6l\u00e9s \u00e1tlaga
menu.range.reverse=Tartom\u00e1ny megford\u00edt\u00e1sa
menu.range.mergetracksegments=Nyomvonalszakaszok egyes\u00edt\u00e9se
@@ -41,7 +41,7 @@ menu.view.browser.mapquest=Mapquest
menu.view.browser.yahoo=Yahoo! Maps
menu.view.browser.bing=Bing Maps
menu.settings=Be\u00e1ll\u00edt\u00e1sok
-menu.settings.onlinemode=T\u00e9rk\u00e9pek bet\u00f6lt\u00e9se az internetr\u0151l
+menu.settings.onlinemode=T\u00e9rk\u00e9pek bet\u00f6lt\u00e9se internetr\u0151l
menu.settings.autosave=Be\u00e1ll\u00edt\u00e1sok automatikus ment\u00e9se kil\u00e9p\u00e9skor
menu.help=S\u00fag\u00f3
# Popup menu for map
@@ -54,9 +54,11 @@ menu.map.connect=Nyompontok \u00f6sszek\u00f6t\u00e9se
menu.map.autopan=Automatikus mozgat\u00e1s
menu.map.showmap=T\u00e9rk\u00e9p megjelen\u00edt\u00e9se
menu.map.showscalebar=M\u00e9retar\u00e1ny megjelen\u00edt\u00e9se
+menu.map.editmode=Szerkeszt\u00e9s m\u00f3d
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=O
altkey.menu.track=V
altkey.menu.range=T
altkey.menu.point=P
@@ -84,8 +86,12 @@ function.exportkml=Export\u00e1l\u00e1s KML-be
function.exportgpx=Export\u00e1l\u00e1s GPX-be
function.exportpov=Export\u00e1l\u00e1s POV-ba
function.exportsvg=Export\u00e1l\u00e1s SVG-be
+function.exportimage=Export\u00e1l\u00e1s k\u00e9pbe
function.editwaypointname=\u00datpont nev\u00e9nek szerkeszt\u00e9se
function.compress=Nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se
+function.deleterange=Tartom\u00e1ny t\u00f6rl\u00e9se
+function.croptrack=Nyomvonal k\u00f6rbev\u00e1g\u00e1sa
+function.interpolate=Pontok interpol\u00e1l\u00e1sa
function.addtimeoffset=Id\u0151eltol\u00e1s hozz\u00e1ad\u00e1sa
function.addaltitudeoffset=Magass\u00e1geltol\u00e1s hozz\u00e1ad\u00e1sa
function.convertnamestotimes=\u00datpontok neveinek konvert\u00e1l\u00e1sa id\u0151pontokk\u00e1
@@ -96,11 +102,16 @@ function.charts=Diagramok
function.show3d=3D n\u00e9zet
function.distances=T\u00e1vols\u00e1gok
function.fullrangedetails=Teljes tartom\u00e1ny r\u00e9szletei
+function.estimatetime=Becs\u00fclt id\u0151
+function.learnestimationparams=Id\u0151becsl\u00e9s tanul\u00e1s\u00e1nak param\u00e9terei
function.setmapbg=H\u00e1tt\u00e9rk\u00e9p be\u00e1ll\u00edt\u00e1sa
function.setpaths=Program\u00fatvonalak be\u00e1ll\u00edt\u00e1sa
+function.splitsegments=Nyomvonal kett\u00e9v\u00e1g\u00e1sa szakaszokk\u00e1
+function.sewsegments=Nyomvonalszakaszok \u00f6sszevon\u00e1sa
function.getgpsies=Gpsies nyomvonalak let\u00f6lt\u00e9se
function.uploadgpsies=Nyomvonal felt\u00f6lt\u00e9se Gpsiesra
function.lookupsrtm=Magass\u00e1gok let\u00f6lt\u00e9se SRTM-r\u0151l
+function.downloadsrtm=SRTM csemp\u00e9k let\u00f6lt\u00e9se
function.getwikipedia=K\u00f6zeli Wikip\u00e9dia sz\u00f3cikkek let\u00f6lt\u00e9se
function.searchwikipedianames=Keres\u00e9s a Wikip\u00e9di\u00e1ban n\u00e9v szerint
function.downloadosm=OSM adatok let\u00f6lt\u00e9se a ter\u00fcletr\u0151l
@@ -129,6 +140,7 @@ function.checkversion=\u00daj verzi\u00f3 keres\u00e9se
function.saveconfig=Be\u00e1ll\u00edt\u00e1sok ment\u00e9se
function.diskcache=T\u00e9rk\u00e9pek ment\u00e9se lemezre
function.managetilecache=Csempegyors\u00edt\u00f3t\u00e1r kezel\u00e9se
+function.getweatherforecast=Id\u0151j\u00e1r\u00e1s el\u0151rejelz\u00e9s
# Dialogs
dialog.exit.confirm.title=Kil\u00e9p\u00e9s a GpsPrune-b\u00f3l
@@ -136,9 +148,10 @@ dialog.exit.confirm.text=Az adatok nincsenek elmentve. Biztos benne, hogy kil\u0
dialog.openappend.title=Hozz\u00e1f\u0171z\u00e9s a megl\u00e9v\u0151 adatokhoz
dialog.openappend.text=Hozz\u00e1f\u0171zi ezeket az adatokat a m\u00e1r bet\u00f6lt\u00f6tt adatokhoz?
dialog.deletepoint.title=Pont t\u00f6rl\u00e9se
-dialog.deletepoint.deletephoto=T\u00f6rli a f\u00e9nyk\u00e9pet, amely ehhez a ponthoz tartozik?
+dialog.deletepoint.deletephoto=T\u00f6rli a ponthoz tartoz\u00f3 f\u00e9nyk\u00e9pet?
dialog.deletephoto.title=F\u00e9nyk\u00e9p t\u00f6rl\u00e9se
-dialog.deletephoto.deletepoint=T\u00f6rli a pontot, amely ehhez a f\u00e9nyk\u00e9phez tartozik?
+dialog.deletephoto.deletepoint=T\u00f6rli a f\u00e9nyk\u00e9phez tartoz\u00f3 pontot?
+dialog.deleteaudio.deletepoint=T\u00f6rli a hangf\u00e1jlhoz tartoz\u00f3 pontot?
dialog.openoptions.title=Be\u00e1ll\u00edt\u00e1sok megnyit\u00e1sa
dialog.openoptions.filesnippet=F\u00e1jl kivonata
dialog.load.table.field=Mez\u0151
@@ -153,7 +166,11 @@ dialog.delimiter.other=Egy\u00e9b
dialog.openoptions.deliminfo.records=rekord
dialog.openoptions.deliminfo.fields=mez\u0151vel
dialog.openoptions.deliminfo.norecords=Nincsenek rekordok
-dialog.openoptions.altitudeunits=Magass\u00e1g egys\u00e9ge
+dialog.openoptions.altitudeunits=Magass\u00e1g m\u00e9rt\u00e9kegys\u00e9ge
+dialog.openoptions.speedunits=Sebess\u00e9g m\u00e9rt\u00e9kegys\u00e9ge
+dialog.openoptions.vertspeedunits=F\u00fcgg\u0151leges sebess\u00e9g m\u00e9rt\u00e9kegys\u00e9ge
+dialog.openoptions.vspeed.positiveup=Pozit\u00edv sebess\u00e9g m\u00e9rt\u00e9kegys\u00e9ge
+dialog.openoptions.vspeed.positivedown=Pozit\u00edv sebess\u00e9g lefel\u00e9
dialog.open.contentsdoubled=Ez a f\u00e1jl minden egyes pont k\u00e9t p\u00e9ld\u00e1ny\u00e1t tartalmazza,\negyszer mint \u00fatpont, m\u00e1sodszor mint nyompont.
dialog.selecttracks.intro=Nyomvonal vagy nyomvonalak kiv\u00e1laszt\u00e1sa bet\u00f6lt\u00e9shez
dialog.selecttracks.noname=N\u00e9vtelen
@@ -171,14 +188,38 @@ dialog.gpsload.save=Ment\u00e9s f\u00e1jlba
dialog.gpssend.sendwaypoints=\u00datpontok k\u00fcld\u00e9se
dialog.gpssend.sendtracks=Nyomvonalak k\u00fcld\u00e9se
dialog.gpssend.trackname=Nyomvonal neve
+dialog.gpsbabel.filters=Sz\u0171r\u0151k
+dialog.addfilter.title=Sz\u0171r\u0151 hozz\u00e1ad\u00e1sa
+dialog.gpsbabel.filter.discard=Mell\u0151z\u00e9s
+dialog.gpsbabel.filter.simplify=Egyszer\u0171s\u00edt\u00e9s
+dialog.gpsbabel.filter.distance=T\u00e1vols\u00e1g
+dialog.gpsbabel.filter.interpolate=Interpol\u00e1l\u00e1s
+dialog.gpsbabel.filter.discard.intro=Pontok kihagy\u00e1sa, ha
+dialog.gpsbabel.filter.discard.hdop=Hdop >
+dialog.gpsbabel.filter.discard.vdop=Hdop >
+dialog.gpsbabel.filter.discard.numsats=M\u0171holdak sz\u00e1ma <
+dialog.gpsbabel.filter.discard.nofix=Fix n\u00e9lk\u00fcli pontok
+dialog.gpsbabel.filter.discard.unknownfix=Ismeretlen fix-es pontok
+dialog.gpsbabel.filter.simplify.intro=Pontok elt\u00e1vol\u00edt\u00e1sa am\u00edg
+dialog.gpsbabel.filter.simplify.maxpoints=Pontok sz\u00e1ma <
+dialog.gpsbabel.filter.simplify.maxerror=vagy hiba m\u00e9rete <
+dialog.gpsbabel.filter.simplify.crosstrack=lesodr\u00f3d\u00e1s
+dialog.gpsbabel.filter.simplify.length=t\u00e1vols\u00e1gk\u00fcl\u00f6nbs\u00e9g
+dialog.gpsbabel.filter.simplify.relative=relat\u00edv hdop
+dialog.gpsbabel.filter.distance.intro=Pont elt\u00e1vol\u00edt\u00e1sa, ha k\u00f6zel van egy kor\u00e1bbi ponthoz
+dialog.gpsbabel.filter.distance.distance=Ha a t\u00e1vols\u00e1g <
+dialog.gpsbabel.filter.distance.time=\u00e9s az id\u0151elt\u00e9r\u00e9s <
+dialog.gpsbabel.filter.interpolate.intro=Extra pontok beilleszt\u00e9se az \u00fatpontok k\u00f6z\u00e9
+dialog.gpsbabel.filter.interpolate.distance=Ha a t\u00e1vols\u00e1g >
+dialog.gpsbabel.filter.interpolate.time=vagy az id\u0151elt\u00e9r\u00e9s >
dialog.saveoptions.title=F\u00e1jl ment\u00e9se
dialog.save.fieldstosave=Mentend\u0151 mez\u0151k
dialog.save.table.field=Mez\u0151
dialog.save.table.hasdata=Tartalmaz adatot
dialog.save.table.save=Ment\u00e9s
dialog.save.headerrow=Fejl\u00e9csor a kimenetbe
-dialog.save.coordinateunits=Koordin\u00e1ta egys\u00e9ge
-dialog.save.altitudeunits=Magass\u00e1g egys\u00e9ge
+dialog.save.coordinateunits=Koordin\u00e1ta form\u00e1tuma
+dialog.save.altitudeunits=Magass\u00e1g m\u00e9rt\u00e9kegys\u00e9ge
dialog.save.timestampformat=Id\u0151b\u00e9lyeg form\u00e1tuma
dialog.save.overwrite.title=A f\u00e1jl m\u00e1r l\u00e9tezik
dialog.save.overwrite.text=Ez a f\u00e1jl m\u00e1r l\u00e9tezik. Biztos benne, hogy fel\u00fcl\u00edrja a f\u00e1jlt?
@@ -187,7 +228,10 @@ dialog.exportkml.text=C\u00edm az adatokhoz
dialog.exportkml.altitude=Abszol\u00fat magass\u00e1gok (rep\u00fcl\u00e9shez)
dialog.exportkml.kmz=T\u00f6m\u00f6r\u00edt\u00e9s kmz f\u00e1jl k\u00e9sz\u00edt\u00e9s\u00e9hez
dialog.exportkml.exportimages=K\u00e9pminiat\u0171r\u00f6k export\u00e1l\u00e1sa kmz-be
+dialog.exportkml.imagesize=K\u00e9pm\u00e9ret
dialog.exportkml.trackcolour=Nyomvonal sz\u00edne
+dialog.exportkml.standardkml=Szabv\u00e1nyos KML
+dialog.exportkml.extendedkml=KML kib\u0151v\u00edt\u00e9se id\u0151b\u00e9lyegekkel
dialog.exportgpx.name=N\u00e9v
dialog.exportgpx.desc=Le\u00edr\u00e1s
dialog.exportgpx.includetimestamps=Id\u0151b\u00e9lyegek is
@@ -204,10 +248,25 @@ dialog.exportpov.modelstyle=Modell st\u00edlusa
dialog.exportpov.ballsandsticks=Goly\u00f3k \u00e9s botok
dialog.exportpov.tubesandwalls=Cs\u00f6vek \u00e9s falak
dialog.3d.warningtracksize=Ez a nyomvonal nagy sz\u00e1m\u00fa pontot tartalmaz, amelyet a Java3D nem biztos, hogy meg tud jelen\u00edteni.\nBiztos benne, hogy folytatni szeretn\u00e9?
+dialog.3d.useterrain=Terep megjelen\u00edt\u00e9se
+dialog.3d.terraingridsize=R\u00e1csm\u00e9ret
+dialog.exportpov.baseimage=Alapk\u00e9p
+dialog.exportpov.cannotmakebaseimage=Az alapk\u00e9p nem \u00edrhat\u00f3
+dialog.baseimage.title=T\u00e9rk\u00e9p k\u00e9p
+dialog.baseimage.useimage=K\u00e9p haszn\u00e1lata
+dialog.baseimage.mapsource=T\u00e9rk\u00e9pforr\u00e1s
+dialog.baseimage.zoom=Zoom szint
+dialog.baseimage.incomplete=Hi\u00e1nyos k\u00e9p
+dialog.baseimage.tiles=Csemp\u00e9k
+dialog.baseimage.size=K\u00e9pm\u00e9ret
dialog.exportsvg.text=Param\u00e9terek kiv\u00e1laszt\u00e1sa az SVG exporthoz
dialog.exportsvg.phi=Ir\u00e1nysz\u00f6g \u03d5
dialog.exportsvg.theta=Emel\u00e9s sz\u00f6ge \u03b8
dialog.exportsvg.gradients=\u00c1tmenetek haszn\u00e1lata az \u00e1rny\u00e9kol\u00e1shoz
+dialog.exportimage.noimagepossible=A t\u00e9rk\u00e9p k\u00e9peit az export\u00e1l\u00e1shoz el\u0151bb lemezre kell menteni.
+dialog.exportimage.drawtrack=Nyomvonal rajzol\u00e1sa a t\u00e9rk\u00e9pen
+dialog.exportimage.drawtrackpoints=A nyomvonal pontjainak kirajzol\u00e1sa
+dialog.exportimage.textscalepercent=Sz\u00f6vegnagy\u00edt\u00e1si faktor (%)
dialog.pointtype.desc=A k\u00f6vetkez\u0151 pontt\u00edpusok ment\u00e9se:
dialog.pointtype.track=Nyompontok
dialog.pointtype.waypoint=\u00datpontok
@@ -219,6 +278,7 @@ dialog.confirmreversetrack.text=Ez a nyomvonal id\u0151b\u00e9lyeg-inform\u00e1c
dialog.confirmcutandmove.title=Kiv\u00e1g\u00e1s \u00e9s mozgat\u00e1s meger\u0151s\u00edt\u00e9se
dialog.confirmcutandmove.text=Ez a nyomvonal id\u0151b\u00e9lyeg-inform\u00e1ci\u00f3t tartalmaz, amely sorrendje mozgat\u00e1s ut\u00e1n megv\u00e1ltozik.\n Biztos benne, hogy mozgatja a kijel\u00f6l\u00e9st?
dialog.interpolate.parameter.text=Pontok sz\u00e1ma, amely a k\u00e9t kiv\u00e1lasztott pont k\u00f6z\u00e9 besz\u00farand\u00f3
+dialog.interpolate.betweenwaypoints=Interpol\u00e1l az \u00fatpontok k\u00f6z\u00f6tt?
dialog.undo.title=M\u0171velet(ek) visszavon\u00e1sa
dialog.undo.pretext=V\u00e1lassza ki a visszavonand\u00f3 m\u0171velet(ek)et
dialog.undo.none.title=Nem vonhat\u00f3 vissza
@@ -226,8 +286,9 @@ dialog.undo.none.text=Nincs visszavonhat\u00f3 m\u0171velet!
dialog.clearundo.title=Visszavon\u00e1si lista t\u00f6rl\u00e9se
dialog.clearundo.text=Biztos benne, hogy t\u00f6r\u00f6lni szeretn\u00e9 a visszavon\u00e1si list\u00e1t?\nMinden visszavon\u00e1si inform\u00e1ci\u00f3 el fog veszni!
dialog.pointedit.title=Pont szerkeszt\u00e9se
-dialog.pointedit.text=V\u00e1lassza ki egyenk\u00e9nt a mez\u0151ket, amelyeket szerkeszteni szeretne, majd az \u00e9rt\u00e9k m\u00f3dos\u00edt\u00e1s\u00e1hoz haszn\u00e1lja a "Szerkeszt\u00e9s" gombot
+dialog.pointedit.intro=V\u00e1lassz ki egy mez\u0151t, hogy megn\u00e9zd \u00e9s szerkeszd az \u00e9rt\u00e9k\u00e9t
dialog.pointedit.table.field=Mez\u0151
+dialog.pointedit.nofield=Nincs kiv\u00e1lasztott mez\u0151
dialog.pointedit.table.value=\u00c9rt\u00e9k
dialog.pointnameedit.name=\u00datpont neve
dialog.pointnameedit.uppercase=NAGYBET\u0170S
@@ -268,6 +329,29 @@ dialog.distances.column.to=V\u00e9gpont
dialog.distances.currentpoint=Jelenlegi pont
dialog.distances.toofewpoints=Ehhez a funkci\u00f3hoz \u00fatpontok kellenek, amelyek k\u00f6z\u00f6tt a t\u00e1vols\u00e1g sz\u00e1m\u00edt\u00e1sra ker\u00fcl
dialog.fullrangedetails.intro=Itt vannak a r\u00e9szletei a kiv\u00e1lasztott tartom\u00e1nynak
+dialog.fullrangedetails.coltotal=R\u00e9sekkel egy\u00fctt
+dialog.fullrangedetails.colsegments=R\u00e9sek n\u00e9lk\u00fcl
+dialog.estimatetime.details=R\u00e9szletek
+dialog.estimatetime.gentle=Lank\u00e1s
+dialog.estimatetime.steep=Meredek
+dialog.estimatetime.climb=M\u00e1sz\u00e1s
+dialog.estimatetime.descent=Ereszked\u00e9s
+dialog.estimatetime.parameters=Param\u00e9terek
+dialog.estimatetime.parameters.timefor=Sz\u00fcks\u00e9ges id\u0151:
+dialog.estimatetime.results=Eredm\u00e9nyek
+dialog.estimatetime.results.estimatedtime=Becs\u00fclt id\u0151
+dialog.estimatetime.results.actualtime=Aktu\u00e1lis id\u0151
+dialog.estimatetime.error.nodistance=Az id\u0151becsl\u00e9shez \u00f6sszek\u00f6t\u00f6tt nyomvonalpontokra van sz\u00fcks\u00e9g a t\u00e1vols\u00e1g meghat\u00e1roz\u00e1s\u00e1hoz
+dialog.estimatetime.error.noaltitudes=A kijel\u00f6l\u00e9s nem tartalmaz magass\u00e1g inform\u00e1ci\u00f3t
+dialog.learnestimationparams.intro=A nyomvonalb\u00f3l sz\u00e1m\u00edtott param\u00e9terek
+dialog.learnestimationparams.averageerror=\u00c1tlagos hiba
+dialog.learnestimationparams.combine=A param\u00e9terek kombin\u00e1lhat\u00f3k a jelenlegi \u00e9rt\u00e9kekkel
+dialog.learnestimationparams.combinedresults=Kombin\u00e1lt eredm\u00e9nyek
+dialog.learnestimationparams.weight.100pccurrent=Jelenlegi \u00e9rt\u00e9kek megtart\u00e1sa
+dialog.learnestimationparams.weight.current=jelenlegi
+dialog.learnestimationparams.weight.calculated=sz\u00e1m\u00edtott
+dialog.learnestimationparams.weight.50pc=A jelenlegi \u00e9s a sz\u00e1m\u00edtott \u00e9rt\u00e9kek \u00e1tlaga
+dialog.learnestimationparams.weight.100pccalculated=Az \u00faj sz\u00e1m\u00edtott \u00e9rt\u00e9kek haszn\u00e1lata
dialog.setmapbg.intro=V\u00e1lassza ki az egyik t\u00e9rk\u00e9pforr\u00e1st, vagy adjon hozz\u00e1 egy \u00fajat
dialog.addmapsource.title=\u00daj t\u00e9rk\u00e9pforr\u00e1s hozz\u00e1ad\u00e1sa
dialog.addmapsource.sourcename=Forr\u00e1s neve
@@ -298,11 +382,11 @@ dialog.wikipedia.column.name=Sz\u00f3cikk neve
dialog.wikipedia.column.distance=T\u00e1vols\u00e1g
dialog.correlate.notimestamps=Nincsenek id\u0151b\u00e9lyegek az adatpontokon, \u00edgy nem feleltethet\u0151 meg semmi a f\u00e9nyk\u00e9pekkel.
dialog.correlate.nouncorrelatedphotos=Nincsenek megfeleltetlen f\u00e9nyk\u00e9pek.\nBiztos benne, hogy folytatja?
+dialog.correlate.nouncorrelatedaudios=Nincsenek megfeleltetlen hangok.\nBiztos benne, hogy folytatja?
dialog.correlate.photoselect.intro=V\u00e1lasszon egyet ezek k\u00f6z\u00fcl a megfeleltetett f\u00e9nyk\u00e9pek k\u00f6z\u00fcl az id\u0151eltol\u00e1s haszn\u00e1lat\u00e1hoz
dialog.correlate.select.photoname=F\u00e9nyk\u00e9p neve
dialog.correlate.select.timediff=Id\u0151k\u00fcl\u00f6nbs\u00e9g
dialog.correlate.select.photolater=K\u00e9s\u0151bbi f\u00e9nyk\u00e9p
-dialog.correlate.options.tip=Tipp: legal\u00e1bb egy elem k\u00e9zzel t\u00f6rt\u00e9n\u0151 \u00f6sszekapcsol\u00e1s\u00e1val az id\u0151eltol\u00e1s kisz\u00e1m\u00edthat\u00f3.
dialog.correlate.options.intro=V\u00e1lassza ki az opci\u00f3kat az automatikus megfeleltet\u00e9shez
dialog.correlate.options.offsetpanel=Id\u0151eltol\u00e1s
dialog.correlate.options.offset=Eltol\u00e1s
@@ -335,7 +419,6 @@ dialog.rearrangephotos.toend=Mozgat\u00e1s a v\u00e9g\u00e9hez
dialog.rearrangephotos.nosort=Ne rendezze
dialog.rearrangephotos.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint
dialog.rearrangephotos.sortbytime=Rendez\u00e9s id\u0151 szerint
-dialog.deletemarked.nonefound=Nem t\u00e1vol\u00edthat\u00f3 el adatpont
dialog.compress.closepoints.title=K\u00f6zeli pontok elt\u00e1vol\u00edt\u00e1sa
dialog.compress.closepoints.paramdesc=Hat\u00f3t\u00e1vols\u00e1g
dialog.compress.wackypoints.title=Kisz\u00e1m\u00edthatatlan pontok elt\u00e1vol\u00edt\u00e1sa
@@ -346,17 +429,20 @@ dialog.compress.duplicates.title=Kett\u0151z\u00f6tt pontok elt\u00e1vol\u00edt\
dialog.compress.douglaspeucker.title=Douglas-Peucker t\u00f6m\u00f6r\u00edt\u00e9s
dialog.compress.douglaspeucker.paramdesc=T\u00f6m\u00f6r\u00edt\u00e9si t\u00e9nyez\u0151
dialog.compress.summarylabel=T\u00f6rlend\u0151 pontok
+dialog.compress.confirm=%d a pontok meg lettek jel\u00f6lve.\nJel\u00f6lt pontok t\u00f6rl\u00e9se?
+dialog.compress.confirmnone=egy pont sem lett megjel\u00f6lve
+dialog.deletemarked.nonefound=Nem t\u00e1vol\u00edthat\u00f3 el adatpont
dialog.pastecoordinates.desc=Adja meg vagy illessze be a koordin\u00e1t\u00e1kat ide
dialog.pastecoordinates.coords=Koordin\u00e1t\u00e1k
-dialog.pastecoordinates.nothingfound=Ellen\u0151rizze a koordin\u00e1t\u00e1kat, \u00e9s pr\u00f3b\u00e1lja \u00f3jra
-dialog.help.help=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a \n http://activityworkshop.net/software/gpsprune/\nwebhelyet.
+dialog.pastecoordinates.nothingfound=Ellen\u0151rizze a koordin\u00e1t\u00e1kat, \u00e9s pr\u00f3b\u00e1lja \u00fajra
+dialog.help.help=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a \n http://gpsprune.activityworkshop.net/\nwebhelyet.
dialog.about.version=Verzi\u00f3
dialog.about.build=Build
dialog.about.summarytext1=A GpsPrune egy program GPS vev\u0151kr\u0151l sz\u00e1rmaz\u00f3 adatok bet\u00f6lt\u00e9s\u00e9re, megjelen\u00edt\u00e9s\u00e9re \u00e9s szerkeszt\u00e9s\u00e9re.
dialog.about.summarytext2=Gnu GPL licenc alatt ker\u00fclt kiad\u00e1sra a szabad, ny\u00edlt, vil\u00e1gm\u00e9ret\u0171 haszn\u00e1lathoz \u00e9s fejleszt\u00e9shez.<br>M\u00e1sol\u00e1sa, terjeszt\u00e9se \u00e9s m\u00f3dos\u00edt\u00e1sa megengedett \u00e9s \u00f6szt\u00f6nz\u00f6tt<br>a mell\u00e9kelt <code>license.txt</code> f\u00e1jlban r\u00f6gz\u00edtett felt\u00e9telek szerint
dialog.about.summarytext3=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a <code style="font-weight:bold">http://activityworkshop.net/</code> webhelyet.
dialog.about.languages=El\u00e9rhet\u0151 nyelvek
-dialog.about.translatedby=Magyar sz\u00f6veg: Ball\u00f3 Gy\u00f6rgy
+dialog.about.translatedby=Magyar sz\u00f6veg: Ball\u00f3 Gy\u00f6rgy \u00e9s B\u00e1thory P\u00e9ter
dialog.about.systeminfo=Rendszerinform\u00e1ci\u00f3
dialog.about.systeminfo.os=Oper\u00e1ci\u00f3s rendszer
dialog.about.systeminfo.java=Java futtat\u00f3k\u00f6rnyezet
@@ -373,7 +459,7 @@ dialog.about.systeminfo.exiflib.external.failed=K\u00fcls\u0151 (nem tal\u00e1lh
dialog.about.yes=Igen
dialog.about.no=Nem
dialog.about.credits=K\u00e9sz\u00edt\u0151k
-dialog.about.credits.code=GpsPrune k\u00f3dj\u00e1t \u00edrta:
+dialog.about.credits.code=A GpsPrune k\u00f3dj\u00e1t \u00edrta:
dialog.about.credits.exifcode=Exif k\u00f3d:
dialog.about.credits.icons=N\u00e9h\u00e1ny ikon sz\u00e1rmazik:
dialog.about.credits.translators=Ford\u00edt\u00f3k
@@ -386,9 +472,9 @@ dialog.checkversion.error=A verzi\u00f3sz\u00e1m nem ellen\u0151rizhet\u0151.\nE
dialog.checkversion.uptodate=A GpsPrune leg\u00fajabb verzi\u00f3j\u00e1t haszn\u00e1lja.
dialog.checkversion.newversion1=El\u00e9rhet\u0151 a GpsPrune \u00faj verzi\u00f3ja! A leg\u00fajabb verzi\u00f3 most:
dialog.checkversion.newversion2=.
-dialog.checkversion.releasedate1=Ez az \u00faj verzi\u00f3 kiad\u00e1sra ker\u00fclt:
+dialog.checkversion.releasedate1=Az \u00faj verzi\u00f3 ekkor lett kiadva:
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Az \u00faj verzi\u00f3 let\u00f6lt\u00e9s\u00e9hez keresse fel a http://activityworkshop.net/software/gpsprune/download.html webhelyet.
+dialog.checkversion.download=Az \u00faj verzi\u00f3 let\u00f6lt\u00e9s\u00e9hez keresse fel a http://gpsprune.activityworkshop.net/download.html webhelyet.
dialog.keys.intro=A k\u00f6vetkez\u0151 gyorsbillenty\u0171k haszn\u00e1lhat\u00f3k az eg\u00e9r haszn\u00e1lata helyett
dialog.keys.keylist=<table><tr><td>Ny\u00edlbillenty\u0171k</td><td>T\u00e9rk\u00e9p mozgat\u00e1sa balra, jobbra, fel, le</td></tr><tr><td>Ctrl + bal, jobb ny\u00edl</td><td>El\u0151z\u0151 vagy k\u00f6vetkez\u0151 pont kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Ctrl + fel, le ny\u00edl</td><td>Nagy\u00edt\u00e1s vagy kicsiny\u00edt\u00e9s</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>El\u0151z\u0151, k\u00f6vetkez\u0151 szakasz kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Ctrl + Home, End</td>< [...]
dialog.keys.normalmodifier=Ctrl
@@ -415,7 +501,7 @@ dialog.saveconfig.prune.autosavesettings=Automatikus ment\u00e9s be\u00e1ll\u00e
dialog.setpaths.intro=Ha sz\u00fcks\u00e9ges, kiv\u00e1laszthatja a k\u00fcls\u0151 alkalmaz\u00e1sok \u00fatvonalait:
dialog.setpaths.found=\u00datvonal megtal\u00e1lhat\u00f3?
dialog.addaltitude.noaltitudes=A kiv\u00e1lasztott tartom\u00e1ny nem tartalmaz magass\u00e1gi \u00e9rt\u00e9keket
-dialog.addaltitude.desc=Magass\u00e1ki eltol\u00e1s, amely hozz\u00e1adand\u00f3
+dialog.addaltitude.desc=Magass\u00e1gi eltol\u00e1s, amely hozz\u00e1adand\u00f3
dialog.lookupsrtm.overwritezeros=Fel\u00fcl\u00edrja a nulla magass\u00e1g \u00e9rt\u00e9ket?
dialog.setcolours.intro=A sz\u00edn m\u00f3dos\u00edt\u00e1s\u00e1hoz kattintson egy sz\u00ednfoltra
dialog.setcolours.background=H\u00e1tt\u00e9r
@@ -440,6 +526,7 @@ dialog.diskcache.save=T\u00e9rk\u00e9pek ment\u00e9se a lemezre
dialog.diskcache.dir=Gyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra
dialog.diskcache.createdir=K\u00f6nyvt\u00e1r l\u00e9trehoz\u00e1sa
dialog.diskcache.nocreate=A gyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem ker\u00fclt l\u00e9trehoz\u00e1sra
+dialog.diskcache.cannotwrite=A t\u00e9rk\u00e9pcsemp\u00e9k nem menthet\u0151ek a kiv\u00e1lasztott k\u00f6nyvt\u00e1rba
dialog.diskcache.table.path=El\u00e9r\u00e9si \u00fat
dialog.diskcache.table.usedby=Haszn\u00e1lja
dialog.diskcache.table.zoom=Nagy\u00edt\u00e1s
@@ -450,12 +537,31 @@ dialog.diskcache.tileset.multiple=t\u00f6bb
dialog.diskcache.deleteold=R\u00e9gi csemp\u00e9k t\u00f6rl\u00e9se
dialog.diskcache.maximumage=Maxim\u00e1lis kor (nap)
dialog.diskcache.deleteall=Az \u00f6sszes csempe t\u00f6rl\u00e9se
-dialog.diskcache.deleted1=
-dialog.diskcache.deleted2=f\u00e1jl t\u00f6r\u00f6lve a gyors\u00edt\u00f3t\u00e1rb\u00f3l
+dialog.diskcache.deleted=%d f\u00e1jl t\u00f6r\u00f6lve a gyors\u00edt\u00f3t\u00e1rb\u00f3l
dialog.deletefieldvalues.intro=V\u00e1lassza ki a t\u00f6rlend\u0151 mez\u0151t a jelenlegi tartom\u00e1nyban
+dialog.deletefieldvalues.nofields=Nincs t\u00f6r\u00f6lhet\u0151 mez\u0151 a tartom\u00e1nyban
dialog.setlinewidth.text=Adja meg a rajzoland\u00f3 vonalak vastags\u00e1g\u00e1t a nyomvonalak sz\u00e1m\u00e1ra (1-4)
dialog.downloadosm.desc=Nyers OSM adatok let\u00f6lt\u00e9s\u00e9nek meger\u0151s\u00edt\u00e9se a megadott ter\u00fcletre:
dialog.searchwikipedianames.search=Keres\u00e9s erre:
+dialog.weather.location=Helysz\u00edn
+dialog.weather.update=El\u0151rejelz\u00e9s friss\u00edtve
+dialog.weather.sunrise=Napkelte
+dialog.weather.sunset=Napnyugta
+dialog.weather.temperatureunits=H\u0151m\u00e9rs\u00e9klet
+dialog.weather.currentforecast=Jelenlegi id\u0151j\u00e1r\u00e1s
+dialog.weather.dailyforecast=Napi el\u0151rejelz\u00e9s
+dialog.weather.3hourlyforecast=3 \u00f3r\u00e1s el\u0151rejelz\u00e9s
+dialog.weather.day.now=Jelenlegi id\u0151j\u00e1r\u00e1s
+dialog.weather.day.today=Ma
+dialog.weather.day.tomorrow=Holnap
+dialog.weather.day.monday=H\u00e9tf\u0151
+dialog.weather.day.tuesday=Kedd
+dialog.weather.day.wednesday=Szerda
+dialog.weather.day.thursday=Cs\u00fct\u00f6rt\u00f6k
+dialog.weather.day.friday=P\u00e9ntek
+dialog.weather.day.saturday=Szombat
+dialog.weather.day.sunday=Vas\u00e1rnap
+dialog.weather.creditnotice=Az adatok az openweathermap.org-r\u00f3l sz\u00e1rmaznak. N\u00e1luk tov\u00e1bbi inform\u00e1ci\u00f3t tal\u00e1lsz.
# 3d window
dialog.3d.title=GpsPrune 3D n\u00e9zet
@@ -474,10 +580,12 @@ confirm.addtimeoffset=Id\u0151eltol\u00e1s hozz\u00e1adva
confirm.addaltitudeoffset=Magass\u00e1geltol\u00e1s hozz\u00e1adva
confirm.rearrangewaypoints=\u00datpontok \u00fajrarendezve
confirm.rearrangephotos=F\u00e9nyk\u00e9pek \u00fajrarendezve
+confirm.splitsegments=%d szakasz v\u00e1g\u00e1s elv\u00e9gezve
+confirm.sewsegments=%d szakasz egyes\u00edt\u00e9se elv\u00e9gezve
confirm.cutandmove=Kijel\u00f6l\u00e9s \u00e1thelyezve
+confirm.interpolate=Pontok hozz\u00e1adva
confirm.convertnamestotimes=\u00datpont nevei konvert\u00e1lva
-confirm.saveexif.ok1=Mentve
-confirm.saveexif.ok2=k\u00e9pf\u00e1jl
+confirm.saveexif.ok=Mentve %d k\u00e9pf\u00e1jl
confirm.undo.single=m\u0171velet visszavonva
confirm.undo.multi=m\u0171velet visszavonva
confirm.jpegload.single=f\u00e9nyk\u00e9p hozz\u00e1adva
@@ -491,13 +599,23 @@ confirm.correlatephotos.multi=f\u00e9nyk\u00e9p megfeleltetve
confirm.createpoint=pont l\u00e9trehozva
confirm.rotatephoto=f\u00e9nyk\u00e9p elforgatva
confirm.running=Futtat\u00e1s...
-confirm.lookupsrtm1=
-confirm.lookupsrtm2=magass\u00e1gi \u00e9rt\u00e9k tal\u00e1lhat\u00f3
+confirm.lookupsrtm=%d magass\u00e1gi \u00e9rt\u00e9k tal\u00e1lhat\u00f3
+confirm.downloadsrtm=%d f\u00e1jl let\u00f6lt\u00e9se gyors\u00edt\u00f3t\u00e1rba
+confirm.downloadsrtm.1=%d f\u00e1jl let\u00f6lt\u00e9se gyors\u00edt\u00f3t\u00e1rba
+confirm.downloadsrtm.none=Nem kellett f\u00e1jlokat let\u00f6lteni, m\u00e1r gyors\u00edt\u00f3t\u00e1rban voltak.
confirm.deletefieldvalues=Mez\u0151 \u00e9rt\u00e9kei t\u00f6r\u00f6lve
confirm.audioload=Hangf\u00e1jl hozz\u00e1adva
confirm.correlateaudios.single=hangf\u00e1jl megfeleltetve
confirm.correlateaudios.multi=hangf\u00e1jl megfeleltetve
+# Tips, shown just once when appropriate
+tip.title=Tipp
+tip.useamapcache=Gyors\u00edt\u00f3t\u00e1r be\u00e1ll\u00edt\u00e1s\u00e1val (Be\u00e1ll\u00edt\u00e1sok -> T\u00e9rk\u00e9pek lemezre ment\u00e9se) gyors\u00edthatod a megjelen\u00edt\u00e9st \u00e9s cs\u00f6kkentheted az adatforgalmat.
+tip.learntimeparams=Az eredm\u00e9ny sokkal pontosabb lesz, ha be\u00e1ll\u00edtod a\n Nyomvonal -> Id\u0151becsl\u00e9s tanul\u00e1s\u00e1nak param\u00e9terei\n \u00e9rt\u00e9keit a r\u00f6gz\u00edtett nyomvonalhoz.
+tip.downloadsrtm=Gyors\u00edthatod a folyamatot, ha az adatokat lemezre mented:\n Online -> SRTM csemp\u00e9k let\u00f6lt\u00e9se
+tip.usesrtmfor3d=A nyomvonal nem tartalmaz magass\u00e1gadatokat.\nHaszn\u00e1lhatod az SRTM funkci\u00f3kat, hogy k\u00f6zel\u00edt\u0151 magass\u00e1gi \u00e9rt\u00e9keket kapj\na 3D n\u00e9zethez
+tip.manuallycorrelateone=Legal\u00e1bb egy elem k\u00e9zzel t\u00f6rt\u00e9n\u0151 \u00f6sszekapcsol\u00e1s\u00e1val az id\u0151eltol\u00e1s kisz\u00e1m\u00edthat\u00f3.
+
# Buttons
button.ok=OK
button.back=El\u0151z\u0151
@@ -515,6 +633,7 @@ button.yes=Igen
button.no=Nem
button.yestoall=Igen, mindet
button.notoall=Nem, egyiket se
+button.always=Mindig
button.select=Kijel\u00f6l\u00e9s
button.selectall=Mindent kijel\u00f6l
button.selectnone=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
@@ -529,6 +648,7 @@ button.browse=B\u00f6ng\u00e9sz\u00e9s...
button.addnew=\u00daj hozz\u00e1ad\u00e1sa
button.delete=T\u00f6rl\u00e9s
button.manage=Kezel\u00e9s
+button.combine=Egyes\u00edt\u00e9s
# File types
filetype.txt=TXT f\u00e1jlok
@@ -546,6 +666,7 @@ filetype.audio=MP3, OGG, WAV f\u00e1jlok
display.nodata=Nincs adat bet\u00f6ltve
display.noaltitudes=A nyomvonal nem tartalmaz magass\u00e1gi adatot
display.notimestamps=A nyomvonal nem tartalmaz id\u0151b\u00e9lyegeket
+display.novalues=A nyomvonal nem tartalmaz \u00e9rt\u00e9keket ehhez a mez\u0151h\u00f6z
details.trackdetails=Nyomvonal r\u00e9szletei
details.notrack=Nincs adat bet\u00f6ltve
details.track.points=Pontok
@@ -582,6 +703,7 @@ details.nophoto=Nincs f\u00e9nyk\u00e9p kiv\u00e1lasztva
details.photo.loading=Bet\u00f6lt\u00e9s
details.photo.bearing=Ir\u00e1ny
details.media.connected=\u00d6sszekapcsolva
+details.media.fullpath=Teljes el\u00e9r\u00e9si \u00fat
details.audiodetails=Hang r\u00e9szletei
details.noaudio=Nincs hangf\u00e1jl kiv\u00e1lasztva
details.audio.file=Hangf\u00e1jl
@@ -592,7 +714,7 @@ map.overzoom=Nem \u00e9rhet\u0151 el t\u00e9rk\u00e9p ezen a nagy\u00edt\u00e1si
fieldname.latitude=Sz\u00e9less\u00e9g
fieldname.longitude=Hossz\u00fas\u00e1g
fieldname.altitude=Magass\u00e1g
-fieldname.timestamp=Id\u00f5
+fieldname.timestamp=Id\u0151
fieldname.time=Id\u0151
fieldname.waypointname=N\u00e9v
fieldname.waypointtype=T\u00edpus
@@ -600,7 +722,6 @@ fieldname.newsegment=Szakasz
fieldname.custom=Egy\u00e9ni
fieldname.prefix=Mez\u0151
fieldname.distance=T\u00e1vols\u00e1g
-fieldname.movingdistance=Mozg\u00e1si t\u00e1vols\u00e1g
fieldname.duration=Id\u0151tartam
fieldname.speed=Sebess\u00e9g
fieldname.verticalspeed=F\u00fcgg\u0151leges sebess\u00e9g
@@ -615,16 +736,34 @@ units.feet=l\u00e1b
units.feet.short=ft
units.kilometres=kilom\u00e9ter
units.kilometres.short=km
+units.kilometresperhour=km / \u00f3ra
+units.kilometresperhour.short=km/h
units.miles=m\u00e9rf\u00f6ld
units.miles.short=mi
+units.milesperhour=m\u00e9rf\u00f6ld / \u00f3ra
units.milesperhour.short=mph
+units.nauticalmiles=Tengeri m\u00e9rf\u00f6ld
+units.nauticalmiles.short=nmi
+units.nauticalmilesperhour.short=csom\u00f3
+units.metrespersec=m\u00e9ter / sec
units.metrespersec.short=m/s
+units.feetpersec=l\u00e1b / sec
units.feetpersec.short=ft/s
units.hours=\u00f3ra
+units.minutes=perc
+units.seconds=m\u00e1sodperc
units.degminsec=Sz\u00f6g-sz\u00f6gperc-sz\u00f6gm\u00e1sodperc
units.degmin=Sz\u00f6g-sz\u00f6gperc
units.deg=Sz\u00f6g
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
+
+# How to combine conditions, such as filters
+logic.and=\u00e9s
+logic.or=vagy
# External urls
url.googlemaps=maps.google.hu
@@ -645,10 +784,13 @@ undo.deletepoint=pont t\u00f6rl\u00e9se
undo.removephoto=f\u00e9nyk\u00e9p elt\u00e1vol\u00edt\u00e1sa
undo.removeaudio=hangf\u00e1jl elt\u00e1vol\u00edt\u00e1sa
undo.deleterange=tartom\u00e1ny t\u00f6rl\u00e9se
+undo.croptrack=nyomvonal k\u00f6rbev\u00e1g\u00e1sa
undo.deletemarked=nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se
undo.insert=pontok besz\u00far\u00e1sa
undo.reverse=tartom\u00e1ny megford\u00edt\u00e1sa
undo.mergetracksegments=nyomvonalszakaszok egyes\u00edt\u00e9se
+undo.splitsegments=nyomvonal szakaszokra v\u00e1g\u00e1sa
+undo.sewsegments=nyomvonalszakaszok egyes\u00edt\u00e9se
undo.addtimeoffset=id\u0151eltol\u00e1s hozz\u00e1ad\u00e1sa
undo.addaltitudeoffset=magass\u00e1geltol\u00e1s hozz\u00e1ad\u00e1sa
undo.rearrangewaypoints=\u00fatpontok \u00fajrarendez\u00e9se
@@ -671,10 +813,8 @@ error.save.failed=Az adatok f\u00e1jlba ment\u00e9se nem siker\u00fclt
error.saveexif.filenotfound=F\u00e9nyk\u00e9pf\u00e1jl keres\u00e9se nem siker\u00fclt
error.saveexif.cannotoverwrite1=A(z)
error.saveexif.cannotoverwrite2=f\u00e9nyk\u00e9pf\u00e1jl csak olvashat\u00f3, \u00e9s nem \u00edrhat\u00f3 fel\u00fcl. \u00cdrjuk m\u00e1solatba?
-error.saveexif.failed1=
-error.saveexif.failed2=k\u00e9p ment\u00e9se nem siker\u00fclt
-error.saveexif.forced1=
-error.saveexif.forced2=k\u00e9p er\u00f6ltet\u00e9st ig\u00e9nyelt
+error.saveexif.failed=%d k\u00e9p ment\u00e9se nem siker\u00fclt
+error.saveexif.forced=%d k\u00e9p er\u0151ltet\u00e9st ig\u00e9nyelt
error.load.dialogtitle=Hiba az adatok bet\u00f6lt\u00e9sekor
error.load.noread=A f\u00e1jl nem olvashat\u00f3
error.load.nopoints=Nem tal\u00e1lhat\u00f3 koordin\u00e1tainform\u00e1ci\u00f3 a f\u00e1jlban
@@ -685,7 +825,7 @@ error.jpegload.dialogtitle=Hiba a k\u00e9pek bet\u00f6lt\u00e9sekor
error.jpegload.nofilesfound=Nem tal\u00e1lhat\u00f3 f\u00e1jl
error.jpegload.nojpegsfound=Nem tal\u00e1lhat\u00f3 jpeg f\u00e1jl
error.jpegload.nogpsfound=Nem tal\u00e1lhat\u00f3 GPS inform\u00e1ci\u00f3
-error.jpegload.exifreadfailed=Az EXIF inform\u00e1ci\u00f3 olvas\u00e1sa nem siker\u00fclt. Nem olvasat\u00f3 EXIF inform\u00e1ci\u00f3\nbe\u00e9p\u00edtett vagy k\u00fcls\u0151 f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r n\u00e9lk\u00fcl.
+error.jpegload.exifreadfailed=Az Exif inform\u00e1ci\u00f3 olvas\u00e1sa nem siker\u00fclt. Nem olvashat\u00f3 Exif inform\u00e1ci\u00f3\nbe\u00e9p\u00edtett vagy k\u00fcls\u0151 f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r n\u00e9lk\u00fcl.
error.audioload.nofilesfound=Nem tal\u00e1lhat\u00f3 hangf\u00e1jl
error.gpsload.unknown=Ismeretlen hiba
error.undofailed.title=A visszavon\u00e1s nem siker\u00fclt
@@ -704,7 +844,13 @@ error.lookupsrtm.nonefound=Nem \u00e9rhet\u0151 el magass\u00e1gi \u00e9rt\u00e9
error.lookupsrtm.nonerequired=Az \u00f6sszes pont m\u00e1r rendelkezik magass\u00e1gadatokkal, \u00edgy nincs mit keresni
error.gpsies.uploadnotok=A gpsies szerver a k\u00f6vetkez\u0151 \u00fczenetet adta vissza
error.gpsies.uploadfailed=A felt\u00f6lt\u00e9s nem siker\u00fclt a k\u00f6vetkez\u0151 hib\u00e1val
+error.showphoto.failed=F\u00e9nyk\u00e9p bet\u00f6lt\u00e9se sikertelen
error.playaudiofailed=A hangf\u00e1jl lej\u00e1tsz\u00e1sa nem siker\u00fclt
error.cache.notthere=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem tal\u00e1lhat\u00f3
error.cache.empty=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra \u00fcres
error.cache.cannotdelete=Nincs t\u00f6r\u00f6lhet\u0151 csempe
+error.interpolate.invalidparameter=A pontok sz\u00e1ma 1 \u00e9s 1000 k\u00f6z\u00f6tt kell legyen
+error.learnestimationparams.failed=Nem lehet param\u00e9tereket tanulni ebb\u0151l a nyomvonalb\u00f3l.\nT\u00f6lts be t\u00f6bb nyomvonalat.
+error.tracksplit.nosplit=A nyomvonal nem elv\u00e1ghat\u00f3
+error.downloadsrtm.nocache=A f\u00e1jlokat nem siker\u00fclt meneni.\nK\u00e9rlek, ellen\u0151rizd a gyors\u00edt\u00f3t\u00e1rat.
+error.sewsegments.nothingdone=A szakaszokat nem lehet \u00f6sszef\u0171zni.\nA nyomvonal jelenleg %d szakaszt tartalmaz.
diff --git a/tim/prune/lang/prune-texts_in.properties b/tim/prune/lang/prune-texts_in.properties
new file mode 100644
index 0000000..c0b4246
--- /dev/null
+++ b/tim/prune/lang/prune-texts_in.properties
@@ -0,0 +1,111 @@
+# Text entries for the Prune application
+# Indonesian entries as extra
+
+# Menu entries
+menu.file=Berkas
+menu.file.addphotos=Muat foto
+menu.file.save=Simpan
+menu.file.exit=Keluar
+menu.track=Track
+menu.track.undo=Batal
+menu.point.editpoint=Perbaiki titik
+menu.point.deletepoint=Hapus titik
+function.deleterange=Hapus jarak
+menu.range=Jangkauan
+menu.point=Titik
+menu.range.all=Pilih semua
+menu.range.none=Tidak memilih
+menu.photo=Foto
+menu.photo.saveexif=Simpan ke Exif
+function.connecttopoint=Hubungkan ke titik
+function.disconnectfrompoint=Putuskan dari titik
+function.removephoto=Menghapus foto
+menu.view=Lihat
+menu.settings=Pengaturan
+menu.view.browser=Peta di browser
+menu.help=Bantuan
+# Popup menu for map
+menu.map.zoomin=Perbesar
+menu.map.zoomout=Perkecil
+menu.map.newpoint=Buat titik baru
+menu.map.connect=Hubungkan titik jalur
+menu.map.showmap=Tampilkan peta
+
+# Alt keys for menus
+altkey.menu.file=B
+altkey.menu.track=T
+altkey.menu.range=J
+altkey.menu.point=K
+altkey.menu.view=L
+altkey.menu.photo=F
+altkey.menu.settings=G
+altkey.menu.help=N
+
+# Ctrl shortcuts for menu items
+shortcut.menu.file.open=B
+shortcut.menu.file.load=M
+shortcut.menu.file.save=S
+shortcut.menu.track.undo=Z
+shortcut.menu.edit.compress=P
+shortcut.menu.range.all=-
+shortcut.menu.help.help=-
+
+# Functions
+function.open=Buka
+function.loadfromgps=Muat data dari GPS
+function.sendtogps=Kirim data ke GPS
+function.exportkml=Ekspor KML
+function.exportgpx=Ekspor GPX
+function.exportpov=Ekspor POV
+function.editwaypointname=Perbaiki Nama waypoint
+function.compress=Padatkan jalur
+function.findwaypoint=Menemukan waypoint
+function.charts=Grafik
+function.show3d=Lihat tiga-D
+function.distances=Jarak
+function.correlatephotos=Korelasikan foto
+function.help=Bantuan
+function.about=Tentang Prune
+function.saveconfig=Simpan pengaturan
+
+# Dialogs
+dialog.load.table.datatype=Jenis
+dialog.load.table.description=Keterangan
+dialog.pointnameedit.name=Nama
+dialog.distances.column.from=Awal
+dialog.distances.column.to=Akhir
+dialog.about.languages=Bahasa
+dialog.about.yes=Ya
+dialog.about.no=Tidak
+
+# Buttons
+button.back=Sebelumnya
+button.next=Lanjut
+button.close=Tutup
+button.yes=Ya
+button.no=Tidak
+button.select=Pilih
+button.guessfields=Deteksi otomatis
+
+# File types
+filetype.txt=Berkas teks
+filetype.jpeg=Berkas JPG
+filetype.kmlkmz=Berkas KML, KMZ
+filetype.kml=Berkas KML
+filetype.kmz=Berkas KMZ
+filetype.gpx=Berkas GPX
+filetype.pov=Berkas POV
+filetype.svg=Berkas SVG
+
+# Display components
+display.nodata=Tidak ada data
+details.trackdetails=Rincian track
+details.notrack=Tidak ada track
+details.pointdetails=Rincian titik
+details.nopointselection=Tidak ada titik
+details.norangeselection=Tidak ada jangkauan
+details.rangedetails=Rincian jangkauan
+details.lists.photos=Foto
+details.photodetails=Rincian foto
+details.nophoto=Tidak ada foto
+details.photo.loading=Membuka
diff --git a/tim/prune/lang/prune-texts_it.properties b/tim/prune/lang/prune-texts_it.properties
index 719b791..121e04d 100644
--- a/tim/prune/lang/prune-texts_it.properties
+++ b/tim/prune/lang/prune-texts_it.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=Aggiungi foto
menu.file.recentfiles=Files recenti
menu.file.save=Salva
menu.file.exit=Esci
+menu.online=Online
menu.track=Traccia
menu.track.undo=Annulla
menu.track.clearundo=Cancella lista annulla
@@ -57,6 +58,7 @@ menu.map.editmode=Modifica
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=N
altkey.menu.track=T
altkey.menu.range=S
altkey.menu.point=P
@@ -101,12 +103,15 @@ function.show3d=Mostra in 3D
function.distances=Mostra distanze
function.fullrangedetails=Mostra dettagli
function.estimatetime=Stima durata
-function.learnestimationparams=Apprendi paramet
+function.learnestimationparams=Apprendi parametri di stima
function.setmapbg=Configura sfondo mappa
function.setpaths=Configura percorsi programmi
+function.splitsegments=Dividi traccia in segmenti
+function.sewsegments=Riorganizza segmenti insieme
function.getgpsies=Ottieni traccie da Gpsies
function.uploadgpsies=Carica traccia su Gpsies
function.lookupsrtm=Ottieni quote da SRTM
+function.downloadsrtm=Scarica file da SRTM
function.getwikipedia=Ottieni informazioni da Wikipedia
function.searchwikipedianames=Cerca il nome in Wikipedia
function.downloadosm=Scarica dati OSM dell'area
@@ -135,6 +140,7 @@ function.checkversion=Controlla gli aggiornamenti
function.saveconfig=Salva configurazione
function.diskcache=Salva mappe su disco
function.managetilecache=Gestione del cache di tasselli
+function.getweatherforecast=Ottieni previsioni del tempo
# Dialogs
dialog.exit.confirm.title=Esci da GpsPrune
@@ -233,6 +239,8 @@ dialog.exportgpx.copysource=Copia xml sorgente
dialog.exportgpx.encoding=Codifica caratteri
dialog.exportgpx.encoding.system=Impostazione di diffetto
dialog.exportgpx.encoding.utf8=UTF-8
+dialog.3d.useterrain=Mostra terreno
+dialog.3d.terraingridsize=Dimensione della griglia
dialog.exportpov.text=Per favore inserisci i parametri per l'esportazione in POV
dialog.exportpov.font=Font
dialog.exportpov.camerax=Camera X
@@ -257,6 +265,7 @@ dialog.exportsvg.theta=Angolo di elevazione \u03b8
dialog.exportsvg.gradients=Usa il gradiente per le ombre
dialog.exportimage.noimagepossible=Le mappe devono essere memorizzate su disco prima di poter essere esportate.
dialog.exportimage.drawtrack=Disegna traccia sulla mappa
+dialog.exportimage.drawtrackpoints=Disegna punti
dialog.exportimage.textscalepercent=Fattore di scala testo (%)
dialog.pointtype.desc=Salva i tipi di punti seguenti:
dialog.pointtype.track=Punti traccia
@@ -378,7 +387,6 @@ dialog.correlate.photoselect.intro=Selezione una delle foto correlate da usare c
dialog.correlate.select.photoname=Nome della foto
dialog.correlate.select.timediff=Differenza di orario
dialog.correlate.select.photolater=Foto scattata dopo il punto
-dialog.correlate.options.tip=Consiglio: Con il collegamento manuale di almeno una foto, lo scarto di orario viene calcolato per te
dialog.correlate.options.intro=Selezione le opzioni per la correlazione automatica
dialog.correlate.options.offsetpanel=Scarto di orario
dialog.correlate.options.offset=Scarto
@@ -421,8 +429,7 @@ dialog.compress.duplicates.title=Cancella duplicati
dialog.compress.douglaspeucker.title=Compressione con algoritmo Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=Fattore di apertura
dialog.compress.summarylabel=Punti da cancellare
-dialog.compress.confirm1=
-dialog.compress.confirm2=punti sono stati contrassegnati.\nUsa traccia-> Elimina i punti segnati per eliminarle
+dialog.compress.confirm=%d punti sono stati contrassegnati.\nElimina i punti?
dialog.compress.confirmnone=I punti non sono stati marcati
dialog.deletemarked.nonefound=Nessun punto rimosso
dialog.pastecoordinates.desc=Inserisci o incolla qui le coordinate
@@ -530,13 +537,34 @@ dialog.diskcache.tileset.multiple=molteplici
dialog.diskcache.deleteold=Cancellare tasselli vecchi
dialog.diskcache.maximumage=Et\u00e0 massima (giorni)
dialog.diskcache.deleteall=Cancellare tutti tasselli
-dialog.diskcache.deleted1=Cancellati
-dialog.diskcache.deleted2=files dal cache
+dialog.diskcache.deleted=Cancellati %d files dal cache
dialog.deletefieldvalues.intro=Selezione il campo da cancellare dall'intervallo corrente
dialog.deletefieldvalues.nofields=Non ci sono campi da cancellare nell'intervallo corrente
dialog.setlinewidth.text=Specifica il tratteggio delle linee per disegnare la traccia (1-4)
dialog.downloadosm.desc=Conferma lo scarico dei dati raw OSM per l'area specificata:
dialog.searchwikipedianames.search=Cerca per:
+dialog.weather.location=Localit\u00e0
+dialog.weather.update=Aggiornata
+dialog.weather.sunrise=Levata del sole
+dialog.weather.sunset=Tramonto del sole
+dialog.weather.temperatureunits=Temperature
+dialog.weather.currentforecast=Tempo attuale
+dialog.weather.dailyforecast=Previsione quotidiano
+dialog.weather.3hourlyforecast=Previsione ogni 3 ore
+dialog.weather.day.now=Tempo attuale
+dialog.weather.day.today=Oggi
+dialog.weather.day.tomorrow=Domani
+dialog.weather.day.monday=Luned\u00ec
+dialog.weather.day.tuesday=Marted\u00ec
+dialog.weather.day.wednesday=Mercoled\u00ec
+dialog.weather.day.thursday=Gioved\u00ec
+dialog.weather.day.friday=Venerd\u00ec
+dialog.weather.day.saturday=Sabato
+dialog.weather.day.sunday=Domenica
+dialog.weather.wind=Vento
+dialog.weather.temp=Temp
+dialog.weather.humidity=Umidit\u00e0
+dialog.weather.creditnotice=Queste informazioni sono rese disponibili da openweathermap.org. Il loro sito web contiene ulteriori dettagli.
# 3d window
dialog.3d.title=Visione GpsPrune in 3D
@@ -549,17 +577,18 @@ confirm.save.ok2=punti nel file
confirm.deletepoint.single=punto \u00e8 stato rimosso
confirm.deletepoint.multi=punti sono stati rimossi
confirm.point.edit=punto editato
-confirm.mergetracksegments=segmeni di traccia uniti
+confirm.mergetracksegments=segmenti di traccia uniti
confirm.reverserange=Intervallo invertito
confirm.addtimeoffset=Scarto temporale aggiunto
confirm.addaltitudeoffset=Scarto altitudine aggiunto
confirm.rearrangewaypoints=Waypoint riorganizzati
confirm.rearrangephotos=Foto riorganizzate
+confirm.splitsegments=%d segmenti sono stati suddivisi
+confirm.sewsegments=%d segmenti sono stati raggruppati
confirm.cutandmove=Selezione spostata
confirm.interpolate=Aggiungi punto
confirm.convertnamestotimes=Nome del waypoint convertito
-confirm.saveexif.ok1=Salvato
-confirm.saveexif.ok2=foto
+confirm.saveexif.ok=Salvato %d foto
confirm.undo.single=operazione annullate
confirm.undo.multi=operazioni annullate
confirm.jpegload.single=foto \u00e8 stata aggiunta
@@ -573,13 +602,19 @@ confirm.correlatephotos.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.lookupsrtm=Trovato %d valori di quota
+confirm.downloadsrtm=Scarica %d file nella cache
+confirm.downloadsrtm.1=Scarica %d file nella cache
+confirm.downloadsrtm.none=Nessun file scaricato, erano gi\u00e0 presenti nella cache
confirm.deletefieldvalues=Valori del campo cancellati
confirm.audioload=Ripresa audio aggiunta
confirm.correlateaudios.single=la ripresa audio era correlata
confirm.correlateaudios.multi=le riprese audio erano correlate
+# Tips
+tip.title=Consiglio
+tip.manuallycorrelateone=Con il collegamento manuale di almeno una foto, lo scarto di orario viene calcolato per te
+
# Buttons
button.ok=OK
button.back=Precedente
@@ -597,6 +632,7 @@ button.yes=S\u00ec
button.no=No
button.yestoall=Si a tutto
button.notoall=No a tutto
+button.always=Sempre
button.select=Seleziona
button.selectall=Seleziona tutto
button.selectnone=Deseleziona tutto
@@ -685,7 +721,6 @@ fieldname.newsegment=Segmento
fieldname.custom=Custom
fieldname.prefix=Campo
fieldname.distance=Distanza
-fieldname.movingdistance=Distanza in movimento
fieldname.duration=Durata
fieldname.speed=Velocit\u00e0
fieldname.verticalspeed=Velocit\u00e0 verticale
@@ -720,6 +755,10 @@ units.degminsec=Deg-min-sec
units.degmin=Deg-min
units.deg=Degrees
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=e
@@ -728,6 +767,7 @@ logic.or=o
# External urls
url.googlemaps=maps.google.it
wikipedia.lang=it
+openweathermap.lang=it
# Cardinals for 3d plots
cardinal.n=N
@@ -749,6 +789,8 @@ undo.deletemarked=elimina punti
undo.insert=inserisci punti
undo.reverse=inverti l'intervallo
undo.mergetracksegments=unisci segmenti traccia
+undo.splitsegments=dividi traccia
+undo.sewsegments=riorganizza segmenti traccia
undo.addtimeoffset=aggiungi scarto temporale
undo.addaltitudeoffset=aggiungi scarto altitudine
undo.rearrangewaypoints=riorganizza waypoint
@@ -771,10 +813,8 @@ 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.saveexif.failed=Salvataggio fallito%d delle immagini
+error.saveexif.forced=%d 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
@@ -785,7 +825,7 @@ error.jpegload.dialogtitle=Errore nel caricamento delle foto
error.jpegload.nofilesfound=File non trovato
error.jpegload.nojpegsfound=File jpeg non trovato
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.jpegload.exifreadfailed=Lettera dei dati Exif fallita. I dati Exif non possono\n essere letti senza una libreria interna o esterna.
error.audioload.nofilesfound=Riprese audio non trovate
error.gpsload.unknown=Errore sconosciuto
error.undofailed.title=Impossibile annullare
@@ -811,3 +851,6 @@ error.cache.empty=Directory del cache di tasselli \u00e8 vuoto
error.cache.cannotdelete=Impossibile cancellare tasselli
error.interpolate.invalidparameter=Il numero di punti deve essere tra 1 e 1000
error.learnestimationparams.failed=Non \u00e8 possibile apprendere i parametri da questa traccia.\nProva a caricare pi\u00f9 tracce.
+error.tracksplit.nosplit=La traccia non pu\u00f2 essere divisa
+error.downloadsrtm.nocache=Non \u00e8 stato possibile salvare i file.\nControlla la cache del disco.
+error.sewsegments.nothingdone=Non \u00e8 stato possibile riorganizzare nessun segmento.\nCi sono %d segmenti nella traccia.
diff --git a/tim/prune/lang/prune-texts_ja.properties b/tim/prune/lang/prune-texts_ja.properties
index 142ae84..ad25d69 100644
--- a/tim/prune/lang/prune-texts_ja.properties
+++ b/tim/prune/lang/prune-texts_ja.properties
@@ -118,7 +118,7 @@ function.rearrangephotos=\u5199\u771f\u306e\u4e26\u3079\u76f4\u3057
function.rotatephotoleft=\u5199\u771f\u3092\u5de6\u306b\u56de\u3059
function.rotatephotoright=\u5199\u771f\u3092\u53f3\u306b\u56de\u3059
function.photopopup=\u5199\u771f\u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3092\u8868\u793a
-function.ignoreexifthumb=EXIF\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u7121\u8996
+function.ignoreexifthumb=Exif\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u7121\u8996
function.loadaudio=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0
function.removeaudio=\u4e00\u89a7\u304b\u3089\u73fe\u5728\u306e\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u3092\u524a\u9664
function.correlateaudios=\u30aa\u30fc\u30c7\u30a3\u30aa\u3092\u95a2\u9023\u4ed8\u3051\u308b
@@ -225,7 +225,6 @@ dialog.undo.none.text=\u30a2\u30f3\u30c9\u30a5\u3067\u304d\u308b\u4f5c\u696d\u75
dialog.clearundo.title=\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3059\u308b
dialog.clearundo.text=\u672c\u5f53\u306b\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3057\u307e\u3059\u304b?\n\u5168\u3066\u306e\u30a2\u30f3\u30c9\u30a5\u60c5\u5831\u304c\u5931\u308f\u308c\u307e\u3059\u3002
dialog.pointedit.title=\u70b9\u3092\u7de8\u96c6
-dialog.pointedit.text=\u7de8\u96c6\u3059\u308b\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u9078\u629e\u3057\u3001'\u7de8\u96c6' \u30dc\u30bf\u30f3\u3067\u5024\u3092\u7de8\u96c6\u3057\u3066\u304f\u3060\u3055\u3044\u3002
dialog.pointedit.table.field=\u30d5\u30a3\u30fc\u30eb\u30c9
dialog.pointedit.table.value=\u503c
dialog.pointnameedit.name=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u540d
@@ -240,7 +239,7 @@ dialog.addtimeoffset.minutes=\u5206
dialog.addtimeoffset.notimestamps=\u3053\u306e\u9078\u629e\u7bc4\u56f2\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u6301\u3063\u3066\u306a\u3044\u306e\u3067\u3001\u504f\u4f4d\u3092\u8db3\u305b\u307e\u305b\u3093\u3002
dialog.findwaypoint.intro=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u540d\u306e\u4e00\u90e8\u3092\u5165\u529b
dialog.findwaypoint.search=\u691c\u7d22
-dialog.saveexif.title=EXIF\u3092\u4fdd\u5b58
+dialog.saveexif.title=Exif\u3092\u4fdd\u5b58
dialog.saveexif.intro=\u30c1\u30a7\u30c3\u30af\u30dc\u30c3\u30af\u30b9\u3092\u4f7f\u3063\u3066\u3001\u4fdd\u5b58\u3059\u308b\u5199\u771f\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002
dialog.saveexif.nothingtosave=\u5ea7\u6a19\u60c5\u5831\u306f\u7121\u5909\u66f4\u3067\u3059\u3002\u4fdd\u5b58\u3059\u308b\u3082\u306e\u306f\u3042\u308a\u307e\u305b\u3093\u3002
dialog.saveexif.noexiftool=exiftool \u30d7\u30ed\u30b0\u30e9\u30e0\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u7d9a\u3051\u307e\u3059\u304b\uff1f
@@ -296,7 +295,7 @@ dialog.correlate.photoselect.intro=\u6642\u9593\u504f\u4f4d\u3092\u4f5c\u308b\u3
dialog.correlate.select.photoname=\u5199\u771f\u540d
dialog.correlate.select.timediff=\u6642\u9593\u5dee
dialog.correlate.select.photolater=\u5199\u771f\u304c\u5f8c
-dialog.correlate.options.tip=\u63d0\u793a\uff1a\u5c11\u306a\u304f\u3068\u3082\u4e00\u3064\u306e\u5199\u771f\u3092\u624b\u52d5\u3067\u95a2\u9023\u4ed8\u3051\u308b\u3068\u3001\u6642\u9593\u504f\u4f4d\u3092\u8a08\u7b97\u3059\u308b\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002
+tip.manuallycorrelateone=\u63d0\u793a\uff1a\u5c11\u306a\u304f\u3068\u3082\u4e00\u3064\u306e\u5199\u771f\u3092\u624b\u52d5\u3067\u95a2\u9023\u4ed8\u3051\u308b\u3068\u3001\u6642\u9593\u504f\u4f4d\u3092\u8a08\u7b97\u3059\u308b\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002
dialog.correlate.options.intro=\u81ea\u52d5\u95a2\u9023\u4ed8\u3051\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002
dialog.correlate.options.offsetpanel=\u6642\u9593\u504f\u4f4d
dialog.correlate.options.offset=\u504f\u4f4d
@@ -336,7 +335,7 @@ dialog.deletemarked.nonefound=\u9664\u304f\u3053\u3068\u304c\u3067\u304d\u308b\u
dialog.pastecoordinates.desc=\u5ea7\u6a19\u3092\u3053\u3053\u306b\u5165\u529b\u3059\u308b\u304b\u30da\u30fc\u30b9\u30c8\u3057\u3066\u304f\u3060\u3055\u3044\u3002
dialog.pastecoordinates.coords=\u5ea7\u6a19
dialog.pastecoordinates.nothingfound=\u5ea7\u6a19\u306e\u78ba\u8a8d\u3092\u3057\u3066\u3001\u3082\u3046\u4e00\u5ea6\u8a66\u3057\u3066\u304f\u3060\u3055\u3044\u3002
-dialog.help.help=\u8a73\u3057\u3044\u60c5\u5831\u3084\u3001\u30e6\u30fc\u30b6\u30fc\u30ac\u30a4\u30c9\u306a\u3069\u306f\u3001\nhttp://activityworkshop.net/software/gpsprune/ \u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.help.help=\u8a73\u3057\u3044\u60c5\u5831\u3084\u3001\u30e6\u30fc\u30b6\u30fc\u30ac\u30a4\u30c9\u306a\u3069\u306f\u3001\nhttp://gpsprune.activityworkshop.net/ \u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002
dialog.about.version=\u30d0\u30fc\u30b8\u30e7\u30f3
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune \u306f\u3001GPS\u53d7\u4fe1\u6a5f\u304b\u3089\u306e\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u307f\u3001\u8868\u793a\u3057\u3001\u7de8\u96c6\u3059\u308b\u305f\u3081\u306e\u30d7\u30ed\u30b0\u30e9\u30e0\u3067\u3059\u3002
@@ -371,7 +370,7 @@ dialog.checkversion.newversion1=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306e GpsPr
dialog.checkversion.newversion2=\u3067\u3059\u3002
dialog.checkversion.releasedate1=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306f\u3001
dialog.checkversion.releasedate2=\u306b\u30ea\u30ea\u30fc\u30b9\u3057\u307e\u3057\u305f\u3002
-dialog.checkversion.download=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f\u3001 http://activityworkshop.net/software/gpsprune/download.html \u3078\u884c\u3063\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.checkversion.download=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f\u3001 http://gpsprune.activityworkshop.net/download.html \u3078\u884c\u3063\u3066\u304f\u3060\u3055\u3044\u3002
dialog.keys.intro=\u30de\u30a6\u30b9\u306e\u4ee3\u308f\u308a\u306b\u6b21\u306e\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\u30ad\u30fc\u3092\u4f7f\u3046\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002
dialog.keys.keylist=<table><tr><td>\u77e2\u5370\u30ad\u30fc</td><td>\u5730\u56f3\u3092\u4e0a\u4e0b\u5de6\u53f3\u306b\u79fb\u52d5</td></tr><tr><td>Ctrl + \u5de6\u30fb\u53f3\u77e2\u5370</td><td>\u524d\u30fb\u6b21\u306e\u70b9\u3092\u9078\u629e</td></tr><tr><td>Ctrl + \u4e0a\u30fb\u4e0b\u77e2\u5370</td><td>\u62e1\u5927\u30fb\u7e2e\u5c0f</td></tr><tr><td>Del</td><td>\u73fe\u5728\u306e\u70b9\u3092\u524a\u9664</td></tr></table>
dialog.keys.normalmodifier=Ctrl\u30ad\u30fc
@@ -390,7 +389,7 @@ dialog.saveconfig.prune.exiftoolpath=exiftool\u3078\u306e\u30d1\u30b9
dialog.saveconfig.prune.mapsource=\u30de\u30c3\u30d7\u30fb\u30bd\u30fc\u30b9\u3092\u9078\u629e
dialog.saveconfig.prune.mapsourcelist=\u30de\u30c3\u30d7\u30fb\u30bd\u30fc\u30b9
dialog.saveconfig.prune.diskcache=\u30de\u30c3\u30d7\u306e\u30ad\u30e3\u30c3\u30b7\u30e5
-dialog.saveconfig.prune.kmzimagewidth=KMZ \u753b\u50cf\u5e45
+dialog.saveconfig.prune.kmzimagewidth=KML \u753b\u50cf\u5e45
dialog.saveconfig.prune.colourscheme=\u8272\u306e\u30b9\u30ad\u30fc\u30e0
dialog.saveconfig.prune.linewidth=\u7dda\u306e\u5e45
dialog.saveconfig.prune.kmltrackcolour=KML \u30c8\u30e9\u30c3\u30af\u306e\u8272
@@ -440,8 +439,7 @@ confirm.rearrangewaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u304c\u4e2
confirm.rearrangephotos=\u5199\u771f\u304c\u4e26\u3079\u66ff\u3048\u3089\u308c\u305f
confirm.cutandmove=\u9078\u629e\u304c\u52d5\u304b\u3055\u308c\u305f
confirm.convertnamestotimes=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u306e\u540d\u524d\u304c\u5909\u63db\u3055\u308c\u305f
-confirm.saveexif.ok1=\u4fdd\u5b58\u3055\u308c\u305f
-confirm.saveexif.ok2=\u5199\u771f\u30d5\u30a1\u30a4\u30eb
+confirm.saveexif.ok=\u4fdd\u5b58\u3055\u308c\u305f %d \u5199\u771f\u30d5\u30a1\u30a4\u30eb
confirm.undo.single=\u64cd\u4f5c\u306f\u30a2\u30f3\u30c9\u30a5\u3055\u308c\u305f
confirm.undo.multi=\u64cd\u4f5c\u306f\u30a2\u30f3\u30c9\u30a5\u3055\u308c\u305f
confirm.jpegload.single=\u5199\u771f\u304c\u52a0\u3048\u3089\u308c\u305f
@@ -455,8 +453,7 @@ confirm.correlatephotos.multi=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u
confirm.createpoint=\u70b9\u304c\u4f5c\u3089\u308c\u305f
confirm.rotatephoto=\u5199\u771f\u3092\u56de\u8ee2\u3057\u305f
confirm.running=\u5b9f\u884c\u4e2d...
-confirm.lookupsrtm1=
-confirm.lookupsrtm2=\u6a19\u9ad8\u5024
+confirm.lookupsrtm=%d \u6a19\u9ad8\u5024
confirm.deletefieldvalues=\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u5024\u304c\u524a\u9664\u3055\u308c\u305f
confirm.audioload=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u304c\u8ffd\u52a0\u3055\u308c\u305f
confirm.correlateaudios.single=\u30aa\u30fc\u30c7\u30a3\u30aa\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
@@ -562,7 +559,6 @@ fieldname.newsegment=\u30bb\u30b0\u30e1\u30f3\u30c8
fieldname.custom=\u30ab\u30b9\u30bf\u30e0
fieldname.prefix=\u30d5\u30a3\u30fc\u30eb\u30c9
fieldname.distance=\u8ddd\u96e2
-fieldname.movingdistance=\u79fb\u52d5\u8ddd\u96e2
fieldname.duration=\u9593\u9694
fieldname.speed=\u901f\u5ea6
fieldname.verticalspeed=\u5782\u76f4\u901f\u5ea6
@@ -634,10 +630,8 @@ error.save.failed=\u30c7\u30fc\u30bf\u3092\u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u
error.saveexif.filenotfound=\u5199\u771f\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f
error.saveexif.cannotoverwrite1=\u5199\u771f\u30d5\u30a1\u30a4\u30eb
error.saveexif.cannotoverwrite2=\u8aad\u307f\u8fbc\u307f\u5c02\u7528\u3067\u4e0a\u66f8\u304d\u3067\u304d\u307e\u305b\u3093\u3002\u8907\u88fd\u3092\u4f5c\u308a\u307e\u3059\u304b\uff1f
-error.saveexif.failed1=
-error.saveexif.failed2=\u679a\u306e\u753b\u50cf\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f
-error.saveexif.forced1=
-error.saveexif.forced2=\u679a\u306e\u753b\u50cf\u304c\u5f37\u884c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002
+error.saveexif.failed=%d \u679a\u306e\u753b\u50cf\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+error.saveexif.forced=%d \u679a\u306e\u753b\u50cf\u304c\u5f37\u884c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002
error.load.dialogtitle=\u30c7\u30fc\u30bf\u306e\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc
error.load.noread=\u30d5\u30a1\u30a4\u30eb\u304c\u8aad\u3081\u307e\u305b\u3093
error.load.nopoints=\u30d5\u30a1\u30a4\u30eb\u306e\u4e2d\u306b\u5ea7\u6a19\u60c5\u5831\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
diff --git a/tim/prune/lang/prune-texts_ko.properties b/tim/prune/lang/prune-texts_ko.properties
index 2904a27..ec5de7d 100644
--- a/tim/prune/lang/prune-texts_ko.properties
+++ b/tim/prune/lang/prune-texts_ko.properties
@@ -219,7 +219,6 @@ dialog.undo.none.text=\ub418\ub3cc\ub9b4 \uc791\uc5c5\uc774 \uc5c6\uc5b4\uc694!
dialog.clearundo.title=\ub418\ub3cc\ub9ac\uae30 \ubaa9\ub85d \uc9c0\uc6b0\uae30
dialog.clearundo.text=\uc815\ub9d0\ub85c \ub418\ub3cc\ub9ac\uae30 \ubaa9\ub85d \uc9c0\uc6b0\uc2e4\uac74\uac00\uc694? /n \ubaa8\ub4e0 \ub418\ub3cc\ub9ac\uae30 \uc815\ubcf4\uac00 \uc5c6\uc5b4\uc9c4\ub2e4\uad6c\uc694!
dialog.pointedit.title=\uc9c0\uc810 \uc218\uc815\ud558\uae30
-dialog.pointedit.text=\uc218\uc815\ud560 \ud544\ub4dc\ub97c \uc120\ud0dd\ud558\uc2dc\uace0, \uc218\uc815\ud558\uae30 \ubc84\ud2bc\uc744 \uc0ac\uc6a9\ud558\uc138\uc694.
dialog.pointedit.table.field=\ud544\ub4dc
dialog.pointedit.table.value=\uac12
dialog.pointnameedit.name=\uacbd\uc720\uc9c0 \uc774\ub984
@@ -295,7 +294,6 @@ dialog.correlate.photoselect.intro=\ub300\ud45c\uc0ac\uc9c4\uc73c\ub85c \uc0ac\u
dialog.correlate.select.photoname=\uc0ac\uc9c4\uc774\ub984
dialog.correlate.select.timediff=\uc0ac\uae34 \ucc28\uc774
dialog.correlate.select.photolater=\uc0ac\uc9c4 \uc2dc\uac04\uc774 \uc9c0\uc810\uc2dc\uac04\ubcf4\ub2e4 \ub290\ub9bc
-dialog.correlate.options.tip=\ub3c4\uc6c0: \ucd5c\uc18c\ud55c \ud55c\uac1c\uc758 \uc544\uc774\ud0ec(\uc0ac\uc9c4,\uc18c\ub9ac)\uc744 \uc9c1\uc811 \uc5f0\uacb0\ud558\uba74, \ud0c0\uc784 \uc624\ud504\uc14b\uc744 \uacc4\uc0b0\ud560 \uc218 \uc788\uc5b4\uc694.
dialog.correlate.options.intro=\uc790\ub3d9 \uc5f0\uacb0\uc744 \uc704\ud574 \uc635\uc158\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694.
dialog.correlate.options.offsetpanel=\uc2dc\uac04 \uc624\ud504\uc14b
dialog.correlate.options.offset=\uc624\ud504\uc14b
@@ -340,7 +338,7 @@ dialog.compress.summarylabel=\uc0ad\uc81c\ud560 \uc9c0\uc810
dialog.pastecoordinates.desc=\uc790\ud45c\ub97c \ub123\uc73c\uc138\uc694
dialog.pastecoordinates.coords=\uc88c\ud45c
dialog.pastecoordinates.nothingfound=\uc88c\ud45c\ub97c \ud655\uc778\ud558\uc2dc\uace0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574 \ubcf4\uc138\uc694.
-dialog.help.help=\ub354 \uc790\uc138\ud55c \uc815\ubcf4\ub098 \uc0ac\uc6a9\uc790\uc124\uba85\uc740 \uc774\uacf3\uc744 \ucc38\uace0\ud574\uc8fc\uc138\uc694.\n http://activityworkshop.net/software/gpsprune/
+dialog.help.help=\ub354 \uc790\uc138\ud55c \uc815\ubcf4\ub098 \uc0ac\uc6a9\uc790\uc124\uba85\uc740 \uc774\uacf3\uc744 \ucc38\uace0\ud574\uc8fc\uc138\uc694.\n http://gpsprune.activityworkshop.net/
dialog.about.version=\ubc84\uc804
dialog.about.build=\ube4c\ub4dc
dialog.about.summarytext1=GpsPrune\uc740 GPS\uc218\uc2e0\uae30\uc5d0\uc11c \uc704\uce58 \uc815\ubcf4\ub97c \ubc1b\uace0, \ud654\uba74\uc5d0 \ubcf4\uc5ec\uc8fc\uace0, \uc218\uc815\ud558\uac8c \ud574\uc8fc\ub294 \ud504\ub85c\uadf8\ub7a8\uc785\ub2c8\ub2e4.
@@ -379,7 +377,7 @@ dialog.checkversion.newversion1=GpsPrune\uc758 \uc0c8 \ubc84\uc804\uc744 \uc9c0\
dialog.checkversion.newversion2=\ubc84\uc804\uc785\ub2c8\ub2e4.
dialog.checkversion.releasedate1=\uc0c8 \ubc84\uc804\uc740
dialog.checkversion.releasedate2=\uc5d0 \ubc30\ud3ec\ub418\uc5c8\uc2b5\ub2c8\ub2e4.
-dialog.checkversion.download=\uc0c8 \ubc84\uc804\uc744 \ub2e4\uc6b4\ubc1b\uace0 \uc2f6\uc73c\uc138\uc694? \uadf8\ub7fc \uc544\ub798 URL\ub85c \uc640\uc8fc\uc138\uc694. /n http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=\uc0c8 \ubc84\uc804\uc744 \ub2e4\uc6b4\ubc1b\uace0 \uc2f6\uc73c\uc138\uc694? \uadf8\ub7fc \uc544\ub798 URL\ub85c \uc640\uc8fc\uc138\uc694. /n http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=\ub9c8\uc6b0\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \ub9c8\uc2dc\uace0 \uc544\ub798 \ub2e8\ucd95\ud0a4\ub97c \uc0ac\uc6a9\ud574\ubcf4\uc138\uc694.
dialog.keys.keylist=<table><tr><td>\ud654\uc0b4\ud45c \ud0a4</td><td>\uc88c,\uc6b0,\uc544\ub798,\uc704\ub85c \uc9c0\ub3c4 \uc774\ub3d9</td></tr><tr><td>Ctrl + \uc67c\ucabd, \uc624\ub978\ucabd \ud654\uc0b4\ud45c</td><td>\uc9c0\uc810 \uc120\ud0dd \uc774\ub3d9</td></tr><tr><td>Ctrl + \uc704, \uc544\ub798 \ud654\uc0b4\ud45c</td><td>\uc9c0\ub3c4 \ud655\ub300 \ucd95\uc18c</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>\uc774\uc804 \uc774\ud6c4 \ubd80\ubd84 \uc120\ud0dd</td></tr><tr><td>Ctrl + Ho [...]
dialog.keys.normalmodifier=Ctrl
@@ -453,8 +451,7 @@ confirm.rearrangewaypoints=\uacbd\uc720\uc9c0\uac00 \uc7ac\uc815\ub82c\ub418\uc5
confirm.rearrangephotos=\uc0ac\uc9c4\ub4e4\uc774 \uc7ac\uc815\ub82c\ub418\uc5c8\uc5b4\uc694.
confirm.cutandmove=\uc62e\uaca8\uc84c\uc5b4\uc694.
confirm.convertnamestotimes=\uacbd\uc720\uc9c0 \uc774\ub984\ub4e4\uc774 \ubcc0\ud658\ub418\uc5c8\uc5b4\uc694.
-confirm.saveexif.ok1=\uc800\uc7a5\ub428
-confirm.saveexif.ok2=\uc0ac\uc9c4 \ud30c\uc77c\ub4e4
+confirm.saveexif.ok=\uc800\uc7a5\ub428 %d \uc0ac\uc9c4 \ud30c\uc77c\ub4e4
confirm.undo.single=\uac1c \uc2e4\ud589\ucde8\uc18c \ub3d9\uc791
confirm.undo.multi=\uac1c \uc2e4\ud589\ucde8\uc18c \ub3d9\uc791
confirm.jpegload.single=\uc0ac\uc9c4\uc774 \ucd94\uac00\ub428
@@ -468,13 +465,16 @@ confirm.correlatephotos.multi=\uc0ac\uc9c4\ub4e4 \uc5f0\uacb0\ub428
confirm.createpoint=\uc9c0\uc810 \uc0dd\uc131\ub428
confirm.rotatephoto=\uc0ac\uc9c4 \ub3cc\ub824\uc9d0
confirm.running=\uc2e4\ud589\uc911
-confirm.lookupsrtm1=
-confirm.lookupsrtm2=\uace0\ub3c4\uac12 \ubc1c\uacac
+confirm.lookupsrtm=%d \uace0\ub3c4\uac12 \ubc1c\uacac
confirm.deletefieldvalues=\ud544\ub4dc\uac12 \uc9c0\uc6cc\uc9d0
confirm.audioload=\uc18c\ub9ac\ud30c\uc77c \ucd94\uac00\ub428
confirm.correlateaudios.single=\uc18c\ub9ac \uc5f0\uacb0\ub428
confirm.correlateaudios.multi=\uc18c\ub9ac\ub4e4 \uc5f0\uacb0\ub428
+# Tips
+tip.title=\ub3c4\uc6c0
+tip.manuallycorrelateone=\ucd5c\uc18c\ud55c \ud55c\uac1c\uc758 \uc544\uc774\ud0ec(\uc0ac\uc9c4,\uc18c\ub9ac)\uc744 \uc9c1\uc811 \uc5f0\uacb0\ud558\uba74, \ud0c0\uc784 \uc624\ud504\uc14b\uc744 \uacc4\uc0b0\ud560 \uc218 \uc788\uc5b4\uc694.
+
# Buttons
button.ok=\ud655\uc778
button.back=\ub4a4\ub85c
@@ -574,7 +574,6 @@ fieldname.newsegment=\ubd80\ubd84
fieldname.custom=\ucee4\uc2a4\ud140
fieldname.prefix=\ud544\ub4dc
fieldname.distance=\uac70\ub9ac
-fieldname.movingdistance=\uc6c0\uc9c0\uc778\uac70\ub9ac
fieldname.duration=\uae30\uac04
fieldname.speed=\uc18d\ub3c4
fieldname.verticalspeed=\uc218\uc9c1\uc18d\ub3c4
@@ -645,10 +644,8 @@ error.save.failed=\ud30c\uc77c\uc5d0 \uc790\ub8cc\uc800\uc7a5 \uc2e4\ud328
error.saveexif.filenotfound=\uc0ac\uc9c4\ud30c\uc77c \ucc3e\uae30 \uc2e4\ud328
error.saveexif.cannotoverwrite1=\uc0ac\uc9c4\ud30c\uc77c
error.saveexif.cannotoverwrite2=\uc774 \uc77d\uae30\uc804\uc6a9\uc774\ub124\uc694, \ub36e\uc5b4\uc4f8\uc218 \uc5c6\uc5b4\uc694 \ubcf5\uc0ac\ud574\uc11c \uc4f8\uae4c\uc694?
-error.saveexif.failed1=
-error.saveexif.failed2=\uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc2e4\ud328
-error.saveexif.forced1=
-error.saveexif.forced2=\uc774\ubbf8\uc9c0\uac00 \uac15\uc81c\ub97c \uc694\uad6c\ud569\ub2c8\ub2e4.
+error.saveexif.failed=%d \uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc2e4\ud328
+error.saveexif.forced=%d \uc774\ubbf8\uc9c0\uac00 \uac15\uc81c\ub97c \uc694\uad6c\ud569\ub2c8\ub2e4.
error.load.dialogtitle=\uc790\ub8cc \ubd88\ub7ec\uc624\ub2e4\uac00 \uc5d0\ub7ec
error.load.noread=\ud30c\uc77c\uc744 \uc77d\uc744 \uc218 \uc5c6\ub124\uc694.
error.load.nopoints=\ud30c\uc77c\uc5d0 \uc88c\ud45c \uc815\ubcf4\uac00 \uc5c6\uc5b4\uc694.
@@ -659,7 +656,7 @@ error.jpegload.dialogtitle=\uc0ac\uc9c4 \ubd88\ub7ec\uc624\ub294 \uc911 \uc5d0\u
error.jpegload.nofilesfound=\ucc3e\uc740 \ud30c\uc77c \uc5c6\uc74c.
error.jpegload.nojpegsfound=\ucc3e\uc740 jpeg\ud30c\uc77c \uc5c6\uc74c.
error.jpegload.nogpsfound=GPS \uc815\ubcf4\ub97c \ucc3e\uc9c0 \ubabb\ud568.
-error.jpegload.exifreadfailed=EXIF\uc815\ubcf4 \uc77d\uae30 \uc2e4\ud328./n\ub0b4\uc7a5\uc774\ub098 \uc678\uc7a5 \ub77c\uc774\ube0c\ub7ec\uc774\uac00 \uc5c6\uc73c\uba74/nEXIF\uc815\ubcf4\ub97c \uc77d\uc744 \uc218 \uc5c6\uc5b4\uc694.
+error.jpegload.exifreadfailed=Exif\uc815\ubcf4 \uc77d\uae30 \uc2e4\ud328./n\ub0b4\uc7a5\uc774\ub098 \uc678\uc7a5 \ub77c\uc774\ube0c\ub7ec\uc774\uac00 \uc5c6\uc73c\uba74/nExif\uc815\ubcf4\ub97c \uc77d\uc744 \uc218 \uc5c6\uc5b4\uc694.
error.audioload.nofilesfound=\ucc3e\uc740 \uc18c\ub9ac\ud30c\uc77c \uc5c6\uc74c.
error.gpsload.unknown=\uc54c\ub824\uc9c0\uc9c0 \uc54a\uc740 \uc624\ub958.
error.undofailed.title=\ub418\ub3cc\ub9ac\uae30 \uc2e4\ud328.
diff --git a/tim/prune/lang/prune-texts_nl.properties b/tim/prune/lang/prune-texts_nl.properties
index 8915ccb..ac4b797 100644
--- a/tim/prune/lang/prune-texts_nl.properties
+++ b/tim/prune/lang/prune-texts_nl.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=Foto's toevoegen
menu.file.recentfiles=Onlangs geopend
menu.file.save=Opslaan als tekst
menu.file.exit=Afsluiten
+menu.online=Online
menu.track=Route
menu.track.undo=Ongedaan maken
menu.track.clearundo=Ongedaan-maken lijst wissen
@@ -57,6 +58,7 @@ menu.map.editmode=Wijzigen
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=O
altkey.menu.track=R
altkey.menu.range=E
altkey.menu.point=P
@@ -104,9 +106,12 @@ function.estimatetime=Geschatte tijd
function.learnestimationparams=Parameters voor geschatte tijd
function.setmapbg=Instellen kaart achtergrond
function.setpaths=Instellen programmapaden
+function.splitsegments=Splits route in segmenten
+function.sewsegments=Voeg segmenten samen
function.getgpsies=Routes van Gpsies ophalen
function.uploadgpsies=Upload routes naar Gpsies
function.lookupsrtm=Hoogtes van SRTM ophalen
+function.downloadsrtm=Downloaden SRTM tegels
function.getwikipedia=Wikipedia artikelen uit de buurt ophalen
function.searchwikipedianames=Wikipedia zoeken op naam
function.downloadosm=Downloaden OSM data voor gebied
@@ -135,6 +140,7 @@ function.checkversion=Controleer op nieuwe versie
function.saveconfig=Instellingen opslaan
function.diskcache=Kaart opslaan op schijf
function.managetilecache=Beheer tegelcache
+function.getweatherforecast=Ophalen weersvoorspelling
# Dialogs
dialog.exit.confirm.title=GpsPrune afsluiten
@@ -242,6 +248,8 @@ dialog.exportpov.modelstyle=Model stijl
dialog.exportpov.ballsandsticks=Balletjes en stokjes
dialog.exportpov.tubesandwalls=Buizen en muren
dialog.3d.warningtracksize=Deze route heeft een groot aantal punten. Java3D kan deze mogelijk niet tonen.\nWeet u zeker dat u door wilt gaan?
+dialog.3d.useterrain=Toon reli\u00ebf
+dialog.3d.terraingridsize=Rasterformaat
dialog.exportpov.baseimage=Basisafbeelding
dialog.exportpov.cannotmakebaseimage=Kan basisafbeelding niet opslaan
dialog.baseimage.title=Basisafbeelding
@@ -257,6 +265,7 @@ dialog.exportsvg.theta=Stijgingshoek \u03b8
dialog.exportsvg.gradients=Gebruik gradaties voor schaduw
dialog.exportimage.noimagepossible=Kaartafbeeldingen dienen gecached te worden naar disk om ze te kunnen exporteren.
dialog.exportimage.drawtrack=Teken route op kaart
+dialog.exportimage.drawtrackpoints=Teken routepunten
dialog.exportimage.textscalepercent=Tekstschaal factor (%)
dialog.pointtype.desc=Sla de volgende punttypen op:
dialog.pointtype.track=Routepunten
@@ -378,7 +387,6 @@ dialog.correlate.photoselect.intro=Selecteer \u00e9\u00e9n van deze gekoppelde f
dialog.correlate.select.photoname=Fotonaam
dialog.correlate.select.timediff=Tijdsverschil
dialog.correlate.select.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
@@ -421,14 +429,13 @@ dialog.compress.duplicates.title=Verwijderen duplicaten
dialog.compress.douglaspeucker.title=Douglas-Peucker compressie
dialog.compress.douglaspeucker.paramdesc=Span factor
dialog.compress.summarylabel=Te verwijderen punten
-dialog.compress.confirm1=Er zijn
-dialog.compress.confirm2=punten zijn gemarkeerd.\nGebruik Track - Verwijder gemarkeerde punten om ze te verwijderen
+dialog.compress.confirm=Er zijn %d punten zijn gemarkeerd.\nVerwijder gemarkeerde punten?
dialog.compress.confirmnone=er zijn geen punten gemarkeerd
dialog.deletemarked.nonefound=Er konden geen punten verwijderd worden
dialog.pastecoordinates.desc=Geef co\u00f6rdinaten in
dialog.pastecoordinates.coords=Co\u00f6rdinaten
dialog.pastecoordinates.nothingfound=Controleer de co\u00f6rdinaten en probeer het nogmaals
-dialog.help.help=Ga naar\n http://activityworkshop.net/software/gpsprune/\nvoor meer informatie en handleidingen.
+dialog.help.help=Ga naar\n http://gpsprune.activityworkshop.net/\nvoor meer informatie en handleidingen.
dialog.about.version=Versie
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune is een programma voor het laden, tonen en wijzigen van data uit GPS ontvangers.
@@ -467,7 +474,7 @@ dialog.checkversion.newversion1=Een nieuwe versie van GpsPrune is beschikbaar. D
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/gpsprune/download.html.
+dialog.checkversion.download=Om de nieuwst versie te downloaden, ga naar http://gpsprune.activityworkshop.net/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
@@ -530,13 +537,32 @@ dialog.diskcache.tileset.multiple=meerdere
dialog.diskcache.deleteold=Verwijder oude tegels
dialog.diskcache.maximumage=Maximum leeftijd (dagen)
dialog.diskcache.deleteall=Verwijder alle tegels
-dialog.diskcache.deleted1=
-dialog.diskcache.deleted2=bestanden uit de cache verwijderd
+dialog.diskcache.deleted=%d bestanden uit de cache verwijderd
dialog.deletefieldvalues.intro=Selecteer het te verwijderen veld voor de huidige reeks
dialog.deletefieldvalues.nofields=Er zijn geen velden in deze reeks om te verwijderen
dialog.setlinewidth.text=Geef lijndikte voor routes (1-4)
dialog.downloadosm.desc=Bevestig het downloaden van ruwe OSM data voor dit gebied:
dialog.searchwikipedianames.search=Zoeken naar:
+dialog.weather.location=Locatie
+dialog.weather.update=Verwachting bijgewerkt
+dialog.weather.sunrise=Zonsopkomst
+dialog.weather.sunset=Zonsondergang
+dialog.weather.temperatureunits=Temperaturen
+dialog.weather.currentforecast=Huidig weer
+dialog.weather.dailyforecast=Dagelijkse voorspelling
+dialog.weather.3hourlyforecast=3-uurse voorspelling
+dialog.weather.day.now=Huidig weer
+dialog.weather.day.today=Vandaag
+dialog.weather.day.tomorrow=Morgen
+dialog.weather.day.monday=Maandag
+dialog.weather.day.tuesday=Dinsdag
+dialog.weather.day.wednesday=Woensdag
+dialog.weather.day.thursday=Donderdag
+dialog.weather.day.friday=Vrijdag
+dialog.weather.day.saturday=Zaterdag
+dialog.weather.day.sunday=Zondag
+dialog.weather.humidity=Luchtvocht.
+dialog.weather.creditnotice=Deze gegevens worden beschikbaar gesteld door openweathermap.org. Hun website heeft meer details.
# 3d window
dialog.3d.title=GpsPrune in 3D
@@ -555,11 +581,12 @@ confirm.addtimeoffset=Tijdsverschil toegevoegd
confirm.addaltitudeoffset=Hoogteverschil toegevoegd
confirm.rearrangewaypoints=Waypoints herschikt
confirm.rearrangephotos=Foto herschikt
+confirm.splitsegments=Er zijn %d opdelingen gemaakt
+confirm.sewsegments=Er zijn %d samenvoegingen gemaakt
confirm.cutandmove=Selectie verplaatst
confirm.interpolate=Punten toegevoegd
confirm.convertnamestotimes=Namen waypoint geconverteerd
-confirm.saveexif.ok1=Opgeslagen
-confirm.saveexif.ok2=foto bestanden
+confirm.saveexif.ok=Opgeslagen %d foto bestanden
confirm.undo.single=Actie geannuleerd
confirm.undo.multi=Acties geannuleerd
confirm.jpegload.single=foto was toegevoegd
@@ -573,13 +600,23 @@ confirm.correlatephotos.multi=Foto's waren gecorreleerd
confirm.createpoint=punt aangemaakt
confirm.rotatephoto=foto geroteerd
confirm.running=Bezig...
-confirm.lookupsrtm1=Gevonden
-confirm.lookupsrtm2=hoote waarden
+confirm.lookupsrtm=Gevonden %d hoote waarden
+confirm.downloadsrtm=Er zijn %d bestanden gedownload nar de cache
+confirm.downloadsrtm.1=Er zijn %d bestanden gedownload nar de cache
+confirm.downloadsrtm.none=Geen bestanden gedownload, waren al aanwezig in de cache.
confirm.deletefieldvalues=Veldwaarden gewist
confirm.audioload=Audiobestanden toegevoegd
confirm.correlateaudios.single=audiobestand gecorreleerd
confirm.correlateaudios.multi=audiobestanden gecorreleerd
+# Tips, shown just once when appropriate
+tip.title=Tip
+tip.useamapcache=Door het instellen van een schijfcache (Instellingen -> Kaart opslaan op schijf<span style="color: #ff0000;">)\nkan je de afbeeldsnelheid verbeteren en het netwerkverkeer verminderen.</span>
+tip.learntimeparams=De resultaten zullen nauwkeuriger zijn als je \nRoute -> Parameters voor geschatte tijd\ngebruikt op je opgenomen routes.
+tip.downloadsrtm=Je kan dit versnellen door hier\nOnline -> Download SRTM tegels\nde data in je kaartcache op te slaan.
+tip.usesrtmfor3d=Deze route heeft geen hoogten.\nJe kan de SRTM functies gebruiken om een geschatte hoogte\nop te halen voor het 3d beeld.
+tip.manuallycorrelateone=Door handmatig een foto te koppelen kan het tijdsverschil voor u berekend worden.
+
# Buttons
button.ok=OK
button.back=Terug
@@ -597,6 +634,7 @@ button.yes=Ja
button.no=Nee
button.yestoall=Ja op alles
button.notoall=Nee op alles
+button.always=Altijd
button.select=Selecteer
button.selectall=Selecteer alles
button.selectnone=Selecteer niets
@@ -685,7 +723,6 @@ 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
@@ -720,6 +757,8 @@ units.degminsec=Grd-min-sec
units.degmin=Grd-min
units.deg=Graden
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreesfahrenheit=Fahrenheit
# How to combine conditions, such as filters
logic.and=en
@@ -728,6 +767,7 @@ logic.or=of
# External urls
url.googlemaps=maps.google.nl
wikipedia.lang=nl
+openweathermap.lang=nl
# Cardinals for 3d plots
cardinal.n=N
@@ -749,6 +789,8 @@ undo.deletemarked=verwijderen punten
undo.insert=punten invoegen
undo.reverse=reeks omkeren
undo.mergetracksegments=samenvoegen route segmenten
+undo.splitsegments=opdelen routesegmenten
+undo.sewsegments=verbinden routesegmenten
undo.addtimeoffset=tijdsverschil toevoegen
undo.addaltitudeoffset=hoogteverschil toevoegen
undo.rearrangewaypoints=herschikken waypoint
@@ -771,10 +813,8 @@ 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.saveexif.failed=Kon %d van de foto's niet wegschrijven
+error.saveexif.forced=%d 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
@@ -785,7 +825,7 @@ error.jpegload.dialogtitle=Fout bij inlezen foto's
error.jpegload.nofilesfound=Bestanden niet gevonden
error.jpegload.nojpegsfound=Geen jpeg-bestanden 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.jpegload.exifreadfailed=Kon geen Exif informatie inlezen. Exif informatie kan niet worden gelezen\n zonder interne of externe bibliotheek.
error.audioload.nofilesfound=Geen audiobestanden gevonden
error.gpsload.unknown=Onbekende fout
error.undofailed.title=Terugdraaien mislukt
@@ -811,3 +851,6 @@ error.cache.empty=De tegelcache map is leeg
error.cache.cannotdelete=Er konden geen tegels verwijderd worden
error.interpolate.invalidparameter=Aantal punten moet tussen 1 en 1000 liggen
error.learnestimationparams.failed=Kan geen parameters bepalen van deze route.\nProbeer meer routes te laden.
+error.tracksplit.nosplit=Deze route kon niet opgedeeld worden
+error.downloadsrtm.nocache=De bestanden konden niet worden opgeslagen.\nControleer de schijfcache.
+error.sewsegments.nothingdone=Er konden geen segmenten worden samengevoegd.\nEr zijn nu %d segmenten in de route.
diff --git a/tim/prune/lang/prune-texts_pl.properties b/tim/prune/lang/prune-texts_pl.properties
index 339c395..212eb69 100644
--- a/tim/prune/lang/prune-texts_pl.properties
+++ b/tim/prune/lang/prune-texts_pl.properties
@@ -101,11 +101,13 @@ function.show3d=Poka\u017c model 3D
function.distances=Odleg\u0142o\u015bci
function.fullrangedetails=Wszystkie detale
function.estimatetime=Przewidywany czas
+function.learnestimationparams=Skoryguj wsp\u00f3\u0142czynniki szacowania czasu
function.setmapbg=Wybierz map\u0119 t\u0142a
function.setpaths=Ustaw \u015bcie\u017cki do program\u00f3w
function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies
function.uploadgpsies=Wy\u015blij \u015bcie\u017cki do Gpsies
function.lookupsrtm=Pobierz wysoko\u015bci z SRTM
+function.downloadsrtm=Zapisz dane z SRTM
function.getwikipedia=Szukaj w Wikipedii o okolicy
function.searchwikipedianames=Szukaj nazwy w Wikipedii
function.downloadosm=Za\u0142aduj dane obszaru z OSM
@@ -134,6 +136,7 @@ function.checkversion=Sprawd\u017a czy jest nowa wersja
function.saveconfig=Zapisz ustawienia
function.diskcache=Zapisz mapy na dysk
function.managetilecache=Zarz\u0105dzaj keszem p\u0142ytek
+function.getweatherforecast=Pobierz prognoza pogody
# Dialogs
dialog.exit.confirm.title=Zako\u0144cz GpsPrune
@@ -162,6 +165,8 @@ dialog.openoptions.deliminfo.norecords=Brak rekord\u00f3w
dialog.openoptions.altitudeunits=Jednostki wysoko\u015bci
dialog.openoptions.speedunits=Jednostki pr\u0119dko\u015bci
dialog.openoptions.vertspeedunits=Jednostki pr\u0119dko\u015bci pionowej
+dialog.openoptions.vspeed.positiveup=Pr\u0119dko\u015b\u0107 wznoszenia
+dialog.openoptions.vspeed.positivedown=Pr\u0119dko\u015b\u0107 opadania
dialog.open.contentsdoubled=Ten plik zawiera dwie kopie ka\u017cdego punktu.\nRaz jako punkt po\u015bredni, a raz jako punkt \u015bcie\u017cki.
dialog.selecttracks.intro=Wybierz \u015bcie\u017ck\u0119 lub \u015bcie\u017cki
dialog.selecttracks.noname=Nienazwane
@@ -197,7 +202,7 @@ dialog.gpsbabel.filter.simplify.maxerror=lub b\u0142\u0105d odleg\u0142o\u015bci
dialog.gpsbabel.filter.simplify.crosstrack=skrzy\u017cowane \u015bcie\u017cki
dialog.gpsbabel.filter.simplify.length=d\u0142ugo\u015b\u0107 r\u00f3\u017cnicy
dialog.gpsbabel.filter.simplify.relative=powi\u0105zan z Hdop
-dialog.gpsbabel.filter.distance.intro=Usu\u0144 punkty je\u015bli poprzednie punkt
+dialog.gpsbabel.filter.distance.intro=Usu\u0144 punkty je\u015bli poprzednie punkty wystarczaj\u0105co blisko
dialog.gpsbabel.filter.distance.distance=Je\u015bli odleg\u0142o\u015b\u0107 <
dialog.gpsbabel.filter.distance.time=i r\u00f3\u017cnica w czasie <
dialog.gpsbabel.filter.interpolate.intro=Dodaj ekstra punkty pomi\u0119dzy punktami \u015bcie\u017cki
@@ -222,7 +227,7 @@ dialog.exportkml.exportimages=Eksportuj miniaturki zdj\u0119\u0107 do KMZ
dialog.exportkml.imagesize=Rozmiar zdj\u0119\u0107
dialog.exportkml.trackcolour=Kolor \u015bcie\u017cki
dialog.exportkml.standardkml=Standardowy KML
-dialog.exportkml.extendedkml=Standardowy KML ze znacznikami czasu (no, *extended*, not standard!)
+dialog.exportkml.extendedkml=Standardowy KML ze znacznikami czasu
dialog.exportgpx.name=Nazwa
dialog.exportgpx.desc=Opis
dialog.exportgpx.includetimestamps=Do\u0142\u0105cz znaczniki czasu
@@ -254,6 +259,7 @@ dialog.exportsvg.theta=K\u0105t wzniesienia \u03b8
dialog.exportsvg.gradients=U\u017cyj gradientu jako wype\u0142nienia
dialog.exportimage.noimagepossible=Obrazy map musz\u0105 zosta\u0107 zapisane na dysku przed ich eksportem
dialog.exportimage.drawtrack=Rysuj \u015bcie\u017ck\u0119 na mapie
+dialog.exportimage.drawtrackpoints=Rysuj punkty
dialog.exportimage.textscalepercent=Wsp\u00f3\u0142czynnik skali tekstu (%)
dialog.pointtype.desc=Zapisz punkty nast\u0119puj\u0105cych typ\u00f3w:
dialog.pointtype.track=punkty \u015bcie\u017cki
@@ -322,14 +328,24 @@ dialog.fullrangedetails.colsegments=Bez luk
dialog.estimatetime.details=Szczeg\u00f3\u0142y
dialog.estimatetime.gentle=\u0141agodnie
dialog.estimatetime.steep=Stromo
+dialog.estimatetime.climb=Wznoszenie
+dialog.estimatetime.descent=Opadanie
dialog.estimatetime.parameters=Parametry
dialog.estimatetime.parameters.timefor=Czas dla
dialog.estimatetime.results=Wynik
dialog.estimatetime.results.estimatedtime=Czas przewidywany
dialog.estimatetime.results.actualtime=Czas bie\u017c\u0105cy
+dialog.estimatetime.error.nodistance=Aby obliczy\u0107 wsp\u00f3\u0142czynniki szacowania czasu punkty bie\u017c\u0105cej \u015bcie\u017cki musz\u0105 posiada\u0107 informacje o odleg\u0142o\u015bci
+dialog.estimatetime.error.noaltitudes=Bie\u017c\u0105ce zaznaczenie nie zawiera informacji o wysoko\u015bci
+dialog.learnestimationparams.intro=Wsp\u00f3\u0142czynniki obliczone na podstawie bie\u017c\u0105cej \u015bcie\u017cki
dialog.learnestimationparams.averageerror=B\u0142\u0105d \u015bredni
+dialog.learnestimationparams.combine=Po\u0142\u0105cz powy\u017csze wsp\u00f3\u0142czynniki z ich warto\u015bciami bie\u017c\u0105cymi
+dialog.learnestimationparams.combinedresults=Nowe wsp\u00f3\u0142czynniki
+dialog.learnestimationparams.weight.100pccurrent=Zatrzymaj warto\u015bci bie\u017c\u0105ce
dialog.learnestimationparams.weight.current=bie\u017c\u0105ce
dialog.learnestimationparams.weight.calculated=obliczone
+dialog.learnestimationparams.weight.50pc=\u015arednia warto\u015bci bie\u017c\u0105cych i obliczonych
+dialog.learnestimationparams.weight.100pccalculated=U\u017cyj nowe warto\u015bci obliczone
dialog.setmapbg.intro=Wybierz dostawc\u0119 map t\u0142a lub dodaj nowego
dialog.addmapsource.title=Dodaj dostawc\u0119 map
dialog.addmapsource.sourcename=Nazwa dostawcy
@@ -365,7 +381,6 @@ dialog.correlate.photoselect.intro=Wybierz jedno z powi\u0105zanych zdj\u0119\u0
dialog.correlate.select.photoname=Nazwa zdj\u0119cia
dialog.correlate.select.timediff=R\u00f3\u017cnica czasowa
dialog.correlate.select.photolater=P\u00f3\u017aniejsze zdj\u0119cie
-dialog.correlate.options.tip=Porada: Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie.
dialog.correlate.options.intro=Wybierz opcje dla automatycznej korelacji
dialog.correlate.options.offsetpanel=Przesuni\u0119cie czasowe
dialog.correlate.options.offset=Przesuni\u0119cie
@@ -408,8 +423,7 @@ dialog.compress.duplicates.title=Usuwanie duplikat\u00f3w
dialog.compress.douglaspeucker.title=kompresja Douglasa-Peuckera
dialog.compress.douglaspeucker.paramdesc=wsp\u00f3\u0142czynnik rozpi\u0119to\u015bci (szeroko\u015bci korytarza)
dialog.compress.summarylabel=Punkty do usuni\u0119cia
-dialog.compress.confirm1=
-dialog.compress.confirm2=punkt\u00f3w zosta\u0142o zaznaczonych\nU\u017cyj \u015acie\u017cka->Usu\u0144 zaznaczone punkty, by je usun\u0105\u0107
+dialog.compress.confirm=%d punkt\u00f3w zosta\u0142o zaznaczonych\nUsu\u0144 zaznaczone punkty?
dialog.compress.confirmnone=\u017cadne punkty nie zosta\u0142y zaznaczone
dialog.deletemarked.nonefound=Nie mo\u017cna usun\u0105\u0107 \u017cadnych punkt\u00f3w
dialog.pastecoordinates.desc=Wprowad\u017a lub wklej wsp\u00f3\u0142rz\u0119dne
@@ -454,7 +468,7 @@ dialog.checkversion.newversion1=Dost\u0119pna jest nowa wersja GpsPrune! Najnows
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Nowa wersja zosta\u0142a opublikowana
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Aby \u015bci\u0105gn\u0105\u0107 now\u0105 wersj\u0119 przejd\u017a na http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Aby \u015bci\u0105gn\u0105\u0107 now\u0105 wersj\u0119 przejd\u017a na http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=U\u017cuwaj nast\u0119puj\u0105cych klawiszy skr\u00f3t\u00f3w zamiast myszki
dialog.keys.keylist=<table><tr><td>klawisze strza\u0142ek</td><td>Przesuwa map\u0119 w lewo, w prawo, w g\u00f3r\u0119, w d\u00f3\u0142</td></tr><tr><td>Ctrl + lewa, prawa strza\u0142ka</td><td>Wybierz punkt poprzedni lub nast\u0119pny</td></tr><tr><td>Ctrl + strza\u0142ka w g\u00f3r\u0119, w d\u00f3\u0142</td><td>Powi\u0119ksz, pomniejsz</td></tr><tr><td>Del</td><td>Usun bie\u017c\u0105cy punkt</td></tr></table>
dialog.keys.normalmodifier=Ctrl
@@ -517,13 +531,22 @@ dialog.diskcache.tileset.multiple=wiele
dialog.diskcache.deleteold=Usu\u0144 stare p\u0142ytki
dialog.diskcache.maximumage=Maksymalny wiek (w dniach)
dialog.diskcache.deleteall=Usu\u0144 wszystkie p\u0142ytki
-dialog.diskcache.deleted1=Usuni\u0119to
-dialog.diskcache.deleted2=plik\u00f3w z kesza
+dialog.diskcache.deleted=Usuni\u0119to %d plik\u00f3w z kesza
dialog.deletefieldvalues.intro=Wybierz pola do skasowania z wybranego zakresu
dialog.deletefieldvalues.nofields=Brak p\u00f3l do skasowania dla tego zakresu
dialog.setlinewidth.text=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek
dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM:
dialog.searchwikipedianames.search=Szukaj
+dialog.weather.day.now=Aktualny
+dialog.weather.day.today=Dzisiaj
+dialog.weather.day.tomorrow=Jutro
+dialog.weather.day.monday=Poniedzia\u0142ek
+dialog.weather.day.tuesday=Wtorek
+dialog.weather.day.wednesday=\u015Aroda
+dialog.weather.day.thursday=Czwartek
+dialog.weather.day.friday=Pi\u0105tek
+dialog.weather.day.saturday=Sobota
+dialog.weather.day.sunday=Niedziela
# 3d window
dialog.3d.title=GpsPrune widok tr\u00f3jwymiarowy
@@ -545,8 +568,7 @@ confirm.rearrangephotos=Zmieniono kolejno\u015b\u0107 zdj\u0119\u0107
confirm.cutandmove=Przesuni\u0119to zaznaczenie
confirm.interpolate=Dodano punkty
confirm.convertnamestotimes=Zmieniono nazwy punkt\u00f3w po\u015brednich
-confirm.saveexif.ok1=Zapisano
-confirm.saveexif.ok2=plik(\u00f3w) zdj\u0119\u0107
+confirm.saveexif.ok=Zapisano %d plik(\u00f3w) zdj\u0119\u0107
confirm.undo.single=cofni\u0119to operacj\u0119
confirm.undo.multi=operacje zosta\u0142y cofni\u0119te
confirm.jpegload.single=dodano zdj\u0119cie
@@ -560,13 +582,16 @@ confirm.correlatephotos.multi=zdj\u0119cia zosta\u0142y po\u0142\u0105czone
confirm.createpoint=stworzono punkt
confirm.rotatephoto=obr\u00f3cono zdj\u0119cie
confirm.running=Przetwarzam dane ...
-confirm.lookupsrtm1=Znaleziono
-confirm.lookupsrtm2=warto\u015bci wysoko\u015bci
+confirm.lookupsrtm=Znaleziono %d warto\u015bci wysoko\u015bci
confirm.deletefieldvalues=Warto\u015bci p\u00f3l usuni\u0119to
confirm.audioload=dodano pliki audio
confirm.correlateaudios.single=audio zosta\u0142o po\u0142\u0105czone
confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone
+# Tips
+tip.title=Porada
+tip.manuallycorrelateone=Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie.
+
# Buttons
button.ok=OK
button.back=Poprzedni
@@ -584,6 +609,7 @@ button.yes=Tak
button.no=Nie
button.yestoall=Tak na wszystko
button.notoall=Nie na wszystko
+button.always=Zawsze
button.select=Zaznacz
button.selectall=Zaznacz wszystko
button.selectnone=Odznacz
@@ -672,7 +698,6 @@ fieldname.newsegment=Odcinek
fieldname.custom=U\u017cytkownika
fieldname.prefix=Pole
fieldname.distance=Odleg\u0142o\u015b\u0107
-fieldname.movingdistance=Odleg\u0142o\u015b\u0107 przesuni\u0119cia
fieldname.duration=Czas trwania
fieldname.speed=Pr\u0119dko\u015b\u0107
fieldname.verticalspeed=Pr\u0119dko\u015b\u0107 pionowa
@@ -707,6 +732,10 @@ units.degminsec=Sto-min-sek
units.degmin=Sto-min
units.deg=Stopnie
units.iso8601=ISO 8601
+units.degreescelsius=Celsjusza
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheita
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=i
@@ -715,6 +744,7 @@ logic.or=lub
# External urls
url.googlemaps=maps.google.pl
wikipedia.lang=pl
+openweathermap.lang=pl
# Cardinals for 3d plots
cardinal.n=N
@@ -758,10 +788,8 @@ error.save.failed=Nie powi\u00f3d\u0142 si\u0119 zapis danych do pliku
error.saveexif.filenotfound=Nie znaleziono pliku ze zdj\u0119ciem
error.saveexif.cannotoverwrite1=Plik ze zdj\u0119ciem
error.saveexif.cannotoverwrite2=jest w trybie tylko do odczytu i nie mo\u017ce zosta\u0107 nadpisany. Zapisa\u0107 kopi\u0119?
-error.saveexif.failed1=B\u0142\u0105d zapisu
-error.saveexif.failed2=zdj\u0119\u0107
-error.saveexif.forced1=
-error.saveexif.forced2=zdj\u0119\u0107 z wymuszonym zapisem
+error.saveexif.failed=B\u0142\u0105d zapisu %d zdj\u0119\u0107
+error.saveexif.forced=%d zdj\u0119\u0107 z wymuszonym zapisem
error.load.dialogtitle=B\u0142\u0105d \u0142adowania danych
error.load.noread=Nie mo\u017cna przeczyta\u0107 pliku
error.load.nopoints=Nie znaleziono informacji o wsp\u00f3\u0142rz\u0119dnych w pliku
@@ -772,7 +800,7 @@ error.jpegload.dialogtitle=B\u0142\u0105d \u0142adowania zdj\u0119cia
error.jpegload.nofilesfound=Nie znaleziono plik\u00f3w
error.jpegload.nojpegsfound=Nie znaleziono plik\u00f3w jpeg
error.jpegload.nogpsfound=Nie znaleziono informacji GPS
-error.jpegload.exifreadfailed=Nie powiod\u0142o si\u0119 odczytanie informacji EXIF\nInformacji tych nie mo\u017cna przeczyta\u0107 bez wewn\u0119trznej lub zewn\u0119trznej biblioteki.
+error.jpegload.exifreadfailed=Nie powiod\u0142o si\u0119 odczytanie informacji Exif\nInformacji tych nie mo\u017cna przeczyta\u0107 bez wewn\u0119trznej lub zewn\u0119trznej biblioteki.
error.audioload.nofilesfound=Nie znaleziono plik\u00f3w audio
error.gpsload.unknown=Nieznany b\u0142\u0105d
error.undofailed.title=Cofnij nie powiod\u0142o si\u0119
@@ -797,3 +825,4 @@ error.cache.notthere=Nie znaleziono katalogu kesza
error.cache.empty=Katalog kesza jest pusty
error.cache.cannotdelete=\u017badne p\u0142ytki nie mog\u0142y zosta\u0107 usuni\u0119te
error.interpolate.invalidparameter=Ilo\u015b\u0107 punkt\u00f3w musi zawiera\u0107 si\u0119 w zakresie od 1 do 1000
+error.learnestimationparams.failed=Oszacowanie wsp\u00f3\u0142czynnik\u00f3w dla danej scie\u017cki nie powiod\u0142o si\u0119.\nSpr\u00f3buj za\u0142adowa\u0107 wi\u0119cej \u015bcie\u017cek.
diff --git a/tim/prune/lang/prune-texts_pt.properties b/tim/prune/lang/prune-texts_pt.properties
index fe7bdc2..50c6844 100644
--- a/tim/prune/lang/prune-texts_pt.properties
+++ b/tim/prune/lang/prune-texts_pt.properties
@@ -7,21 +7,21 @@ menu.file.addphotos=Adicionar fotos
menu.file.recentfiles=Arquivos recentes
menu.file.save=Salvar
menu.file.exit=Sair
+menu.online=Online
menu.track=Rota
menu.track.undo=Desfazer
menu.track.clearundo=Limpar lista de desfazer
+menu.track.markrectangle=Marcar pontos no ret\u00e2ngulo
menu.track.deletemarked=Remover pontos marcados
menu.track.rearrange=Rearrumar pontos
menu.track.rearrange.start=Tudo para o in\u00edcio do arquivo
menu.track.rearrange.end=Tudo para o fim do arquivo
menu.track.rearrange.nearest=Cada um para o ponto da rota mais pr\u00f3ximo
menu.range=Intervalo
-menu.range.all=Selectionar tudo
-menu.range.none=N\u00e3o selecionar nenhuns
+menu.range.all=Selecionar tudo
+menu.range.none=Desmarcar todas as sele\u00e7\u00f5es
menu.range.start=Definir in\u00edcio do intervalo
menu.range.end=Definir fim do intervalo
-function.deleterange=Remover intervalo
-function.interpolate=Interpolar pontos
menu.range.average=Sele\u00e7\u00e3o m\u00e9dia
menu.range.reverse=Reverter intervalo
menu.range.mergetracksegments=Mesclar trechos da rota
@@ -36,7 +36,7 @@ menu.view=Exibir
menu.view.showsidebars=Mostrar barras laterais
menu.view.browser=Mapear no navegador
menu.view.browser.google=Mapas do Google
-menu.view.browser.openstreetmap=Mapas do Openstreetmap
+menu.view.browser.openstreetmap=Mapas do OpenStreetMap
menu.view.browser.mapquest=Mapas do Mapquest
menu.view.browser.yahoo=Mapas do Yahoo
menu.view.browser.bing=Mapas do Bing
@@ -54,9 +54,11 @@ menu.map.connect=Conectar pontos da rota
menu.map.autopan=Auto-ajustar
menu.map.showmap=Mostrar o mapa
menu.map.showscalebar=Mostrar barra de escala
+menu.map.editmode=Modo de edi\u00e7\u00e3o
# Alt keys for menus
altkey.menu.file=A
+altkey.menu.online=N
altkey.menu.track=R
altkey.menu.range=I
altkey.menu.point=P
@@ -84,8 +86,12 @@ function.exportkml=Exportar para KML
function.exportgpx=Exportar para GPX
function.exportpov=Exportar para POV
function.exportsvg=Exportar para SVG
+function.exportimage=Exportar imagem
function.editwaypointname=Editar nome do ponto
function.compress=Comprimir rota
+function.deleterange=Remover intervalo
+function.croptrack=Cortar rota
+function.interpolate=Interpolar pontos
function.addtimeoffset=Adicionar diferen\u00e7a de tempo
function.addaltitudeoffset=Adicionar diferen\u00e7a de altitude
function.convertnamestotimes=Converter nomes dos pontos para tempos
@@ -96,13 +102,18 @@ function.charts=Gr\u00e1ficos
function.show3d=Visualizar 3D
function.distances=Dist\u00e2ncias
function.fullrangedetails=Todos os detalhes
+function.estimatetime=Tempo estimado
+function.learnestimationparams=Aprender os par\u00e2metros para estimativa de tempo
function.setmapbg=Definir como fundo do mapa
function.setpaths=Definir caminhos do programa
+function.splitsegments=Dividir rota em segmentos
+function.sewsegments=Reunir segmentos em rota
function.getgpsies=Obter rotas Gpsies
function.uploadgpsies=Enviar rotas para o Gpsies
function.lookupsrtm=Obter altitudes a partir do SRTM
-function.getwikipedia=Obter artigos da Wikipedia das redondezas
-function.searchwikipedianames=Procurar na Wikipedia por nome
+function.downloadsrtm=Baixar arquivos SRTM
+function.getwikipedia=Obter artigos da Wikip\u00e9dia das redondezas
+function.searchwikipedianames=Procurar na Wikip\u00e9dia por nome
function.downloadosm=Baixar dados OSM para a \u00e1rea
function.duplicatepoint=Duplicar ponto
function.setcolours=Definir cores
@@ -116,7 +127,7 @@ function.rearrangephotos=Rearrumar fotos
function.rotatephotoleft=Roda foto \u00e0 esquerda
function.rotatephotoright=Roda foto \u00e0 direita
function.photopopup=Mostrar janela da foto
-function.ignoreexifthumb=Ignorar miniatura do exif
+function.ignoreexifthumb=Ignorar miniatura do Exif
function.loadaudio=Adicionar arquivos de \u00e1udio
function.removeaudio=Remover arquivo de \u00e1udio
function.correlateaudios=Correlacionar \u00e1udios
@@ -129,16 +140,18 @@ function.checkversion=Verificar novas vers\u00f5es
function.saveconfig=Salvar configura\u00e7\u00f5es
function.diskcache=Salvar mapas para o disco
function.managetilecache=Gerenciar cache de fundos
+function.getweatherforecast=Baixar a previs\u00e3o do tempo para a \u00e1rea atual
# Dialogs
dialog.exit.confirm.title=Sair do GpsPrune
-dialog.exit.confirm.text=Seus dados n\u00e3o foram salvos. Voc\u00ea tem certeza que deseja sair?
+dialog.exit.confirm.text=Seus dados n\u00e3o foram salvos. Voc\u00ea tem certeza de que deseja sair?
dialog.openappend.title=Adicionar aos dados existentes
dialog.openappend.text=Adicionar estes dados aos dados j\u00e1 carregados?
dialog.deletepoint.title=Remover Ponto
dialog.deletepoint.deletephoto=Remover foto anexada a este ponto?
dialog.deletephoto.title=Remover Foto
dialog.deletephoto.deletepoint=Remover ponto anexado a esta foto?
+dialog.deleteaudio.deletepoint=Remover ponto anexado a este clipe de \u00e1udio?
dialog.openoptions.title=Op\u00e7\u00f5es de abertura
dialog.openoptions.filesnippet=Extrair do arquivo
dialog.load.table.field=Campo
@@ -154,6 +167,10 @@ dialog.openoptions.deliminfo.records=registros, com
dialog.openoptions.deliminfo.fields=campos
dialog.openoptions.deliminfo.norecords=Sem registros
dialog.openoptions.altitudeunits=Unidades de altitude
+dialog.openoptions.speedunits=Unidades de velocidade
+dialog.openoptions.vertspeedunits=Unidades de velocidade vertical
+dialog.openoptions.vspeed.positiveup=Velocidades de subida positivas
+dialog.openoptions.vspeed.positivedown=Velocidades de descida positivas
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.selecttracks.intro=Selecione a rota ou rotas para carregar
dialog.selecttracks.noname=Sem nome
@@ -171,6 +188,30 @@ dialog.gpsload.save=Salvar para arquivo
dialog.gpssend.sendwaypoints=Enviar pontos
dialog.gpssend.sendtracks=Enviar rotas
dialog.gpssend.trackname=Nome da rota
+dialog.gpsbabel.filters=Filtros
+dialog.addfilter.title=Adicionar filtro
+dialog.gpsbabel.filter.discard=Descartar
+dialog.gpsbabel.filter.simplify=Simplificar
+dialog.gpsbabel.filter.distance=Dist\u00e2ncia
+dialog.gpsbabel.filter.interpolate=Interpolar
+dialog.gpsbabel.filter.discard.intro=Descartar pontos se
+dialog.gpsbabel.filter.discard.hdop=Hdop >
+dialog.gpsbabel.filter.discard.vdop=Vdop >
+dialog.gpsbabel.filter.discard.numsats=N\u00famero de sat\u00e9lites <
+dialog.gpsbabel.filter.discard.nofix=Ponto n\u00e3o possui fixo
+dialog.gpsbabel.filter.discard.unknownfix=Ponto possui fixo desconhecido
+dialog.gpsbabel.filter.simplify.intro=Remover pontos at\u00e9
+dialog.gpsbabel.filter.simplify.maxpoints=N\u00famero de pontos <
+dialog.gpsbabel.filter.simplify.maxerror=ou erro de dist\u00e2ncia <
+dialog.gpsbabel.filter.simplify.crosstrack=cruzamento de rota
+dialog.gpsbabel.filter.simplify.length=diferen\u00e7a de comprimento
+dialog.gpsbabel.filter.simplify.relative=relativo ao hdop
+dialog.gpsbabel.filter.distance.intro=Remover pontos se pr\u00f3ximos a qualquer ponto anterior
+dialog.gpsbabel.filter.distance.distance=Se dist\u00e2ncia <
+dialog.gpsbabel.filter.distance.time=e diferen\u00e7a de tempo <
+dialog.gpsbabel.filter.interpolate.intro=Adicionar pontos entre os pontos da rota
+dialog.gpsbabel.filter.interpolate.distance=Se dist\u00e2ncia >
+dialog.gpsbabel.filter.interpolate.time=ou diferen\u00e7a de tempo >
dialog.saveoptions.title=Salvar arquivo
dialog.save.fieldstosave=Campos a salvar
dialog.save.table.field=Campo
@@ -181,13 +222,16 @@ dialog.save.coordinateunits=Unidades das coordenadas
dialog.save.altitudeunits=Unidades da altitude
dialog.save.timestampformat=Formato da data-hora
dialog.save.overwrite.title=Arquivo j\u00e1 existe
-dialog.save.overwrite.text=Este arquivo j\u00e1 existe. Voc\u00ea tem certeza que deseja sobrescrev\u00ea-lo?
+dialog.save.overwrite.text=Este arquivo j\u00e1 existe. Voc\u00ea tem certeza de que deseja sobrescrev\u00ea-lo?
dialog.save.notypesselected=Nenhum tipo de ponto foi selecionado
dialog.exportkml.text=T\u00edtulo para os dados
dialog.exportkml.altitude=Incluir altitudes (para avia\u00e7\u00e3o)
-dialog.exportkml.kmz=Comprimir para criar arquivo kmz
-dialog.exportkml.exportimages=Exportar miniaturas de imagem para o kmz
+dialog.exportkml.kmz=Comprimir para criar arquivo KMZ
+dialog.exportkml.exportimages=Exportar miniaturas de imagem para o KMZ
+dialog.exportkml.imagesize=Tamanho da imagem
dialog.exportkml.trackcolour=Cor da rota
+dialog.exportkml.standardkml=KML plano
+dialog.exportkml.extendedkml=KML extendido com estampa de tempo
dialog.exportgpx.name=Nome
dialog.exportgpx.desc=Descri\u00e7\u00e3o
dialog.exportgpx.includetimestamps=Incluir data-hora
@@ -203,12 +247,26 @@ dialog.exportpov.cameraz=Z da C\u00e2mera
dialog.exportpov.modelstyle=Estilo do modelo
dialog.exportpov.ballsandsticks=Bolas e galhos
dialog.exportpov.tubesandwalls=Tubos e muros
-dialog.3d.warningtracksize=Esta rota possui um grande n\u00famero de pontos, os quais o Java3D n\u00e3o ser\u00e1 capaz de exibir.\n Voc\u00ea tem certeza que deseja continuar?
-dialog.exportkml.imagesize=Tamanho da imagem
+dialog.3d.warningtracksize=Esta rota possui um grande n\u00famero de pontos, os quais o Java3D n\u00e3o ser\u00e1 capaz de exibir.\n Voc\u00ea tem certeza de que deseja continuar?
+dialog.3d.useterrain=Mostrar terreno
+dialog.3d.terraingridsize=Tamanho da grade
+dialog.exportpov.baseimage=Imagem base
+dialog.exportpov.cannotmakebaseimage=N\u00e3o foi poss\u00edvel gravar imagem base
+dialog.baseimage.title=Imagem do mapa
+dialog.baseimage.useimage=Usar imagem
+dialog.baseimage.mapsource=Fonte do mapa
+dialog.baseimage.zoom=N\u00edvel de amplia\u00e7\u00e3o
+dialog.baseimage.incomplete=Imagem incompleta
+dialog.baseimage.tiles=Ladrilhos
+dialog.baseimage.size=Tamanho da imagem
dialog.exportsvg.text=Selecione os par\u00e2metros para a exporta\u00e7\u00e3o para o SVG
dialog.exportsvg.phi=\u00c2ngulo do azimute \u03d5
dialog.exportsvg.theta=\u00c2ngulo da eleva\u00e7\u00e3o \u03b8
dialog.exportsvg.gradients=Usar gradientes para sombreamento
+dialog.exportimage.noimagepossible=Imagens de mapa precisam estar em cache no disco para serem usados por uma exporta\u00e7\u00e3o.
+dialog.exportimage.drawtrack=Desenhar rota no mapa
+dialog.exportimage.drawtrackpoints=Desenhar pontos da rota
+dialog.exportimage.textscalepercent=Fator de escala do texto (%)
dialog.pointtype.desc=Salvar os seguintes tipos de ponto:
dialog.pointtype.track=Pontos de rotas
dialog.pointtype.waypoint=Pontos
@@ -216,19 +274,21 @@ dialog.pointtype.photo=Pontos de foto
dialog.pointtype.audio=Pontos de \u00e1udio
dialog.pointtype.selection=Apenas sele\u00e7\u00e3o
dialog.confirmreversetrack.title=Confirmar invers\u00e3o
-dialog.confirmreversetrack.text=Esta rota possui informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s a revers\u00e3o.\n Voc\u00ea tem certeza que deseja reverter esta se\u00e7\u00e3o?
+dialog.confirmreversetrack.text=Esta rota possui informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s a revers\u00e3o.\n Voc\u00ea tem certeza de que deseja reverter esta se\u00e7\u00e3o?
dialog.confirmcutandmove.title=Confirmar cortar e mover
-dialog.confirmcutandmove.text=A rota cont\u00e9m informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s o movimento.\n Voc\u00ea tem certeza que deseja mover esta se\u00e7\u00e3o?
+dialog.confirmcutandmove.text=A rota cont\u00e9m informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s o movimento.\n Voc\u00ea tem certeza de que deseja mover esta se\u00e7\u00e3o?
dialog.interpolate.parameter.text=N\u00famero de pontos para inserir entre os pontos selecionados
+dialog.interpolate.betweenwaypoints=Interpolar entre os pontos do caminho?
dialog.undo.title=A\u00e7\u00e3o(\u00f5es) de desfazer
dialog.undo.pretext=Por favor, selecione a a\u00e7\u00e3o(\u00f5es) a desfazer
dialog.undo.none.title=N\u00e3o foi poss\u00edvel desfazer
dialog.undo.none.text=Nenhuma opera\u00e7\u00e3o a desfazer!
dialog.clearundo.title=Limpar lista de desfazer
-dialog.clearundo.text=Voc\u00ea tem certeza que deseja limpar a lista de desfazer?\n Todas as informa\u00e7\u00f5es para desfazer ser\u00e3o perdidas!
+dialog.clearundo.text=Voc\u00ea tem certeza de que deseja limpar a lista de desfazer?\n Todas as informa\u00e7\u00f5es para desfazer ser\u00e3o perdidas!
dialog.pointedit.title=Editar ponto
dialog.pointedit.intro=Selecionar cada campo para mudar o valor
dialog.pointedit.table.field=Campo
+dialog.pointedit.nofield=Nenhum campo selecionado
dialog.pointedit.table.value=Valor
dialog.pointnameedit.name=Nome do ponto
dialog.pointnameedit.uppercase=MAI\u00daSCULAS
@@ -269,6 +329,29 @@ 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.fullrangedetails.coltotal=Incluindo intervalos
+dialog.fullrangedetails.colsegments=Sem intervalos
+dialog.estimatetime.details=Detalhes
+dialog.estimatetime.gentle=Suave
+dialog.estimatetime.steep=\u00cdngreme
+dialog.estimatetime.climb=Subida
+dialog.estimatetime.descent=Descida
+dialog.estimatetime.parameters=Par\u00e2metros
+dialog.estimatetime.parameters.timefor=Tempo para
+dialog.estimatetime.results=Resultados
+dialog.estimatetime.results.estimatedtime=Tempo estimado
+dialog.estimatetime.results.actualtime=Tempo real
+dialog.estimatetime.error.nodistance=Para estimar o tempo \u00e9 necess\u00e1rio conectar os pontos da rota, para obter uma dist\u00e2ncia
+dialog.estimatetime.error.noaltitudes=A sele\u00e7\u00e3o n\u00e3o inclui nenhuma informa\u00e7\u00e3o de altitude
+dialog.learnestimationparams.intro=Estes s\u00e3o os par\u00e2metros calculados desta rota
+dialog.learnestimationparams.averageerror=Erro m\u00e9dio
+dialog.learnestimationparams.combine=Estes par\u00e2metros podem ser combinados com os valores atuais
+dialog.learnestimationparams.combinedresults=Resultados combinados
+dialog.learnestimationparams.weight.100pccurrent=Manter os valores atuais
+dialog.learnestimationparams.weight.current=atual
+dialog.learnestimationparams.weight.calculated=calculado
+dialog.learnestimationparams.weight.50pc=M\u00e9dia dos valores atuais e calculados
+dialog.learnestimationparams.weight.100pccalculated=Usar novos valores calculados
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
@@ -298,12 +381,12 @@ dialog.gpsies.activity.skating=Patina\u00e7\u00e3o
dialog.wikipedia.column.name=Nome do artigo
dialog.wikipedia.column.distance=Dist\u00e2ncia
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.nouncorrelatedphotos=Existem fotos n\u00e3o correlacionadas.\nVoc\u00ea tem certeza de que deseja continuar?
+dialog.correlate.nouncorrelatedaudios=Existem \u00e1udios n\u00e3o correlacionados.\nVoc\u00ea tem certeza de que deseja continuar?
dialog.correlate.photoselect.intro=Selecione uma destas fotos correlacionadas para usar como diferen\u00e7a de tempo
dialog.correlate.select.photoname=Nome da foto
dialog.correlate.select.timediff=Diferen\u00e7a de tempo
dialog.correlate.select.photolater=Foto \u00e9 mais recente
-dialog.correlate.options.tip=Dica: Correlacionando pelo menos uma foto manualmente, a diferen\u00e7a de tempo pode ser calculada para voc\u00ea.
dialog.correlate.options.intro=Selecione as op\u00e7\u00f5es para correla\u00e7\u00e3o autom\u00e1tica
dialog.correlate.options.offsetpanel=Diferen\u00e7a de tempo
dialog.correlate.options.offset=Diferen\u00e7a
@@ -336,7 +419,6 @@ dialog.rearrangephotos.toend=Mover para o fim
dialog.rearrangephotos.nosort=N\u00e3o ordenar
dialog.rearrangephotos.sortbyfilename=Ordenar pelo nome do arquivo
dialog.rearrangephotos.sortbytime=Ordenar pela hora
-dialog.deletemarked.nonefound=Nenhum dado dos pontos pode ser removido
dialog.compress.closepoints.title=Remo\u00e7\u00e3o de ponto pr\u00f3ximo
dialog.compress.closepoints.paramdesc=Fator de deslocamento
dialog.compress.wackypoints.title=Remo\u00e7\u00e3o de ponto exc\u00eantrica
@@ -347,10 +429,13 @@ dialog.compress.duplicates.title=Remo\u00e7\u00e3o de duplicado
dialog.compress.douglaspeucker.title=Compress\u00e3o Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=Fator de deslocamento
dialog.compress.summarylabel=Pontos para remover
+dialog.compress.confirm=%d pontos foram marcados.\nRemover estes pontos marcados agora?
+dialog.compress.confirmnone=nenhum ponto foi marcado
+dialog.deletemarked.nonefound=Nenhum dado dos pontos pode ser removido
dialog.pastecoordinates.desc=Insira ou cole as coordenadas aqui
dialog.pastecoordinates.coords=Coordenadas
dialog.pastecoordinates.nothingfound=Por favor, verifique as coordenadas novamente
-dialog.help.help=Por favor, veja\n http://activityworkshop.net/software/gpsprune/\npara mais informa\u00e7\u00f5es e guia do usu\u00e1rio.
+dialog.help.help=Por favor, veja\n http://gpsprune.activityworkshop.net/\npara mais informa\u00e7\u00f5es e guia do usu\u00e1rio.
dialog.about.version=Vers\u00e3o
dialog.about.build=Compila\u00e7\u00e3o
dialog.about.summarytext1=GpsPrune \u00e9 um programa para carregar, exibir e editar dados de receptores de GPS.
@@ -389,7 +474,7 @@ dialog.checkversion.newversion1=Uma nova vers\u00e3o do GpsPrune est\u00e1 dispo
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Esta nova vers\u00e3o foi lan\u00e7ada em
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Para baixar a nova vers\u00e3o, v\u00e1 para http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Para baixar a nova vers\u00e3o, v\u00e1 para http://gpsprune.activityworkshop.net/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
@@ -441,6 +526,7 @@ 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
+dialog.diskcache.cannotwrite=Ladrilhos de mapa n\u00e3o puderam ser salvos na pasta selecionada
dialog.diskcache.table.path=Caminho
dialog.diskcache.table.usedby=Usado por
dialog.diskcache.table.zoom=Zoom
@@ -451,12 +537,34 @@ dialog.diskcache.tileset.multiple=m\u00faltiplos
dialog.diskcache.deleteold=Apagar fundos antigos
dialog.diskcache.maximumage=Idade m\u00e1xima (dias)
dialog.diskcache.deleteall=Apagar todos os fundos
-dialog.diskcache.deleted1=Removidos
-dialog.diskcache.deleted2=arquivos do cache
+dialog.diskcache.deleted=Removidos %d arquivos do cache
dialog.deletefieldvalues.intro=Selecione o campo a remover para o intervalo atual
+dialog.deletefieldvalues.nofields=N\u00e3o existem campos a remover para este intervalo
dialog.setlinewidth.text=Insira a espessura das linhas para desenhar as rotas (1-4)
dialog.downloadosm.desc=Confirmar a transfer\u00eancia de dados OSM brutos para a \u00e1rea especificada:
dialog.searchwikipedianames.search=Procurar por:
+dialog.weather.location=Localiza\u00e7\u00e3o
+dialog.weather.update=Previs\u00e3o atualizada
+dialog.weather.sunrise=Nascer do sol
+dialog.weather.sunset=P\u00f4r do sol
+dialog.weather.temperatureunits=Temperaturas
+dialog.weather.currentforecast=Clima atual
+dialog.weather.dailyforecast=Previs\u00e3o para o dia
+dialog.weather.3hourlyforecast=Previs\u00e3o para tr\u00eas horas
+dialog.weather.day.now=Clima atual
+dialog.weather.day.today=Hoje
+dialog.weather.day.tomorrow=Amanh\u00e3
+dialog.weather.day.monday=Segunda
+dialog.weather.day.tuesday=Ter\u00e7a
+dialog.weather.day.wednesday=Quarta
+dialog.weather.day.thursday=Quinta
+dialog.weather.day.friday=Sexta
+dialog.weather.day.saturday=S\u00e1bado
+dialog.weather.day.sunday=Domingo
+dialog.weather.wind=Vento
+dialog.weather.temp=Temp
+dialog.weather.humidity=Umidade
+dialog.weather.creditnotice=Estes dados foram disponibilizados por openweathermap.org. A p\u00e1gina Web possui mais detalhes.
# 3d window
dialog.3d.title=Vista 3D do GpsPrune
@@ -475,10 +583,12 @@ confirm.addtimeoffset=Diferen\u00e7a de tempo adicionada
confirm.addaltitudeoffset=Diferen\u00e7a de altitude adicionadas
confirm.rearrangewaypoints=Pontos rearrumados
confirm.rearrangephotos=Fotos rearrumadas
+confirm.splitsegments=%d divis\u00f5es de segmentos feitas
+confirm.sewsegments=%d reuni\u00f5es de segmentos feitas
confirm.cutandmove=Sele\u00e7\u00e3o movida
+confirm.interpolate=Pontos adicionados
confirm.convertnamestotimes=Nomes dos pontos convertidos
-confirm.saveexif.ok1=Salvo
-confirm.saveexif.ok2=arquivos de foto
+confirm.saveexif.ok=Salvo %d arquivos de foto
confirm.undo.single=opera\u00e7\u00e3o desfeita
confirm.undo.multi=opera\u00e7\u00f5es desfeitas
confirm.jpegload.single=foto foi adicionada
@@ -492,13 +602,23 @@ confirm.correlatephotos.multi=fotos foram correlacionadas
confirm.createpoint=ponto criado
confirm.rotatephoto=foto rotacionada
confirm.running=Rodando...
-confirm.lookupsrtm1=Encontrado
-confirm.lookupsrtm2=valores de altitude
+confirm.lookupsrtm=Encontrado %d valores de altitude
+confirm.downloadsrtm=%d arquivos baixados para a cache
+confirm.downloadsrtm.1=%d arquivo baixados para a cache
+confirm.downloadsrtm.none=Nenhum arquivo baixado, pois j\u00e1 est\u00e3o na cache.
confirm.deletefieldvalues=Valores do campo removidos
confirm.audioload=Arquivos de \u00e1udio adicionados
confirm.correlateaudios.single=\u00e1udio foi correlacionado
confirm.correlateaudios.multi=\u00e1udios foram correlacionados
+# Tips, shown just once when appropriate
+tip.title=Dica
+tip.useamapcache=Configurando a cache de disco (Configura\u00e7\u00f5es -> Salvar mapas para disco)\nvoc\u00ea pode acelerar a exibi\u00e7\u00e3o e reduzir o tr\u00e1fego de rede.
+tip.learntimeparams=Os resultados ser\u00e3o mais precisos se voc\u00ea usar\nRota -> Aprender os par\u00e2metros para estimativa de tempo\nde suas rotas gravadas.
+tip.downloadsrtm=Voc\u00ea pode acelerar chamando\nOnline -> Baixar ladrilhos SRTM\npara obter as altitudes\naproximadas para a vis\u00e3o 3D.
+tip.usesrtmfor3d=Esta rota n\u00e3o possui altitudes.\nVoc\u00ea pode usar as fun\u00e7\u00f5es SRTM para obter as altitudes\naproximadas para a vis\u00e3o 3D.
+tip.manuallycorrelateone=Correlacionando pelo menos uma foto manualmente, a diferen\u00e7a de tempo pode ser calculada para voc\u00ea.
+
# Buttons
button.ok=Ok
button.back=Voltar
@@ -516,6 +636,7 @@ button.yes=Sim
button.no=N\u00e3o
button.yestoall=Sim para todos
button.notoall=N\u00e3o para todos
+button.always=Sempre
button.select=Selecionar
button.selectall=Selecionar todos
button.selectnone=Selecionar nenhum
@@ -530,6 +651,7 @@ button.browse=Navegar...
button.addnew=Adicionar novo
button.delete=Remover
button.manage=Gerenciar
+button.combine=Combinar
# File types
filetype.txt=Arquivos TXT
@@ -540,12 +662,14 @@ filetype.kmz=Arquivos KMZ
filetype.gpx=Arquivos GPX
filetype.pov=Arquivos POV
filetype.svg=Arquivos SVG
+filetype.png=Arquivos PNG
filetype.audio=Arquivos MP3, OGG, WAV
# Display components
display.nodata=Nenhum dado carregado
display.noaltitudes=Dados da rota n\u00e3o incluem altitudes
display.notimestamps=Dados da rota n\u00e3o incluem data-hora
+display.novalues=Dados da rota n\u00e3o incluem valores para este campo
details.trackdetails=Detalhes da track
details.notrack=Nenhuma rota carrgeada
details.track.points=Pontos
@@ -582,6 +706,7 @@ details.nophoto=Nenhuma foto selecionada
details.photo.loading=Carregando
details.photo.bearing=Apontando
details.media.connected=Conectada
+details.media.fullpath=Caminho completo
details.audiodetails=Detalhes do \u00e1udio
details.noaudio=Nenhum arquivo de \u00e1udio selecionado
details.audio.file=Arquivo de \u00e1udio
@@ -600,7 +725,6 @@ fieldname.newsegment=Segmento
fieldname.custom=Personalizado
fieldname.prefix=Campo
fieldname.distance=Dist\u00e2ncia
-fieldname.movingdistance=Dist\u00e2ncia de movimento
fieldname.duration=Dura\u00e7\u00e3o
fieldname.speed=Velocidade
fieldname.verticalspeed=Velocidade vertical
@@ -615,21 +739,39 @@ units.feet=P\u00e9s
units.feet.short=ft
units.kilometres=Quil\u00f4metros
units.kilometres.short=km
+units.kilometresperhour=km por hora
units.kilometresperhour.short=km/h
units.miles=Milhas
units.miles.short=mi
+units.milesperhour=milhas por hora
units.milesperhour.short=mph
+units.nauticalmiles=milhas n\u00e1uticas
+units.nauticalmiles.short=m.n.
+units.nauticalmilesperhour.short=n\u00f3s
+units.metrespersec=metros por segundo
units.metrespersec.short=m/s
+units.feetpersec=p\u00e9s por segundo
units.feetpersec.short=ft/s
units.hours=horas
+units.minutes=minutos
+units.seconds=segundos
units.degminsec=Graus-min-seg
units.degmin=Graus-min
units.deg=Graus
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
+
+# How to combine conditions, such as filters
+logic.and=e
+logic.or=ou
# External urls
url.googlemaps=maps.google.com.br
wikipedia.lang=pt
+openweathermap.lang=pt
# Cardinals for 3d plots
cardinal.n=N
@@ -646,10 +788,13 @@ undo.deletepoint=remover ponto
undo.removephoto=remover foto
undo.removeaudio=remover arquivo de \u00e1udio
undo.deleterange=remover intervalo
+undo.croptrack=recortar rota
undo.deletemarked=remover pontos
undo.insert=inserir pontos
undo.reverse=inverter intervalo
undo.mergetracksegments=mesclar segmentos de rota
+undo.splitsegments=dividir segmentos de rota
+undo.sewsegments=reunir segmentos de rota
undo.addtimeoffset=adicionar diferen\u00e7a de tempo
undo.addaltitudeoffset=adicionar diferen\u00e7a de altitude
undo.rearrangewaypoints=rearrumar pontos
@@ -672,10 +817,8 @@ error.save.failed=Falha ao salvar dados para arquivo
error.saveexif.filenotfound=Falha ao procurar o arquivo de foto
error.saveexif.cannotoverwrite1=Arquivo de foto
error.saveexif.cannotoverwrite2=\u00e9 somente leitura e n\u00e3o pode ser sobrescrito. Gravar para c\u00f3pia?
-error.saveexif.failed1=Falha ao salvar
-error.saveexif.failed2=das imagens
-error.saveexif.forced1=
-error.saveexif.forced2=das imagens for\u00e7adas por solicita\u00e7\u00e3o
+error.saveexif.failed=Falha ao salvar %d das imagens
+error.saveexif.forced=%d das imagens for\u00e7adas por solicita\u00e7\u00e3o
error.load.dialogtitle=Erro ao carregar dados
error.load.noread=N\u00e3o foi poss\u00edvel ler arquivo
error.load.nopoints=Nenhuma informa\u00e7\u00e3o de coordenadas encontrada no arquivo
@@ -686,7 +829,7 @@ error.jpegload.dialogtitle=Erro ao carregar fotos
error.jpegload.nofilesfound=Nenhum arquivo encontrado
error.jpegload.nojpegsfound=Nenhum arquivo jpeg encontrado
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.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.audioload.nofilesfound=Nenhum arquivo de \u00e1udio encontrado
error.gpsload.unknown=Erro desconhecido
error.undofailed.title=Falha ao desfazer
@@ -705,7 +848,13 @@ error.lookupsrtm.nonefound=Nenhum valor de altitude encontrado
error.lookupsrtm.nonerequired=Todos os pontos j\u00e1 possuem altitude, assim n\u00e3o h\u00e1 nada a procurar
error.gpsies.uploadnotok=O servidor Gpsies retornou a mensagem
error.gpsies.uploadfailed=O envio falhou com o erro
+error.showphoto.failed=Falha ao carregar foto
error.playaudiofailed=Falha ao reproduzir arquivo de \u00e1udio
error.cache.notthere=A paste de cache de fundos n\u00e3o foi encontrada
error.cache.empty=A pasta de cache de fundos est\u00e1 vazia
error.cache.cannotdelete=Nenhum fundo pode ser removido
+error.interpolate.invalidparameter=O n\u00famero de pontos deve estar entre 1 e 1000
+error.learnestimationparams.failed=N\u00e3o foi poss\u00edvel aprender par\u00e2metros desta rota.\nTente baixar mais rotas.
+error.tracksplit.nosplit=A rota n\u00e3o pode ser dividida.
+error.downloadsrtm.nocache=Os arquivos n\u00e3o puderam ser salvos.\nPor favor, verifique a cache do disco.
+error.sewsegments.nothingdone=Os segmentos n\u00e3o puderam ser reunidos.\nExistem agora %d segmentos na rota.
diff --git a/tim/prune/lang/prune-texts_ro.properties b/tim/prune/lang/prune-texts_ro.properties
index 992d246..b94b88c 100644
--- a/tim/prune/lang/prune-texts_ro.properties
+++ b/tim/prune/lang/prune-texts_ro.properties
@@ -3,7 +3,7 @@
# Menu entries
menu.file=Fi\u015fier
-menu.file.addphotos=Adaugare foto
+menu.file.addphotos=Adaug\u0103 foto
menu.file.recentfiles=Fi\u015fiere recente
menu.file.save=Salvare
menu.file.exit=Iesire
@@ -12,8 +12,8 @@ menu.track.undo=Anulare
menu.track.clearundo=\u015etergere lista de anulari
menu.track.deletemarked=\u015etergere puncte marcate
menu.track.rearrange=Rearanjare waypoint
-menu.track.rearrange.start=Toate la inceputul fisierului
-menu.track.rearrange.end=Toate la sfarsitul fisierului
+menu.track.rearrange.start=Toate la inceputul fi\u015fierului
+menu.track.rearrange.end=Toate la sfarsitul fi\u015fierului
menu.track.rearrange.nearest=Fiecare la punctul cel mai apropiat al traseului
menu.range=Interval
menu.range.all=Selectare toate
@@ -36,16 +36,20 @@ menu.view.browser.google=Harti Google
menu.view.browser.openstreetmap=Openstreetmap
menu.view.browser.mapquest=Mapquest
menu.view.browser.yahoo=Harti Yahoo
+menu.view.browser.bing=Harti Bing
menu.settings=Set\u0103ri
menu.help=Ajutor
# Popup menu for map
menu.map.zoomin=Apropie
menu.map.zoomout=Departeaza
menu.map.zoomfull=Departeaza la maxim
-menu.map.newpoint=Adaug\u0103 punct
+menu.map.newpoint=Creaz\u0103 punct
+menu.map.drawpoints=Creaz\u0103 serie de puncte
menu.map.connect=Traseaz\u0103 linii \u00eentre puncte
menu.map.autopan=Autovizualizare punct ales
-menu.map.showmap=Arata harta
+menu.map.showmap=Arat\u0103 harta
+menu.map.showscalebar=Arat\u0103 scar\u0103
+menu.map.editmode=Mod de editare
# Alt keys for menus
altkey.menu.file=F
@@ -81,10 +85,14 @@ function.editwaypointname=Editare nume waypoint
function.compress=Comprima traseu
function.deleterange=\u015etergere gama
function.interpolate=Interpolare
+function.addtimeoffset=Adaug\u0103 decalaj timp
+function.addaltitudeoffset=Adaug\u0103 decalaj altitudine
function.findwaypoint=Gasire waypoint
function.charts=Grafice
function.show3d=Vizualizare arborescenta
function.distances=Distan\u0163e
+function.fullrangedetails=Informa\u0163ie complet
+function.loadaudio=Adaug\u0103 audio
function.setmapbg=Fundal
function.setcolours=Selectare culorile
function.setlanguage=Selectare limba
@@ -97,6 +105,7 @@ function.showkeys=Arat\u0103 tastele scurt\u0103turi
function.about=Despre GpsPrune
function.checkversion=Verific\u0103 pentru o versiune noua
function.saveconfig=Salvare set\u0103ri
+function.getweatherforecast=Prognoz\u0103 meteo
# Dialogs
dialog.exit.confirm.title=Ie\u015fire din programul GpsPrune
@@ -120,6 +129,7 @@ dialog.delimiter.other=Alte
dialog.openoptions.deliminfo.records=inregistrari, cu
dialog.openoptions.deliminfo.fields=cimpuri
dialog.openoptions.deliminfo.norecords=Nu sunt inregistrari
+dialog.selecttracks.noname=F\u0103r\u0103 nume
dialog.jpegload.subdirectories=Include subdirectori
dialog.jpegload.loadjpegswithoutcoords=Include fotografii fara coordonate
dialog.jpegload.loadjpegsoutsidearea=Include fotografii din afara zonei curente
@@ -131,14 +141,33 @@ dialog.gpsload.format=Format
dialog.gpsload.getwaypoints=Incarcare waypoints
dialog.gpssend.trackname=Nume traseu
dialog.gpsbabel.filters=Filtre
+dialog.gpsbabel.filter.simplify=Simplifica
dialog.gpsbabel.filter.distance=Distan\u0163\u0103
+dialog.gpsbabel.filter.discard.numsats=Num\u0103r de sateli\u0163i <
dialog.saveoptions.title=Salvare fi\u015fier
dialog.save.table.field=Cimp
dialog.save.table.save=Salvare
dialog.save.overwrite.title=Fi\u015fierul exist\u0103
dialog.save.overwrite.text=Fi\u015fierul exist\u0103. \u00cel suprascriu?
+dialog.exportkml.text=Titlu
+dialog.exportkml.trackcolour=Culoarea liniei
dialog.exportgpx.name=Nume
dialog.exportgpx.desc=Descriere
+dialog.exportgpx.encoding.system=Sistem
+dialog.exportgpx.encoding.utf8=UTF-8
+dialog.exportpov.font=Fontul
+dialog.exportpov.camerax=Vedere X
+dialog.exportpov.cameray=Vedere Y
+dialog.exportpov.cameraz=Vedere Z
+dialog.exportpov.modelstyle=Stilul
+dialog.3d.useterrain=Arat\u0103 teren
+dialog.3d.terraingridsize=Dimensiune a grilei
+dialog.exportpov.baseimage=Imagine cartografice
+dialog.baseimage.title=Imagine cartografice
+dialog.baseimage.tiles=Tigla
+dialog.exportsvg.phi=Azimut \u03D5
+dialog.exportsvg.theta=\u00cenclina\u0163ie \u03B8
+dialog.undo.title=Anulare
dialog.pointedit.intro=V\u0103 rog selecta\u0163i r\u00e2ndul care va fi editat
dialog.pointedit.table.field=Cimp
dialog.pointedit.table.value=Valoare
@@ -153,19 +182,29 @@ dialog.saveexif.table.save=Salveaza
dialog.saveexif.photostatus.connected=Conectat
dialog.saveexif.photostatus.disconnected=Deconectat
dialog.saveexif.photostatus.modified=Modificat
-dialog.saveexif.overwrite=Suprascrie fisiere
+dialog.saveexif.overwrite=Suprascrie fi\u015fiere
dialog.charts.xaxis=Axa X
dialog.charts.yaxis=Axa Y
dialog.distances.currentpoint=Punct curent
+dialog.addmapsource.noname=F\u0103r\u0103 nume
dialog.gpsies.column.name=Nume
dialog.gpsies.column.length=Lungime
dialog.gpsies.description=Descriere
-dialog.gpsies.nodescription=Fara descriere
+dialog.gpsies.nodescription=F\u0103r\u0103 descriere
dialog.wikipedia.column.name=Nume
dialog.wikipedia.column.distance=Distan\u0163\u0103
-dialog.correlate.options.tip=Indiciu: By manually correlating at least one photo, the time offset can be calculated for you.
+dialog.correlate.options.offset.hours=ore,
+dialog.correlate.options.offset.minutes=minute,
+dialog.correlate.options.offset.seconds=secunde
+dialog.pastecoordinates.coords=Coordonate
dialog.about.version=Versiunea
+dialog.about.systeminfo=Informa\u0163ii a sistemului
dialog.about.systeminfo.os=Sistem de operare
+dialog.about.systeminfo.exiflib=Bibliotec\u0103 Exif
+dialog.about.systeminfo.exiflib.internal=Intern
+dialog.about.systeminfo.exiflib.internal.failed=Intern (absent)
+dialog.about.systeminfo.exiflib.external=Extern
+dialog.about.systeminfo.exiflib.external.failed=Extern (absent)
dialog.about.yes=Da
dialog.about.no=Nu
dialog.about.readme=Cite\u015fte-m\u0103
@@ -180,10 +219,23 @@ dialog.setcolours.text=Text
dialog.colourchooser.red=Ro\u0219u
dialog.colourchooser.green=Verde
dialog.colourchooser.blue=Albastru
+dialog.weather.day.today=Ast\u0103zi
+dialog.weather.day.tomorrow=M\u00e2ine
+dialog.weather.day.monday=Luni
+dialog.weather.day.tuesday=Mar\u0163i
+dialog.weather.day.wednesday=Miercuri
+dialog.weather.day.thursday=Joi
+dialog.weather.day.friday=Vineri
+dialog.weather.day.saturday=S\u00e2mb\u0103t\u0103
+dialog.weather.day.sunday=Duminic\u0103
# Confirm messages
-confirm.loadfile=Date incarcate din fisier
+confirm.loadfile=Date incarcate din fi\u015fier
confirm.save.ok1=Salvat cu succes
+confirm.save.ok2=puncte \u00een
+
+# Tips
+tip.title=Indiciu
# Buttons
button.ok=OK
@@ -214,20 +266,22 @@ button.manage=Administra
button.combine=Combina
# File types
-filetype.txt=Fisier text
+filetype.txt=Fi\u015fiere text
filetype.jpeg=Imagine JPEG (*.jpg)
-filetype.kmlkmz=Fisier KML, KMZ
-filetype.kml=Fisier KML
-filetype.kmz=Fisier KMZ
-filetype.gpx=Fisier GPX
-filetype.pov=Fisier POV
-filetype.svg=Fisier SVG
-filetype.png=Fisier PNG
-filetype.audio=Fisier MP3, OGG, WAV
+filetype.kmlkmz=Fi\u015fiere KML, KMZ
+filetype.kml=Fi\u015fiere KML
+filetype.kmz=Fi\u015fiere KMZ
+filetype.gpx=Fi\u015fiere GPX
+filetype.pov=Fi\u015fiere POV
+filetype.svg=Fi\u015fiere SVG
+filetype.png=Fi\u015fiere PNG
+filetype.audio=Fi\u015fiere MP3, OGG, WAV
# Display components
+details.trackdetails=Detalii traseul
details.track.points=Puncte
-details.pointdetails=Punct
+details.pointdetails=Detalii punctul
+details.rangedetails=Detalii intervalul
details.range.selected=Selectat
details.range.to=la
details.altitude.to=la
@@ -240,6 +294,8 @@ display.range.time.days=z
details.range.avespeed=Viteza medie
details.range.maxspeed=Viteza maxim\u0103
details.lists.photos=Foto-uri
+details.lists.audio=Audio
+details.audiodetails=Detalii audio
# Field names
fieldname.latitude=Latitudine
@@ -282,3 +338,6 @@ cardinal.n=N
cardinal.s=S
cardinal.e=E
cardinal.w=V
+
+wikipedia.lang=ro
+openweathermap.lang=ro
diff --git a/tim/prune/lang/prune-texts_ru.properties b/tim/prune/lang/prune-texts_ru.properties
index a34b95d..a851289 100644
--- a/tim/prune/lang/prune-texts_ru.properties
+++ b/tim/prune/lang/prune-texts_ru.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0444\u043
menu.file.recentfiles=\u041f\u0440\u0438\u043d\u044f\u0442\u044b\u0435 \u0444\u0430\u0439\u043b\u044b
menu.file.save=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u043a \u0442\u0435\u043a\u0441\u0442
menu.file.exit=\u0412\u044b\u0445\u043e\u0434
+menu.online=\u041e\u043d\u043b\u0430\u0439\u043d
menu.track=\u0422\u0440\u0435\u043a
menu.track.undo=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c
menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439
@@ -57,6 +58,7 @@ menu.map.editmode=\u0420\u0435\u0436\u0438\u043c \u0440\u0435\u0434\u0430\u043a\
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=N
altkey.menu.track=T
altkey.menu.range=R
altkey.menu.point=P
@@ -105,6 +107,7 @@ function.setpaths=\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \
function.getgpsies=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0442\u0440\u0435\u043a\u0438
function.uploadgpsies=\u0412\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0442\u0440\u0435\u043a \u043d\u0430 gpsies.com
function.lookupsrtm=\u0412\u044b\u0441\u043e\u0442\u044b \u0432 SRTM
+function.downloadsrtm=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c SRTM \u0434\u0430\u043d\u043d\u044b\u0435
function.getwikipedia=\u0421\u0442\u0430\u0442\u044c\u044f \u043e \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0432 \u0412\u0438\u043a\u0438
function.searchwikipedianames=\u041f\u043e\u0438\u0441\u043a \u0441\u0442\u0430\u0442\u0435\u0439 \u0432 \u0412\u0438\u043a\u0438 \u043f\u043e \u0438\u043c\u0435\u043d\u0438
function.downloadosm=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c OSM \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u0442\u0435\u0440\u0440\u0438\u0442\u043e\u0440\u0438\u044e
@@ -133,6 +136,7 @@ function.checkversion=\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0
function.saveconfig=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438
function.diskcache=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u044b \u043d\u0430 \u0434\u0438\u0441\u043a
function.managetilecache=\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0435\u0448\u0435\u043c
+function.getweatherforecast=\u043f\u0440\u043e\u0433\u043d\u043e\u0301\u0437 \u043f\u043e\u0433\u043e\u0301\u0434\u044b
# Dialogs
dialog.exit.confirm.title=\u0412\u044b\u0445\u043e\u0434
@@ -176,6 +180,9 @@ dialog.gpsload.save=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043
dialog.gpssend.sendwaypoints=\u041f\u043e\u0441\u043b\u0430\u0442\u044c \u043f\u0443\u0442\u0435\u0432\u044b\u0435 \u0442\u043e\u0447\u043a\u0438
dialog.gpssend.sendtracks=\u041f\u043e\u0441\u043b\u0430\u0442\u044c \u0442\u0440\u0435\u043a\u0438
dialog.gpssend.trackname=\u0418\u043c\u044f \u0442\u0440\u0435\u043a\u0430
+dialog.gpsbabel.filters=\u0424\u0438\u043b\u044c\u0442\u0440\u044b
+dialog.addfilter.title=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0444\u0438\u043b\u044c\u0442\u0440
+dialog.gpsbabel.filter.discard.numsats=\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u043f\u0443\u0442\u043d\u0438\u043a\u043e\u0432 <
dialog.saveoptions.title=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0444\u0430\u0439\u043b
dialog.save.fieldstosave=\u041f\u043e\u043b\u044f \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435
dialog.save.table.field=\u041f\u043e\u043b\u0435
@@ -210,8 +217,10 @@ dialog.exportpov.modelstyle=\u0421\u0442\u0438\u043b\u044c \u043c\u043e\u0434\u0
dialog.exportpov.ballsandsticks=\u041c\u044f\u0447\u0438 \u0438 \u043f\u0430\u043b\u043e\u0447\u043a\u0438
dialog.exportpov.tubesandwalls=\u0422\u0440\u0443\u0431\u044b \u0438 \u0441\u0442\u0435\u043d\u044b
dialog.3d.warningtracksize=\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0432 \u0442\u0440\u0435\u043a\u0435 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0447\u0435\u043a - Java3D \u043c\u043e\u0436\u0435\u0442 \u0435\u0433\u043e \u043d\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c!\n\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\ [...]
+dialog.3d.terraingridsize=\u0420\u0430\u0437\u043c\u0435\u0440 \u0441\u0435\u0442\u043a\u0438
dialog.baseimage.mapsource=\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430
dialog.baseimage.zoom=\u041c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442
+dialog.baseimage.tiles=\u0422\u0430\u0439\u043b\u044b
dialog.baseimage.size=\u0420\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f
dialog.exportsvg.text=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430 SVG
dialog.exportsvg.phi=\u0410\u0437\u0438\u043c\u0443\u0442 \u03d5
@@ -236,7 +245,9 @@ dialog.undo.none.text=\u041d\u0435\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0
dialog.clearundo.title=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0434\u043b\u044f \u043e\u0442\u043c\u0435\u043d\u044b
dialog.clearundo.text=\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0434\u043b\u044f \u043e\u0442\u043c\u0435\u043d\u044b?\n\u0421\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0438\u0449\u0435\ [...]
dialog.pointedit.title=\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u0447\u043a\u0443
+dialog.pointedit.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f
dialog.pointedit.table.field=\u041f\u043e\u043b\u0435
+dialog.pointedit.nofield=\u041f\u043e\u043b\u0435 \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u0430
dialog.pointedit.table.value=\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435
dialog.pointnameedit.name=\u0418\u043c\u044f \u043f\u0443\u0442\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438
dialog.pointnameedit.uppercase=\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440
@@ -283,6 +294,7 @@ dialog.estimatetime.details=\u0414\u0435\u0442\u0430\u043b\u0438\u0437\u0430\u04
dialog.estimatetime.climb=\u041f\u043e\u0434\u044a\u0435\u043c
dialog.estimatetime.descent=\u0421\u043f\u0443\u0441\u043a
dialog.estimatetime.parameters=\u041f\u0430\u0440\u0430\u0301\u043c\u0435\u0442\u0440\u044b
+dialog.estimatetime.parameters.timefor=\u0412\u0440\u0435\u043c\u044f \u0434\u043b\u044f
dialog.setmapbg.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0444\u043e\u043d\u0430 \u0438\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043d\u043e\u0432\u044b\u0439
dialog.addmapsource.title=\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0444\u043e\u043d\u0430
dialog.addmapsource.sourcename=\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430
@@ -318,7 +330,6 @@ dialog.correlate.photoselect.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0
dialog.correlate.select.photoname=\u0418\u043c\u044f \u0444\u043e\u0442\u043e
dialog.correlate.select.timediff=\u0420\u0430\u0437\u043d\u0438\u0446\u0430 \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438
dialog.correlate.select.photolater=\u0424\u043e\u0442\u043e \u043f\u043e\u0437\u0434\u043d\u0435\u0435
-dialog.correlate.options.tip=\u0421\u043e\u0432\u0435\u0442: \u041f\u0440\u0438 \u0440\u0443\u0447\u043d\u043e\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043a\u0440\u0430\u0439\u043d\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043c\u0435\u0442\u043a\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u0430 \u0430\u0432 [...]
dialog.correlate.options.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f
dialog.correlate.options.offsetpanel=\u041e\u0442\u043c\u0435\u0442\u043a\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438
dialog.correlate.options.offset=\u0421\u043c\u0435\u0449\u0435\u043d\u0438\u0435
@@ -361,8 +372,7 @@ dialog.compress.duplicates.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u043
dialog.compress.douglaspeucker.title=\u0421\u0436\u0430\u0442\u0438\u0435 \u043f\u043e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0443 Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=\u0420\u0430\u0437\u043c\u0430\u0445
dialog.compress.summarylabel=\u0422\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f
-dialog.compress.confirm1=
-dialog.compress.confirm2=\u0442\u043e\u0447\u043a\u0438 \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u044b.\n\u0427\u0442\u043e\u0431\u044b \u0438\u0445 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043c\u0435\u043d\u044e \u0422\u0440\u0435\u043a->\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438
+dialog.compress.confirm=%d \u0442\u043e\u0447\u043a\u0438 \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u044b.\n\u0427\u0442\u043e\u0431\u044b \u0438\u0445 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043c\u0435\u043d\u044e \u0422\u0440\u0435\u043a->\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438
dialog.compress.confirmnone=\u043d\u0435\u0442 \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a
dialog.deletemarked.nonefound=\u041d\u0435\u0442 \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f
dialog.pastecoordinates.desc=\u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0437\u0434\u0435\u0441\u044c
@@ -470,13 +480,27 @@ dialog.diskcache.tileset.multiple=\u043c\u043d\u043e\u0436\u0435\u0441\u0442\u04
dialog.diskcache.deleteold=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0440\u044b\u0445 \u0442\u0430\u0439\u043b\u043e\u0432
dialog.diskcache.maximumage=\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0432\u043e\u0437\u0440\u0430\u0441\u0442 (\u0432 \u0434\u043d\u044f\u0445)
dialog.diskcache.deleteall=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u0442\u0430\u0439\u043b\u044b
-dialog.diskcache.deleted1=\u0423\u0434\u0430\u043b\u0435\u043d\u043e
-dialog.diskcache.deleted2=\u0444\u0430\u0439\u043b\u043e\u0432 \u0438\u0437 \u043a\u044d\u0448\u0430
+dialog.diskcache.deleted=\u0423\u0434\u0430\u043b\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u043e\u0432 \u0438\u0437 \u043a\u044d\u0448\u0430
dialog.deletefieldvalues.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430
dialog.deletefieldvalues.nofields=\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 \u043d\u0435\u0442 \u043f\u043e\u043b\u0435\u0439 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f
dialog.setlinewidth.text=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u043e\u043b\u0449\u0438\u043d\u0443 \u043b\u0438\u043d\u0438\u0439 \u0434\u043b\u044f \u0442\u0440\u0435\u043a\u043e\u0432 (1-4)
dialog.downloadosm.desc=\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 OSM \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438:
dialog.searchwikipedianames.search=\u041f\u043e\u0438\u0441\u043a \u0434\u043b\u044f:
+dialog.weather.location=\u043c\u0435\u0301\u0441\u0442\u043e
+dialog.weather.temperatureunits=\u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0301\u0440\u0430
+dialog.weather.day.now=\u0422\u0435\u043a\u0443\u0449\u0430\u044f \u043f\u043e\u0433\u043e\u0434\u0430
+dialog.weather.day.today=\u0421\u0435\u0433\u043e\u0434\u043d\u044f
+dialog.weather.day.tomorrow=\u0417\u0430\u0432\u0442\u0440\u0430
+dialog.weather.day.monday=\u041f\u043e\u043d\u0435\u0434\u0435\u043b\u044c\u043d\u0438\u043a
+dialog.weather.day.tuesday=\u0412\u0442\u043e\u0440\u043d\u0438\u043a
+dialog.weather.day.wednesday=\u0421\u0440\u0435\u0434\u0430
+dialog.weather.day.thursday=\u0427\u0435\u0442\u0432\u0435\u0440\u0433
+dialog.weather.day.friday=\u041f\u044f\u0442\u043d\u0438\u0446\u0430
+dialog.weather.day.saturday=\u0421\u0443\u0431\u0431\u043e\u0442\u0430
+dialog.weather.day.sunday=\u0412\u043e\u0441\u043a\u0440\u0435\u0441\u0435\u043d\u044c\u0435
+dialog.weather.wind=\u0432\u0435\u0301\u0442\u0435\u0440
+dialog.weather.temp=\u0442\u0435\u043c\u043f
+dialog.weather.humidity=\u0432\u043b\u0430\u0301\u0433\u0430
# 3d window
dialog.3d.title=GpsPrune 3D-\u0432\u0438\u0434
@@ -498,8 +522,7 @@ confirm.rearrangephotos=\u0424\u043e\u0442\u043e \u043f\u0435\u0440\u0435\u0434\
confirm.cutandmove=\u041e\u0442\u043e\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u043e
confirm.interpolate=\u0422\u043e\u0447\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b
confirm.convertnamestotimes=\u0418\u043c\u044f \u043f\u0443\u0442\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u043e
-confirm.saveexif.ok1=\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e
-confirm.saveexif.ok2=\u0444\u0430\u0439\u043b\u044b \u0441 \u0444\u043e\u0442\u043e
+confirm.saveexif.ok=\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u044b \u0441 \u0444\u043e\u0442\u043e
confirm.undo.single=\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043e\u0442\u043c\u0435\u043d\u044b
confirm.undo.multi=\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043e\u0442\u043c\u0435\u043d\u044b
confirm.jpegload.single=\u0444\u043e\u0442\u043e \u0431\u044b\u043b\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e
@@ -513,13 +536,18 @@ confirm.correlatephotos.multi=\u0444\u043e\u0442\u043e \u0431\u044b\u043b\u0438
confirm.createpoint=\u0442\u043e\u0447\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0430
confirm.rotatephoto=\u0444\u043e\u0442\u043e \u043f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u043e
confirm.running=\u0420\u0430\u0431\u043e\u0442\u0430\u044e...
-confirm.lookupsrtm1=\u041d\u0430\u0439\u0434\u0435\u043d\u043e
-confirm.lookupsrtm2=\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b
+confirm.lookupsrtm=\u041d\u0430\u0439\u0434\u0435\u043d\u043e %d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b
+confirm.downloadsrtm=\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u043e\u0432 \u0432 \u043a\u0435\u0448
+confirm.downloadsrtm.1=\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u043e\u0432 \u0432 \u043a\u0435\u0448
confirm.deletefieldvalues=\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u044b
confirm.audioload=\u0424\u0430\u0439\u043b\u044b \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b
confirm.correlateaudios.single=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u044c \u0431\u044b\u043b\u0438 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430
confirm.correlateaudios.multi=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u0431\u044b\u043b\u0438 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b
+# Tips, shown just once when appropriate
+tip.title=\u0421\u043e\u0432\u0435\u0442
+tip.manuallycorrelateone=\u041f\u0440\u0438 \u0440\u0443\u0447\u043d\u043e\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043a\u0440\u0430\u0439\u043d\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043c\u0435\u0442\u043a\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438 [...]
+
# Buttons
button.ok=OK
button.back=\u041d\u0430\u0437\u0430\u0434
@@ -623,7 +651,6 @@ fieldname.newsegment=\u0421\u0435\u0433\u043c\u0435\u043d\u0442
fieldname.custom=\u041e\u0431\u044b\u0447\u043d\u043e
fieldname.prefix=\u041f\u043e\u043b\u0435
fieldname.distance=\u0420\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435
-fieldname.movingdistance=\u0420\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u0435\u0437 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043a\u043e\u0432
fieldname.duration=\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c
fieldname.speed=\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c
fieldname.verticalspeed=\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c
@@ -710,10 +737,8 @@ error.save.failed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441
error.saveexif.filenotfound=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0444\u0430\u0439\u043b \u0441 \u0444\u043e\u0442\u043e
error.saveexif.cannotoverwrite1=\u0444\u0430\u0439\u043b \u0441 \u0444\u043e\u0442\u043e
error.saveexif.cannotoverwrite2=\u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f, \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u043d! \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u043a \u043a\u043e\u043f\u0438\u044e?
-error.saveexif.failed1=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c
-error.saveexif.failed2=\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435(-\u0438\u0439)
-error.saveexif.forced1=
-error.saveexif.forced2=\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435(-\u0438\u0439) \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e
+error.saveexif.failed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c %d \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435(-\u0438\u0439)
+error.saveexif.forced=%d \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435(-\u0438\u0439) \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e
error.load.dialogtitle=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0434\u0430\u043d\u043d\u044b\u0445
error.load.noread=\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0444\u0430\u0439\u043b
error.load.nopoints=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445
@@ -724,7 +749,7 @@ error.jpegload.dialogtitle=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u04
error.jpegload.nofilesfound=\u0424\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
error.jpegload.nojpegsfound=JEPG-\u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
error.jpegload.nogpsfound=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 GPS-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f
-error.jpegload.exifreadfailed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c EXIF-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u041d\u0435\u0442 EXIF-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u200b\u200b\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c\n\u0431\u0435\u0437 \u0438\u0441\ [...]
+error.jpegload.exifreadfailed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c Exif-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u041d\u0435\u0442 Exif-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u200b\u200b\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c\n\u0431\u0435\u0437 \u0438\u0441\ [...]
error.audioload.nofilesfound=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
error.gpsload.unknown=\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430
error.undofailed.title=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c
diff --git a/tim/prune/lang/prune-texts_sv.properties b/tim/prune/lang/prune-texts_sv.properties
new file mode 100644
index 0000000..eeffcd9
--- /dev/null
+++ b/tim/prune/lang/prune-texts_sv.properties
@@ -0,0 +1,58 @@
+# Text entries for the GpsPrune application
+# Swedish entries
+
+# Menu entries
+menu.file=Fil
+menu.file.addphotos=L\u00e4gg till foto
+menu.file.recentfiles=Senaste filer
+menu.file.save=Spara som text
+menu.file.exit=Avsluta
+menu.track=Sp\u00e5r
+menu.track.undo=\u00c5ngra
+menu.track.clearundo=Rensa \u00e5ngra
+menu.track.markrectangle=Markera punkter i rektangel
+menu.track.deletemarked=Radera markerade punkter
+menu.track.rearrange=Arrangera om ruttpunkter
+menu.track.rearrange.start=Alla till b\u00f6rjan av fil
+menu.track.rearrange.end=Alla till slut av fil
+menu.track.rearrange.nearest=Varje till n\u00e4rmaste sp\u00e5rpunkt
+menu.range=Omr\u00e5de
+menu.range.all=V\u00e4lj alla
+menu.range.none=V\u00e4lj ingen
+menu.range.start=St\u00e4ll in b\u00f6rjan p\u00e5 omr\u00e5de
+menu.range.end=St\u00e4ll in slut p\u00e5 omr\u00e5de
+menu.range.average=Medelv\u00e4rdesval
+menu.range.reverse=Backa omr\u00e5de
+menu.range.mergetracksegments=Sl\u00e5 ihop sp\u00e5rsegment
+menu.range.cutandmove=Klipp och flytta urval
+menu.point=Punkt
+menu.point.editpoint=Redigera punkt
+menu.point.deletepoint=Radera punkt
+menu.photo=Foto
+menu.photo.saveexif=Spara och avsluta
+menu.audio=Ljud
+menu.view=Vy
+menu.view.showsidebars=Visa sidolister
+menu.view.browser=Karta i ett l\u00e4sarf\u00f6nster
+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=Inst\u00e4llningar
+menu.settings.onlinemode=Ladda karta fr\u00e5n Internet
+menu.settings.autosave=Autospara inst\u00e4llningar vid avslut
+menu.help=Hj\u00e4lp
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=S
+altkey.menu.range=O
+altkey.menu.point=P
+altkey.menu.view=V
+altkey.menu.photo=T
+altkey.menu.audio=L
+altkey.menu.settings=I
+altkey.menu.help=H
+
+openweathermap.lang=se
diff --git a/tim/prune/lang/prune-texts_tr.properties b/tim/prune/lang/prune-texts_tr.properties
index 32eed12..4baf0de 100644
--- a/tim/prune/lang/prune-texts_tr.properties
+++ b/tim/prune/lang/prune-texts_tr.properties
@@ -5,7 +5,7 @@
menu.file=Dosya
menu.file.addphotos=Foto ekle
menu.file.save=Kaydet
-menu.file.exit=�\u0131k\u0131\u015f
+menu.file.exit=\u00c7\u0131k\u0131\u015f
menu.track=\u0130z
menu.track.undo=Geri al
menu.track.clearundo=Geri alma listesi s\u0131f\u0131rla
@@ -198,7 +198,7 @@ dialog.saveexif.overwrite=Dosyalar\u0131n \u00fczerinde yaz
dialog.saveexif.force=Ufak hatalar\u0131 bo\u015fver
dialog.charts.xaxis=X axis
dialog.charts.yaxis=Y axis
-dialog.charts.output=�\u0131kt\u0131
+dialog.charts.output=\u00c7\u0131kt\u0131
dialog.charts.screen=Ekranda g\u00f6ster
dialog.charts.svg=SVG dosya olarak g\u00f6ster
dialog.charts.svgwidth=SVG geni\u015fli\u011fi
@@ -228,7 +228,7 @@ dialog.correlate.options.offset.seconds=saniye
dialog.correlate.options.photolater=Foto noktadan sonra
dialog.correlate.options.pointlaterphoto=Nokta fotodan sonra
dialog.pastecoordinates.coords=Koordinatlar
-dialog.help.help=Ayr\u0131nt\u0131l\u0131 bilgi ve kullanma k\u0131lavuzu i\u00e7in l\u00fctfen\n http://activityworkshop.net/software/gpsprune/\n sitesinde bak.
+dialog.help.help=Ayr\u0131nt\u0131l\u0131 bilgi ve kullanma k\u0131lavuzu i\u00e7in l\u00fctfen\n http://gpsprune.activityworkshop.net/\n sitesinde bak.
dialog.about.version=S\u00fcr\u00fcm
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune GPS ayg\u0131tlardan veri y\u00fckler, g\u00f6r\u00fcnt\u00fcler ver d\u00fczenler bir uygulamad\u0131r.
@@ -245,7 +245,7 @@ dialog.about.systeminfo.gpsbabel=Gpsbabel kuruldu
dialog.about.systeminfo.gnuplot=Gnuplot kuruldu
dialog.about.yes=Evet
dialog.about.no=Hay\u0131r
-dialog.about.credits.translators=�evirmen
+dialog.about.credits.translators=\u00c7evirmen
dialog.about.credits.thanks=Te\u015fekk\u00fcrler
dialog.about.readme=Beni oku
dialog.checkversion.uptodate=GpsPrune'nin so s\u00fcr\u00fcm\u00fc kullan\u0131yorsun.
@@ -253,7 +253,7 @@ dialog.checkversion.newversion1=GpsPrune'nin yeni bir s\u00fcr\u00fcm\u00fc \u00
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Yeni s\u00fcr\u00fcm\u00fcn\u00fcn devir tarihi
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Yeni s\u00fcr\u00fcm indirmek i\u00e7in http://activityworkshop.net/software/gpsprune/download.html adresine git.
+dialog.checkversion.download=Yeni s\u00fcr\u00fcm indirmek i\u00e7in http://gpsprune.activityworkshop.net/download.html adresine git.
dialog.keys.intro=Fare yerinde a\u015fa\u011f\u0131daki k\u0131sayol tu\u015flar\u0131 kullanabilirsin:
dialog.keys.keylist=<table><tr><td>Ok tu\u015flar\u0131</td><td>Haritay\u0131 sola/sa\u011fa/a\u015fa\u011f\u0131/yukar\u0131 kayd\u0131r</td></tr><tr><td>Ctrl + sol, sa\u011f</td><td>\u00d6nceki/sonraki noktay\u0131 se\u00e7</td></tr><tr><td>Ctrl + yukar/a\u015fa\u011f\u0131</td><td>Yak\u0131nla\u015ft\u0131r/Uzakla\u015ft\u0131r</td></tr><tr><td>Del</td><td>Se\u00e7ili noltay\u0131 sil</td></tr></table>
dialog.saveconfig.desc=A\u011fa\u015f\u0131daki ayarlar\u0131 bir dasyada kaydedilir:
@@ -274,7 +274,7 @@ dialog.addaltitude.noaltitudes=Se\u00e7ili s\u0131rada y\u00fckseklik bilgisi bu
dialog.addaltitude.desc=Eklenecek y\u00fckseklik ofseti
dialog.setcolours.background=Arkafonu
dialog.setcolours.borders=Kenarlar
-dialog.setcolours.lines=�izgiler
+dialog.setcolours.lines=\u00c7izgiler
dialog.setcolours.primary=Birincil
dialog.setcolours.secondary=\u0130kincil
dialog.setcolours.point=Noktalar
@@ -300,7 +300,7 @@ button.overwrite=\u00dczerinde yaz
button.moveup=Yukar\u0131
button.movedown=A\u015fa\u011f\u0131
button.edit=D\u00fczenle
-button.exit=�\u0131k\u0131\u015f
+button.exit=\u00c7\u0131k\u0131\u015f
button.close=Kapat
button.continue=Devam
button.yes=Evet
@@ -400,6 +400,8 @@ units.iso8601=ISO 8601
# External urls
url.googlemaps=maps.google.com
+wikipedia.lang=tr
+openweathermap.lang=tr
# Cardinals for 3d plots
cardinal.n=K
diff --git a/tim/prune/lang/prune-texts_uk.properties b/tim/prune/lang/prune-texts_uk.properties
new file mode 100644
index 0000000..6f74d55
--- /dev/null
+++ b/tim/prune/lang/prune-texts_uk.properties
@@ -0,0 +1,234 @@
+# Text entries for the GpsPrune application
+# Ukrainian entries thanks to serhijdubyk
+
+# Menu entries
+menu.file=\u0424\u0430\u0439\u043b
+menu.file.addphotos=\u0414\u043e\u0434\u0430\u0442\u0438 \u0444\u043e\u0442\u043e
+menu.file.recentfiles=\u041f\u0440\u0438\u0439\u043d\u044f\u0442\u0456 \u0444\u0430\u0439\u043b\u0438
+menu.file.save=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u044f\u043a \u0442\u0435\u043a\u0441\u0442
+menu.file.exit=\u0412\u0438\u0445\u0456\u0434
+menu.track=\u0422\u0440\u0435\u043a
+menu.track.undo=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438
+menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043c\u0456\u043d
+menu.track.markrectangle=\u041f\u043e\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u0443 \u043f\u0440\u044f\u043c\u043e\u043a\u0443\u0442\u043d\u0438\u043a\u0443
+menu.track.deletemarked=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u043f\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u0456 \u0442\u043e\u0447\u043a\u0438
+menu.track.rearrange=\u041f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0438
+menu.track.rearrange.start=\u0423\u0441\u0435 \u043d\u0430 \u043f\u043e\u0447\u0430\u0442\u043e\u043a \u0444\u0430\u0439\u043b\u0443
+menu.track.rearrange.end=\u0423\u0441\u0435 \u043d\u0430 \u043a\u0456\u043d\u0435\u0446\u044c \u0444\u0430\u0439\u043b\u0443
+menu.track.rearrange.nearest=\u041f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u0434\u043e \u043d\u0430\u0439\u0431\u043b\u0438\u0436\u0447\u043e\u0457
+menu.range=\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b
+menu.range.all=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u0443\u0441\u0456
+menu.range.none=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438 \u0432\u0438\u0431\u0456\u0440\u043a\u0443
+menu.range.start=\u041f\u043e\u0447\u0430\u0442\u043e\u043a \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0443
+menu.range.end=\u041a\u0456\u043d\u0435\u0446\u044c \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0443
+menu.range.average=\u0422\u043e\u0447\u043a\u0430 \u043f\u043e \u0441\u0435\u0440\u0435\u0434\u043d\u044c\u043e\u043c\u0443
+menu.range.reverse=\u041f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b
+menu.range.mergetracksegments=\u0417\u043b\u0438\u0442\u0438 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0438 \u0442\u0440\u0435\u043a\u0443
+menu.range.cutandmove=\u0412\u0438\u0440\u0456\u0437\u0430\u0442\u0438 \u0456 \u043f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u0432\u0438\u0431\u0456\u0440\u043a\u0443
+menu.point=\u0422\u043e\u0447\u043a\u0430
+menu.point.editpoint=\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0442\u043e\u0447\u043a\u0443
+menu.point.deletepoint=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0443
+menu.photo=\u0421\u0432\u0456\u0442\u043b\u0438\u043d\u0438
+menu.photo.saveexif=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0432 Exif
+menu.audio=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438
+menu.view=\u0412\u0438\u0433\u043b\u044f\u0434
+menu.view.showsidebars=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u043f\u0430\u043d\u0435\u043b\u044c
+menu.view.browser=\u0412\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u043c\u0430\u043f\u0443 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0456
+menu.view.browser.google=\u041c\u0430\u043f\u0438 Google
+menu.view.browser.openstreetmap=Openstreetmap
+menu.view.browser.mapquest=Mapquest
+menu.view.browser.yahoo=\u041c\u0430\u043f\u0438 Yahoo
+menu.view.browser.bing=\u041c\u0430\u043f\u0438 Bing
+menu.settings=\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f
+menu.settings.onlinemode=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u043c\u0430\u043f\u0438 \u0437 \u0406\u043d\u0442\u0435\u0440\u043d\u0435\u0442
+menu.settings.autosave=\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u0435 \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u043d\u044f \u043f\u0440\u0438 \u0432\u0438\u0445\u043e\u0434\u0456
+menu.help=\u0414\u043e\u043f\u043e\u043c\u043e\u0433\u0430
+# Popup menu for map
+menu.map.zoomin=\u0417\u0431\u0456\u043b\u044c\u0448\u0438\u0442\u0438
+menu.map.zoomout=\u0417\u043c\u0435\u043d\u0448\u0438\u0442\u0438
+menu.map.zoomfull=\u0417\u0431\u0456\u043b\u044c\u0448\u0438\u0442\u0438 \u0434\u043e \u043f\u043e\u0432\u043d\u043e\u0457 \u0448\u043a\u0430\u043b\u0438
+menu.map.newpoint=\u0421\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043e\u0434\u043d\u0443 \u0442\u043e\u0447\u043a\u0443
+menu.map.drawpoints=\u0421\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043a\u0456\u043b\u044c\u043a\u0430 \u0442\u043e\u0447\u043e\u043a
+menu.map.connect=\u0417\u2019\u0454\u0434\u043d\u0430\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u0442\u0440\u0435\u043a\u0443 \u0437 \u043b\u0456\u043d\u0456\u0454\u044e
+menu.map.autopan=\u0412\u0456\u0434\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u0438 \u0432\u0438\u0431\u0440\u0430\u043d\u0435
+menu.map.showmap=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u041e\u0421\u041c-\u043c\u0430\u043f\u0443
+menu.map.showscalebar=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u043b\u0456\u043d\u0456\u0439\u043a\u0443 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0443
+menu.map.editmode=\u0420\u0435\u0436\u0438\u043c \u0440\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u043d\u043d\u044f
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=T
+altkey.menu.range=R
+altkey.menu.point=P
+altkey.menu.view=V
+altkey.menu.photo=O
+altkey.menu.audio=A
+altkey.menu.settings=S
+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=\u0412\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u0444\u0430\u0439\u043b
+function.importwithgpsbabel=\u0406\u043c\u043f\u043e\u0440\u0442 \u0444\u0430\u0439\u043b\u0443 \u0437GPSBabel
+function.loadfromgps=\u0412\u0438\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0437 GPS
+function.sendtogps=\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0432 GPS
+function.exportkml=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 KML
+function.exportgpx=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 GPX
+function.exportpov=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 POV
+function.exportsvg=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 SVG
+function.exportimage=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f
+function.editwaypointname=\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0456\u043c\u2019\u044f \u0448\u043b\u044f\u0445\u043e\u0432\u043e\u0457 \u0442\u043e\u0447\u043a\u0438
+function.compress=\u0421\u0442\u0438\u0441\u043d\u0443\u0442\u0438 \u0442\u0440\u0435\u043a
+function.deleterange=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b
+function.croptrack=\u041e\u0431\u0440\u0456\u0437\u0430\u0442\u0438 \u0442\u0440\u0435\u043a
+function.interpolate=\u0406\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0456\u044f \u0442\u043e\u0447\u043e\u043a
+function.addtimeoffset=\u0414\u043e\u0434\u0430\u0442\u0438 \u043f\u043e\u0437\u043d\u0430\u0447\u043a\u0443 \u0447\u0430\u0441\u0443
+function.addaltitudeoffset=\u0414\u043e\u0434\u0430\u0442\u0438 \u043f\u043e\u0437\u043d\u0430\u0447\u043a\u0443 \u0432\u0438\u0441\u043e\u0442\u0438
+function.convertnamestotimes=\u041f\u0435\u0440\u0435\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u0456\u043c\u2019\u044f \u0448\u043b\u044f\u0445\u043e\u0432\u043e\u0457 \u0442\u043e\u0447\u043a\u0438 \u0443 \u0447\u0430\u0441
+function.deletefieldvalues=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u043f\u043e\u043b\u044f
+function.findwaypoint=\u0417\u043d\u0430\u0439\u0442\u0438 \u0448\u043b\u044f\u0445\u043e\u0432\u0443 \u0442\u043e\u0447\u043a\u0443
+function.pastecoordinates=\u0412\u0432\u0435\u0434\u0435\u043d\u043d\u044f \u043d\u043e\u0432\u0438\u0445 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442
+function.charts=\u0413\u0440\u0430\u0444\u0456\u043a\u0438
+function.show3d=3D-\u0432\u0438\u0433\u043b\u044f\u0434
+function.distances=\u0412\u0456\u0434\u0441\u0442\u0430\u043d\u0456
+function.fullrangedetails=\u0414\u0435\u0442\u0430\u043b\u0456\u0437\u0430\u0446\u0456\u044f \u043f\u043e \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0443
+function.estimatetime=\u041f\u0440\u0438\u0431\u043b\u0438\u0437\u043d\u0438\u0439 \u0447\u0430\u0441
+function.learnestimationparams=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 \u0432\u0433\u0430\u0434\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0440\u0438\u0431\u043b\u0438\u0437\u043d\u043e\u0433\u043e \u0447\u0430\u0441\u0443
+function.setmapbg=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u043c\u0430\u043f\u0443-\u043f\u0456\u0434\u043a\u043b\u0430\u0434\u043a\u0443
+function.setpaths=\u0412\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0438 \u0448\u043b\u044f\u0445\u0438 \u0434\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c
+function.getgpsies=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0442\u0440\u0435\u043a\u0438 \u0437 Gpsies
+function.uploadgpsies=\u0412\u0438\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0442\u0440\u0435\u043a\u0438 \u043d\u0430 Gpsies
+function.lookupsrtm=\u041e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 \u0432\u0438\u0441\u043e\u0442\u0438 \u0437 SRTM
+function.getwikipedia=\u041e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 \u043d\u0430\u0439\u0431\u043b\u0438\u0436\u0447\u0443 \u0441\u0442\u0430\u0442\u0442\u044e \u0437 \u0412\u0456\u043a\u0456\u043f\u0435\u0434\u0456\u0457
+function.searchwikipedianames=\u041f\u043e\u0448\u0443\u043a \u0441\u0442\u0430\u0442\u0435\u0439 \u0437 \u0412\u0456\u043a\u0456\u043f\u0435\u0434\u0456\u0457 \u0437\u0430 \u043d\u0430\u0437\u0432\u043e\u044e
+function.downloadosm=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 OSM-\u0434\u0430\u043d\u0456 \u043d\u0430 \u0442\u0435\u0440\u0438\u0442\u043e\u0440\u0456\u044e
+function.duplicatepoint=\u041a\u043e\u043f\u0456\u044e\u0432\u0430\u0442\u0438 \u0442\u043e\u0447\u043a\u0443 \u0432 \u043a\u0456\u043d\u0435\u0446\u044c \u0442\u0440\u0435\u043a\u0443
+function.setcolours=\u0412\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0438 \u043a\u043e\u043b\u044c\u043e\u0440\u0438
+function.setlinewidth=\u0417\u0430\u0434\u0430\u0442\u0438 \u0448\u0438\u0440\u0438\u043d\u0443 \u043b\u0456\u043d\u0456\u0457
+function.setlanguage=\u0412\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0438 \u043c\u043e\u0432\u0443
+function.connecttopoint=\u041f\u0440\u0438\u043a\u0440\u0456\u043f\u0438\u0442\u0438 \u0434\u043e \u0442\u043e\u0447\u043a\u0438
+function.disconnectfrompoint=\u0412\u0456\u0434\u043a\u0440\u0456\u043f\u0438\u0442\u0438 \u0432\u0456\u0434 \u0442\u043e\u0447\u043a\u0438
+function.removephoto=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443
+function.correlatephotos=\u0417\u0456\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0438 \u0437\u0430 \u0447\u0430\u0441\u043e\u043c
+function.rearrangephotos=\u0412\u043f\u043e\u0440\u044f\u0434\u043a\u0443\u0432\u0430\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0438 \u0437\u0430 \u0442\u0440\u0435\u043a\u043e\u043c
+function.rotatephotoleft=\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443 \u043d\u0430 90\u00b0 \u0432\u043b\u0456\u0432\u043e
+function.rotatephotoright=\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443 \u043d\u0430 90\u00b0 \u0432\u043f\u0440\u0430\u0432\u043e
+function.photopopup=\u0412\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443 \u0432 \u043e\u043a\u0440\u0435\u043c\u043e\u043c\u0443 \u0432\u0456\u043a\u043d\u0456
+function.ignoreexifthumb=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u043f\u043e\u0432\u043d\u0443 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443
+function.loadaudio=\u0414\u043e\u0434\u0430\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441
+function.removeaudio=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441
+function.correlateaudios=\u0417\u0456\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u0437\u0430 \u0447\u0430\u0441\u043e\u043c
+function.playaudio=\u041f\u0440\u043e\u0433\u0440\u0430\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441
+function.stopaudio=\u0417\u0443\u043f\u0438\u043d\u0438\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441
+function.help=\u0414\u043e\u043f\u043e\u043c\u043e\u0433\u0430
+function.showkeys=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u0433\u0430\u0440\u044f\u0447\u0456 \u043a\u043b\u0430\u0432\u0456\u0448\u0456
+function.about=\u041f\u0440\u043e GpsPrune
+function.checkversion=\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u0438\u0442\u0438 \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f
+function.saveconfig=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f
+function.diskcache=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u043c\u0430\u043f\u0438 \u043d\u0430 \u0434\u0438\u0441\u043a
+function.managetilecache=\u0423\u043f\u0440\u0430\u0432\u043b\u0456\u043d\u043d\u044f \u043a\u0435\u0448\u0435\u043c
+
+# Dialogs
+dialog.exit.confirm.title=\u0412\u0438\u0445\u0456\u0434
+dialog.exit.confirm.text=\u0414\u0430\u043d\u0456 \u043d\u0435 \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u0456! \u041f\u0440\u043e\u0434\u043e\u0432\u0436\u0438\u0442\u0438?
+dialog.openappend.title=\u0414\u043e\u0434\u0430\u0442\u0438 \u0430\u0431\u043e \u0432\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u0437\u0430\u043c\u0456\u0441\u0442\u044c \u0432\u0456\u0434\u043a\u0440\u0438\u0442\u0438\u0445.
+dialog.openappend.text=\u0414\u043e\u0434\u0430\u0442\u0438 \u0434\u043e \u043f\u043e\u0442\u043e\u0447\u043d\u0438\u0445 \u0434\u0430\u043d\u0438\u0445?
+dialog.deletepoint.title=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0443
+dialog.deletepoint.deletephoto=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443 \u0437 \u0446\u0456\u0454\u0457 \u0442\u043e\u0447\u043a\u0438?
+dialog.deletephoto.title=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443
+dialog.deletephoto.deletepoint=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0443 \u0437 \u0446\u0456\u0454\u0457 \u0447\u0432\u0456\u0442\u043b\u0438\u043d\u0438?
+dialog.deleteaudio.deletepoint=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0443 \u0437 \u0446\u044c\u043e\u0433\u043e \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0443?
+dialog.openoptions.title=\u0412\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u043e\u043f\u0446\u0456\u0457
+dialog.openoptions.filesnippet=\u0424\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u0444\u0430\u0439\u043b\u0443
+dialog.load.table.field=\u041f\u043e\u043b\u0435
+dialog.load.table.datatype=\u0422\u0438\u043f \u0434\u0430\u043d\u0438\u0445
+dialog.load.table.description=\u041e\u043f\u0438\u0441
+dialog.delimiter.label=\u0420\u043e\u0437\u0434\u0456\u043b\u044c\u043d\u0438\u043a \u043f\u043e\u043b\u0456\u0432
+dialog.delimiter.comma=\u041a\u043e\u043c\u0430 ,
+dialog.delimiter.tab=\u0422\u0430\u0431\u0443\u043b\u044f\u0446\u0456\u044f
+dialog.delimiter.space=\u041f\u0440\u043e\u0431\u0456\u043b
+dialog.delimiter.semicolon=\u041a\u0440\u0430\u043f\u043a\u0430 \u0437 \u043a\u043e\u043c\u043e\u044e ;
+dialog.delimiter.other=\u0406\u043d\u0448\u0435
+dialog.openoptions.deliminfo.records=\u0437\u0430\u043f\u0438\u0441, \u0437
+dialog.openoptions.deliminfo.fields=\u043f\u043e\u043b\u0435
+dialog.openoptions.deliminfo.norecords=\u041d\u0435\u043c\u0430\u0454 \u0437\u0430\u043f\u0438\u0441\u0456\u0432
+dialog.openoptions.altitudeunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u0432\u0438\u0441\u043e\u0442
+dialog.openoptions.speedunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u0448\u0432\u0438\u0434\u043a\u043e\u0441\u0442\u0456
+dialog.openoptions.vertspeedunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u0448\u0432\u0438\u0434\u043a\u043e\u0441\u0442\u0456 \u043f\u043e \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0456
+dialog.openoptions.vspeed.positiveup=\u041f\u043e\u0437\u0438\u0442\u0438\u0432\u043d\u0456 \u0448\u0432\u0438\u0434\u043a\u043e\u0441\u0442\u0456 \u0432\u0433\u043e\u0440\u0443
+dialog.openoptions.vspeed.positivedown=\u041f\u043e\u0437\u0438\u0442\u0438\u0432\u043d\u0456 \u0448\u0432\u0438\u0434\u043a\u043e\u0441\u0442\u0456 \u0432\u043d\u0438\u0437
+dialog.open.contentsdoubled=\u0426\u0435\u0439 \u0444\u0430\u0439\u043b \u043c\u0456\u0441\u0442\u0438\u0442\u044c \u0434\u0443\u0431\u043b\u044e\u0432\u0430\u043d\u043d\u044f \u0432 \u043a\u043e\u0436\u043d\u0456\u0439 \u0442\u043e\u0447\u0446\u0456, \n\u043e\u0434\u043d\u0430 \u044f\u043a \u0448\u043b\u044f\u0445\u043e\u0432\u0430 \u0442\u043e\u0447\u043a\u0430 \u0456 \u043e\u0434\u043d\u0430 \u044f\u043a \u0442\u0440\u0435\u043a\u043e\u0432\u0430 \u0442\u043e\u0447\u043a\u0430.
+dialog.selecttracks.intro=\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0442\u0440\u0435\u043a(-\u0438) \u0434\u043b\u044f \u0432\u0456\u0434\u043a\u0440\u0438\u0442\u0442\u044f
+dialog.selecttracks.noname=\u0411\u0435\u0437\u0456\u043c\u0435\u043d\u043d\u0438\u0439
+dialog.jpegload.subdirectories=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u043f\u0456\u0434\u0442\u0435\u043a\u0438
+dialog.jpegload.loadjpegswithoutcoords=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0438 \u0431\u0435\u0437 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442
+dialog.jpegload.loadjpegsoutsidearea=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0438 \u0437\u0430 \u043c\u0435\u0436\u0430\u043c\u0438 \u043f\u043e\u0442\u043e\u0447\u043d\u043e\u0457 \u043e\u0431\u043b\u0430\u0441\u0442\u0456
+dialog.jpegload.progress.title=\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0435\u043d\u043d\u044f \u0441\u0432\u0456\u0442\u043b\u0438\u043d
+dialog.jpegload.progress=\u0411\u0443\u0434\u044c-\u043b\u0430\u0441\u043a\u0430, \u0437\u0430\u0447\u0435\u043a\u0430\u0439\u0442\u0435, \u0439\u0434\u0435 \u043f\u043e\u0448\u0443\u043a \u0441\u0432\u0456\u0442\u043b\u0438\u043d
+dialog.gpsload.nogpsbabel=\u201egpsbabel\u201c \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u043e. \u041f\u0440\u043e\u0434\u043e\u0432\u0436\u0438\u0442\u0438?
+dialog.gpsload.device=\u0406\u043c\u2019\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e
+dialog.gpsload.format=\u0424\u043e\u0440\u043c\u0430\u0442
+dialog.gpsload.getwaypoints=\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0448\u043b\u044f\u0445\u043e\u0432\u0456 \u0442\u043e\u0447\u043a\u0438
+dialog.gpsload.gettracks=\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0442\u0440\u0435\u043a\u0438
+dialog.gpsload.save=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0434\u043e \u0444\u0430\u0439\u043b\u0443
+dialog.gpssend.sendwaypoints=\u0412\u0438\u0441\u043b\u0430\u0442\u0438 \u0448\u043b\u044f\u0445\u043e\u0432\u0456 \u0442\u043e\u0447\u043a\u0438
+dialog.gpssend.sendtracks=\u0412\u0438\u0441\u043b\u0430\u0442\u0438 \u0442\u0440\u0435\u043a\u0438
+dialog.gpssend.trackname=\u041d\u0430\u0437\u0432\u0430 \u0442\u0440\u0435\u043a\u0443
+dialog.gpsbabel.filters=\u0424\u0456\u043b\u044c\u0442\u0440\u0438
+dialog.addfilter.title=\u0414\u043e\u0434\u0430\u0442\u0438 \u0444\u0456\u043b\u044c\u0442\u0440
+dialog.gpsbabel.filter.discard=\u0412\u0456\u0434\u043a\u0438\u043d\u0443\u0442\u0438
+dialog.gpsbabel.filter.simplify=\u0421\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u0438
+dialog.gpsbabel.filter.distance=\u0412\u0456\u0434\u0441\u0442\u0430\u043d\u044c
+dialog.gpsbabel.filter.interpolate=\u0406\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044e\u0432\u0430\u0442\u0438
+dialog.gpsbabel.filter.discard.intro=\u0412\u0456\u0434\u043a\u0438\u043d\u0443\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u044f\u043a\u0449\u043e
+dialog.gpsbabel.filter.discard.hdop=\u0413\u043e\u0440\u041f\u043e\u0433\u0456\u0440\u0448\u0422\u043e\u0447\u043d >
+dialog.gpsbabel.filter.discard.vdop=\u0412\u0435\u0440\u0442\u041f\u043e\u0433\u0456\u0440\u0448\u0422\u043e\u0447\u043d >
+dialog.gpsbabel.filter.discard.numsats=\u0427\u0438\u0441\u043b\u043e \u0441\u0443\u043f\u0443\u0442\u043d\u0438\u043a\u0456\u0432 <
+dialog.gpsbabel.filter.discard.nofix=\u0422\u043e\u0447\u043a\u0430 \u043d\u0435 \u043c\u0430\u0454 \u0432\u0438\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044f
+dialog.gpsbabel.filter.discard.unknownfix=\u0422\u043e\u0447\u043a\u0430 \u043c\u0430\u0454 \u043d\u0435\u0432\u0456\u0434\u043e\u043c\u0435 \u0432\u0438\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044f
+dialog.gpsbabel.filter.simplify.intro=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u0434\u043e
+dialog.gpsbabel.filter.simplify.maxpoints=\u041a\u0456\u043b\u044c\u043a\u0456\u0441\u0442\u044c \u0442\u043e\u0447\u043e\u043a <
+dialog.gpsbabel.filter.simplify.maxerror=\u0430\u0431\u043e \u043f\u043e\u043c\u0438\u043b\u043a\u0430 \u0432\u0456\u0434\u0441\u0442\u0430\u043d\u0456 <
+dialog.gpsbabel.filter.simplify.crosstrack=\u043f\u0435\u0440\u0435\u0445\u0440\u0435\u0449\u0435\u043d\u043d\u044f \u0442\u0440\u0435\u043a\u0456\u0432
+dialog.gpsbabel.filter.simplify.length=\u0440\u0456\u0437\u043d\u0438\u0446\u044f \u0432 \u0434\u043e\u0432\u0436\u0438\u043d\u0456
+dialog.gpsbabel.filter.simplify.relative=\u0432\u0456\u0434\u043d\u043e\u0441\u043d\u043e \u0413\u043e\u0440\u041f\u043e\u0433\u0456\u0440\u0448\u0422\u043e\u0447\u043d
+dialog.gpsbabel.filter.distance.intro=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0438, \u044f\u043a\u0449\u043e \u0431\u043b\u0438\u0437\u044c\u043a\u043e \u0434\u043e \u0431\u0443\u0434\u044c-\u044f\u043a\u043e\u0457 \u043f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u044c\u043e\u0457 \u0442\u043e\u0447\u043a\u0438
+dialog.gpsbabel.filter.distance.distance=\u042f\u043a\u0449\u043e \u0432\u0456\u0434\u0441\u0442\u0430\u043d\u044c <
+dialog.gpsbabel.filter.distance.time=\u0456 \u0440\u0456\u0437\u043d\u0438\u0446\u044f \u0432 \u0447\u0430\u0441\u0456 <
+dialog.gpsbabel.filter.interpolate.intro=\u0414\u043e\u0434\u0430\u0442\u0438 \u0434\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u0456 \u0442\u043e\u0447\u043a\u0438 \u043c\u0456\u0436 \u0442\u043e\u0447\u043e\u043a \u043d\u0430 \u0442\u0440\u0435\u043a\u0443
+dialog.gpsbabel.filter.interpolate.distance=\u042f\u043a\u0449\u043e \u0432\u0456\u0434\u0441\u0442\u0430\u043d\u044c >
+dialog.gpsbabel.filter.interpolate.time=\u0430\u0431\u043e \u0440\u0456\u0437\u043d\u0438\u0446\u044f \u0432 \u0447\u0430\u0441\u0456 >
+dialog.saveoptions.title=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0444\u0430\u0439\u043b
+dialog.save.fieldstosave=\u041f\u043e\u043b\u044f \u0434\u043b\u044f \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u043d\u044f
+dialog.save.table.field=\u041f\u043e\u043b\u0435
+dialog.save.table.hasdata=\u041c\u0430\u0454 \u0434\u0430\u0442\u0443
+dialog.save.table.save=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438
+dialog.save.headerrow=\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0440\u044f\u0434\u043a\u0430
+dialog.save.coordinateunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442
+dialog.save.altitudeunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u0432\u0438\u0441\u043e\u0442\u0438
+dialog.save.timestampformat=\u0424\u043e\u0440\u043c\u0430\u0442 \u0447\u0430\u0441\u0443
+dialog.save.overwrite.title=\u0424\u0430\u0439\u043b \u0432\u0436\u0435 \u0456\u0441\u043d\u0443\u0454
+dialog.save.overwrite.text=\u0424\u0430\u0439\u043b \u0432\u0436\u0435 \u0456\u0441\u043d\u0443\u0454. \u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u0439\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u0442\u0438?
+dialog.save.notypesselected=\u041d\u0435 \u0432\u0438\u0431\u0440\u0430\u043d\u043e \u0442\u0438\u043f \u0442\u043e\u0447\u043e\u043a
+dialog.exportkml.text=\u043e\u043f\u0438\u0441 \u0434\u043e \u0434\u0430\u043d\u0438\u0445
+dialog.exportkml.altitude=\u0410\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u0456 \u0432\u0438\u0441\u043e\u0442\u0438 (\u0434\u043b\u044f \u0430\u0432\u0456\u0430\u0446\u0456\u0457)
+dialog.exportkml.kmz=\u0421\u0442\u0438\u0441\u043d\u0435\u043d\u043d\u044f \u0434\u043b\u044f kmz-\u0444\u0430\u0439\u043b\u0443
+dialog.exportkml.exportimages=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0435\u0441\u043a\u0456\u0437\u0443 \u0432 kmz
+dialog.exportkml.trackcolour=\u041a\u043e\u043b\u0456\u0440 \u0442\u0440\u0435\u043a\u0443
+
+# External urls
+url.googlemaps=maps.google.com.ua
+wikipedia.lang=uk
+openweathermap.lang=ua
+
+# Below here is still Russian
+#############################
+
diff --git a/tim/prune/lang/prune-texts_zh.properties b/tim/prune/lang/prune-texts_zh.properties
index 2a605bf..a7576e0 100644
--- a/tim/prune/lang/prune-texts_zh.properties
+++ b/tim/prune/lang/prune-texts_zh.properties
@@ -7,6 +7,7 @@ menu.file.addphotos=\u6dfb\u52a0\u7167\u7247
menu.file.recentfiles=\u6700\u8fd1\u6253\u5f00\u8fc7\u6587\u4ef6
menu.file.save=\u4fdd\u5b58
menu.file.exit=\u9000\u51fa
+menu.online=\u8054\u7f51
menu.track=\u8f68\u8ff9
menu.track.undo=\u64a4\u9500
menu.track.clearundo=\u6e05\u9664\u64a4\u9500\u6e05\u5355
@@ -57,6 +58,7 @@ menu.map.editmode=\u7f16\u8f91\u6a21\u5f0f
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=N
altkey.menu.track=T
altkey.menu.range=R
altkey.menu.point=P
@@ -104,9 +106,12 @@ function.estimatetime=\u4f30\u8ba1\u65f6\u95f4
function.learnestimationparams=\u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4
function.setmapbg=\u80cc\u666f\u5730\u56fe
function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84
+function.splitsegments=\u5206\u5272\u8f68\u8ff9
+function.sewsegments=\u63a5\u5408\u8f68\u8ff9\u7247\u6bb5
function.getgpsies=\u83b7\u53d6Gpsies\u8f68\u8ff9
function.uploadgpsies=\u4e0a\u4f20\u8f68\u8ff9\u5230Gpsies
function.lookupsrtm=\u4eceSRTM\u83b7\u5f97\u9ad8\u5ea6\u4fe1\u606f
+function.downloadsrtm=\u4e0b\u8f7dSRTM\u6570\u636e
function.getwikipedia=\u7ef4\u57fa\u767e\u79d1\u6709\u5173\u672c\u5730\u6587\u7ae0
function.searchwikipedianames=\u6309\u540d\u5b57\u4ece\u7ef4\u57fa\u767e\u79d1\u67e5\u627e
function.downloadosm=\u4e0b\u8f7d\u6b64\u5730OSM\u6570\u636e
@@ -135,6 +140,7 @@ function.checkversion=\u68c0\u67e5\u66f4\u65b0
function.saveconfig=\u4fdd\u5b58\u8bbe\u7f6e
function.diskcache=\u4fdd\u5b58\u5730\u56fe
function.managetilecache=\u7ba1\u7406\u5730\u56fe\u533a\u57df\u6570\u636e\u7f13\u5b58
+function.getweatherforecast=\u83b7\u53d6\u5929\u6c14\u9884\u62a5
# Dialogs
dialog.exit.confirm.title=\u9000\u51fa
@@ -241,7 +247,9 @@ dialog.exportpov.cameraz=\u76f8\u673aZ\u5750\u6807
dialog.exportpov.modelstyle=\u6a21\u578b\u7c7b\u578b
dialog.exportpov.ballsandsticks=\u7403\u6746\u6a21\u578b
dialog.exportpov.tubesandwalls=\u7ba1\u5899\u6a21\u578b
-dialog.3d.warningtracksize=\u8f68\u8ff9\u542b\u6709\u592a\u591a\u822a\u70b9\uff0cJAVA3D\u53ef\u80fd\u65e0\u6cd5\u663e\u793a\u3002\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.3d.warningtracksize=\u8f68\u8ff9\u542b\u6709\u592a\u591a\u822a\u70b9\uff0cJAVA3D\u53ef\u80fd\u65e0\u6cd5\u663e\u793a\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.3d.useterrain=\u663e\u793a\u5730\u5f62
+dialog.3d.terraingridsize=\u7f51\u683c\u5927\u5c0f
dialog.exportpov.baseimage=\u57fa\u7840\u56fe
dialog.exportpov.cannotmakebaseimage=\u65e0\u6cd5\u4fdd\u5b58\u57fa\u7840\u56fe
dialog.baseimage.title=\u8bbe\u7f6e\u57fa\u7840\u56fe
@@ -257,6 +265,7 @@ dialog.exportsvg.theta=\u4ef0\u89d2
dialog.exportsvg.gradients=\u4f7f\u7528\u6e10\u53d8\u8272
dialog.exportimage.noimagepossible=\u8f93\u51fa\u7684\u5730\u56fe\u56fe\u50cf\u9996\u5148\u9700\u8981\u7f13\u5b58\u5728\u672c\u5730\u78c1\u76d8\u4e0a
dialog.exportimage.drawtrack=\u7ed8\u51fa\u8f68\u8ff9
+dialog.exportimage.drawtrackpoints=\u7ed8\u51fa\u5404\u8f68\u8ff9\u70b9
dialog.exportimage.textscalepercent=\u6587\u5b57\u7f29\u653e\u6bd4\u4f8b (%)
dialog.pointtype.desc=\u4fdd\u5b58\u4e0b\u5217\u70b9\uff1a
dialog.pointtype.track=\u8f68\u8ff9\u70b9
@@ -265,9 +274,9 @@ dialog.pointtype.photo=\u7167\u7247\u70b9
dialog.pointtype.audio=\u5e26\u58f0\u97f3\u7684\u822a\u70b9
dialog.pointtype.selection=\u4ec5\u5df2\u9009\u62e9\u822a\u6bb5
dialog.confirmreversetrack.title=\u786e\u8ba4\u53cd\u8f6c
-dialog.confirmreversetrack.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u53cd\u8f6c\u540e\u53ef\u80fd\u4e22\u5931\u3002\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.confirmreversetrack.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u53cd\u8f6c\u540e\u53ef\u80fd\u4e22\u5931\n\u662f\u5426\u7ee7\u7eed\uff1f
dialog.confirmcutandmove.title=\u786e\u8ba4\u526a\u5207\u548c\u79fb\u52a8
-dialog.confirmcutandmove.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u79fb\u52a8\u540e\u53ef\u80fd\u4e22\u5931\u3002\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.confirmcutandmove.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u79fb\u52a8\u540e\u53ef\u80fd\u4e22\u5931\n\u662f\u5426\u7ee7\u7eed\uff1f
dialog.interpolate.parameter.text=\u6240\u9009\u4e24\u70b9\u4e2d\u63d2\u5165\u70b9\u7684\u4e2a\u6570
dialog.interpolate.betweenwaypoints=\u521b\u5efa\u4e2d\u7ee7\u822a\u70b9\uff1f
dialog.undo.title=\u64a4\u9500\u64cd\u4f5c
@@ -372,13 +381,12 @@ dialog.gpsies.activity.skating=\u6ed1\u51b0
dialog.wikipedia.column.name=\u6587\u7ae0\u9898\u76ee
dialog.wikipedia.column.distance=\u8ddd\u79bb
dialog.correlate.notimestamps=\u6570\u636e\u70b9\u4e2d\u65e0\u65f6\u95f4\u4fe1\u606f\uff0c\u7167\u7247\u65e0\u6cd5\u94fe\u63a5
-dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u7167\u7247\u5df2\u94fe\u63a5\u3002\u7ee7\u7eed\uff1f
-dialog.correlate.nouncorrelatedaudios=\u6240\u6709\u97f3\u9891\u5df2\u94fe\u63a5\u3002\u7ee7\u7eed\uff1f
+dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u7167\u7247\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f
+dialog.correlate.nouncorrelatedaudios=\u6240\u6709\u97f3\u9891\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f
dialog.correlate.photoselect.intro=\u9009\u62e9\u5df2\u94fe\u63a5\u7167\u7247\u4f5c\u4e3a\u65f6\u95f4\u504f\u79fb
dialog.correlate.select.photoname=\u7167\u7247\u540d
dialog.correlate.select.timediff=\u65f6\u95f4\u5dee
dialog.correlate.select.photolater=\u7167\u7247\u5ef6\u540e
-dialog.correlate.options.tip=\u63d0\u793a\uff1a\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u7167\u7247\uff0c\u53ef\u81ea\u52a8\u8ba1\u7b97\u65f6\u95f4\u504f\u79fb
dialog.correlate.options.intro=\u9009\u62e9\u81ea\u52a8\u94fe\u63a5\u8bbe\u7f6e
dialog.correlate.options.offsetpanel=\u65f6\u95f4\u504f\u79fb
dialog.correlate.options.offset=\u504f\u79fb
@@ -395,7 +403,7 @@ dialog.correlate.options.timelimit=\u65f6\u95f4\u9650\u5236
dialog.correlate.options.nodistancelimit=\u65e0\u8ddd\u79bb\u9650\u5236
dialog.correlate.options.distancelimit=\u8ddd\u79bb\u9650\u5236
dialog.correlate.options.correlate=\u5173\u8054
-dialog.correlate.alloutsiderange=\u65e0\u6cd5\u94fe\u63a5\uff0c\u6240\u6709\u7167\u7247\u8d85\u51fa\u8f68\u8ff9\u65f6\u95f4\u8303\u56f4\u3002\n\u8bf7\u6539\u53d8\u65f6\u95f4\u504f\u79fb\u6216\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u5728\u7167\u7247\u3002
+dialog.correlate.alloutsiderange=\u65e0\u6cd5\u94fe\u63a5\uff0c\u6240\u6709\u7167\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\u5728\u7167\u7247
dialog.correlate.filetimes=\u6587\u4ef6\u65f6\u95f4\u8868\u793a\u58f0\u97f3\u7684\uff1a
dialog.correlate.filetimes2=\u90e8\u5206
dialog.correlate.correltimes=\u5982\u8981\u5173\u8054\uff0c\u8bf7\u4f7f\u7528\uff1a
@@ -421,8 +429,7 @@ dialog.compress.duplicates.title=\u91cd\u590d\u70b9\u5220\u9664
dialog.compress.douglaspeucker.title=Douglas-Peucker \u538b\u7f29
dialog.compress.douglaspeucker.paramdesc=\u95f4\u8ddd\u7cfb\u6570
dialog.compress.summarylabel=\u8981\u5220\u9664\u7684\u70b9
-dialog.compress.confirm1=\u5df2\u6807\u8bb0
-dialog.compress.confirm2=\u70b9\u3002\n\u70b9\u51fb \u8f68\u8ff9->\u5220\u9664 \u5220\u9664\u8fd9\u4e9b\u70b9
+dialog.compress.confirm=\u5df2\u6807\u8bb0 %d \u70b9\n\u70b9\u51fb \u8f68\u8ff9->\u5220\u9664 \u5220\u9664\u8fd9\u4e9b\u70b9
dialog.compress.confirmnone=\u672a\u6807\u8bb0\u4efb\u4f55\u70b9
dialog.deletemarked.nonefound=\u65e0\u6cd5\u5220\u9664\u6570\u636e\u70b9
dialog.pastecoordinates.desc=\u5728\u6b64\u8f93\u5165\u6216\u7c98\u8d34\u5750\u6807\u70b9
@@ -530,13 +537,31 @@ dialog.diskcache.tileset.multiple=\u6570\u76ee
dialog.diskcache.deleteold=\u5220\u9664\u65e7\u5730\u56fe\u5757
dialog.diskcache.maximumage=\u6700\u957f\u65f6\u95f4(\u5929)
dialog.diskcache.deleteall=\u5220\u9664\u6240\u6709\u5730\u56fe\u5757
-dialog.diskcache.deleted1=\u5df2\u5220\u9664
-dialog.diskcache.deleted2=\u7f13\u5b58\u5185\u6587\u4ef6
+dialog.diskcache.deleted=\u5df2\u5220\u9664 %d \u7f13\u5b58\u5185\u6587\u4ef6
dialog.deletefieldvalues.intro=\u9009\u62e9\u5f53\u524d\u8303\u56f4\u5185\u8981\u5220\u9664\u7684\u5b57\u6bb5
dialog.deletefieldvalues.nofields=\u9009\u5b9a\u8303\u56f4\u5185\u6ca1\u6709\u8981\u5220\u9664\u7684\u5b57\u6bb5
dialog.setlinewidth.text=\u8f93\u5165\u8f68\u8ff9\u7ebf\u5bbd\u50cf\u7d20\u503c(1-4)
dialog.downloadosm.desc=\u786e\u8ba4\u4eceOSM\u4e0b\u8f7d\u8be5\u5730\u533a\u539f\u59cb\u6570\u636e:
dialog.searchwikipedianames.search=\u67e5\u627e:
+dialog.weather.location=\u5730\u70b9
+dialog.weather.update=\u5929\u6c14\u9884\u62a5\u66f4\u65b0
+dialog.weather.sunrise=\u65e5\u51fa
+dialog.weather.sunset=\u65e5\u843d
+dialog.weather.temperatureunits=\u6e29\u5ea6
+dialog.weather.currentforecast=\u5929\u6c14\u73b0\u72b6
+dialog.weather.dailyforecast=\u9010\u65e5\u9884\u62a5
+dialog.weather.3hourlyforecast=\u4e09\u5c0f\u65f6\u9884\u62a5
+dialog.weather.day.now=\u5929\u6c14\u73b0\u72b6
+dialog.weather.day.today=\u4eca\u65e5
+dialog.weather.day.tomorrow=\u660e\u65e5
+dialog.weather.day.monday=\u5468\u4e00
+dialog.weather.day.tuesday=\u5468\u4e8c
+dialog.weather.day.wednesday=\u5468\u4e09
+dialog.weather.day.thursday=\u5468\u56db
+dialog.weather.day.friday=\u5468\u4e94
+dialog.weather.day.saturday=\u5468\u516d
+dialog.weather.day.sunday=\u5468\u65e5
+dialog.weather.creditnotice=\u5929\u6c14\u4fe1\u606f\u83b7\u53d6\u81eaopenweathermap.org\uff0c\u83b7\u53d6\u66f4\u591a\u5929\u6c14\u8be6\u60c5\u8bf7\u8bbf\u95ee\u7f51\u7ad9\u3002
# 3d window
dialog.3d.title=GpsPrune 3D \u663e\u793a
@@ -555,11 +580,12 @@ 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=\u7167\u7247\u5df2\u91cd\u6392
+confirm.splitsegments=\u8f68\u8ff9\u5df2\u5206\u5272\u4e3a %d \u6bb5
+confirm.sewsegments=%d \u8f68\u8ff9\u6bb5\u5df2\u5408\u5e76
confirm.cutandmove=\u5df2\u79fb\u52a8\u7684\u8f68\u8ff9\u6bb5
confirm.interpolate=\u8f68\u8ff9\u70b9\u5df2\u6dfb\u52a0
confirm.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u5df2\u8f6c\u6362
-confirm.saveexif.ok1=\u5df2\u4fdd\u5b58
-confirm.saveexif.ok2=\u7167\u7247\u6587\u4ef6
+confirm.saveexif.ok=\u5df2\u4fdd\u5b58 %d \u7167\u7247\u6587\u4ef6
confirm.undo.single=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c
confirm.undo.multi=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c
confirm.jpegload.single=\u5df2\u52a0\u5165\u7167\u7247
@@ -573,13 +599,23 @@ confirm.correlatephotos.multi=\u7167\u7247\u5df2\u94fe\u63a5
confirm.createpoint=\u5df2\u521b\u5efa\u70b9
confirm.rotatephoto=\u7167\u7247\u5df2\u65cb\u8f6c
confirm.running=\u8bf7\u7a0d\u7b49...
-confirm.lookupsrtm1=\u627e\u5230
-confirm.lookupsrtm2=\u9ad8\u5ea6\u503c
+confirm.lookupsrtm=\u627e\u5230 %d \u9ad8\u5ea6\u503c
+confirm.downloadsrtm=\u4e0b\u8f7d %d \u9ad8\u5ea6\u6587\u4ef6\u5230\u7f13\u5b58\u4e2d
+confirm.downloadsrtm.1=\u4e0b\u8f7d %d \u9ad8\u5ea6\u6587\u4ef6\u5230\u7f13\u5b58\u4e2d
+confirm.downloadsrtm.none=\u65e0\u9700\u4e0b\u8f7d\uff0c\u6587\u4ef6\u5df2\u5b58\u50a8\u5728\u7f13\u5b58\u4e2d
confirm.deletefieldvalues=\u533a\u57df\u6570\u636e\u5df2\u5220\u9664
confirm.audioload=\u5df2\u6dfb\u52a0\u58f0\u97f3\u6587\u4ef6
confirm.correlateaudios.single=\u58f0\u97f3\u5df2\u5173\u8054
confirm.correlateaudios.multi=\u58f0\u97f3\u5df2\u5173\u8054
+# Tips, shown just once when appropriate
+tip.title=\u63d0\u793a
+tip.useamapcache=\u542f\u7528\u78c1\u76d8\u7f13\u5b58 (\u8bbe\u7f6e -> \u4fdd\u5b58\u5730\u56fe)\n\u53ef\u4ee5\u63d0\u9ad8\u663e\u793a\u901f\u5ea6\uff0c\u51cf\u5c11\u7f51\u7edc\u6d41\u91cf
+tip.learntimeparams=\u5bf9\u8bb0\u5f55\u7684\u8f68\u8ff9\u542f\u7528 \u8f68\u8ff9 -> \u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4\n\u5c06\u4f7f\u7ed3\u679c\u66f4\u52a0\u7cbe\u786e
+tip.downloadsrtm=\u53ef\u4ee5\u70b9\u51fb \u8054\u7f51 -> \u4e0b\u8f7dSRTM\u6570\u636e\n\u5c06\u6570\u636e\u4fdd\u5b58\u5230\u78c1\u76d8\u7f13\u5b58\u4ee5\u63d0\u9ad8\u901f\u5ea6
+tip.usesrtmfor3d=\u6b64\u8f68\u8ff9\u6ca1\u6709\u9ad8\u5ea6\u4fe1\u606f\n\u53ef\u4ee5\u901a\u8fc7SRTM\u83b7\u53d6\u5927\u81f4\u9ad8\u5ea6\u4ee5\u663e\u793a3D\u89c6\u56fe
+tip.manuallycorrelateone=\u63d0\u793a\uff1a\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u7167\u7247\uff0c\u53ef\u81ea\u52a8\u8ba1\u7b97\u65f6\u95f4\u504f\u79fb
+
# Buttons
button.ok=\u786e\u5b9a
button.back=\u8fd4\u56de
@@ -597,6 +633,7 @@ button.yes=\u662f
button.no=\u5426
button.yestoall=\u5168\u90e8\u662f
button.notoall=\u5168\u90e8\u5426
+button.always=\u603b\u662f
button.select=\u9009\u62e9
button.selectall=\u5168\u9009
button.selectnone=\u5168\u4e0d\u9009
@@ -685,7 +722,6 @@ fieldname.newsegment=\u6bb5
fieldname.custom=\u7528\u6237
fieldname.prefix=\u6570\u636e\u6bb5
fieldname.distance=\u8ddd\u79bb
-fieldname.movingdistance=\u79fb\u52a8\u8ddd\u79bb
fieldname.duration=\u65f6\u957f
fieldname.speed=\u901f\u5ea6
fieldname.verticalspeed=\u5782\u76f4\u901f\u5ea6
@@ -720,6 +756,10 @@ units.degminsec=\u5ea6-\u5206-\u79d2
units.degmin=\u5ea6-\u5206
units.deg=\u5ea6
units.iso8601=ISO 8601
+units.degreescelsius=\u6444\u6c0f\u5ea6
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=\u534e\u6c0f\u5ea6
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=\u4e0e
@@ -728,6 +768,7 @@ logic.or=\u6216
# External urls
url.googlemaps=ditu.google.cn
wikipedia.lang=zh
+openweathermap.lang=zh_cn
# Cardinals for 3d plots
cardinal.n=N
@@ -749,6 +790,8 @@ undo.deletemarked=\u538b\u7f29\u8f68\u8ff9
undo.insert=\u63d2\u5165\u822a\u70b9
undo.reverse=\u53cd\u8f6c\u6bb5
undo.mergetracksegments=\u5408\u5e76\u6bb5
+undo.splitsegments=\u5206\u5272\u8f68\u8ff9
+undo.sewsegments=\u5408\u5e76\u8f68\u8ff9\u7247\u6bb5
undo.addtimeoffset=\u6dfb\u52a0\u65f6\u95f4\u504f\u79fb
undo.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb
undo.rearrangewaypoints=\u91cd\u65b0\u914d\u7f6e\u822a\u70b9
@@ -770,11 +813,9 @@ error.save.nodata=\u65e0\u6570\u636e\u4fdd\u5b58
error.save.failed=\u5411\u6587\u4ef6\u4fdd\u5b58\u6570\u636e\u5931\u8d25
error.saveexif.filenotfound=\u627e\u4e0d\u5230\u7167\u7247\u6587\u4ef6
error.saveexif.cannotoverwrite1=\u7167\u7247
-error.saveexif.cannotoverwrite2=\u662f\u53ea\u8bfb\u6587\u4ef6\u3002\u4fdd\u5b58\u526f\u672c\uff1f
-error.saveexif.failed1=\u65e0\u6cd5\u4fdd\u5b58
-error.saveexif.failed2=\u5f20\u7167\u7247
-error.saveexif.forced1=
-error.saveexif.forced2=\u5f20\u7167\u7247\u9700\u8981\u5f3a\u5236\u6267\u884c
+error.saveexif.cannotoverwrite2=\u662f\u53ea\u8bfb\u6587\u4ef6\uff0c\u4fdd\u5b58\u526f\u672c\uff1f
+error.saveexif.failed=\u65e0\u6cd5\u4fdd\u5b58 %d \u5f20\u7167\u7247
+error.saveexif.forced=%d \u5f20\u7167\u7247\u9700\u8981\u5f3a\u5236\u6267\u884c
error.load.dialogtitle=\u5bfc\u5165\u6570\u636e\u9519\u8bef
error.load.noread=\u65e0\u6cd5\u8bfb\u6587\u4ef6
error.load.nopoints=\u6587\u4ef6\u4e2d\u65e0\u5750\u6807\u4fe1\u606f
@@ -785,7 +826,7 @@ error.jpegload.dialogtitle=\u5bfc\u5165\u7167\u7247\u9519\u8bef
error.jpegload.nofilesfound=\u627e\u4e0d\u5230\u6587\u4ef6
error.jpegload.nojpegsfound=\u627e\u4e0d\u5230Jpeg\u6587\u4ef6
error.jpegload.nogpsfound=\u627e\u4e0d\u5230GPS\u4fe1\u606f
-error.jpegload.exifreadfailed=Exif\u8bfb\u53d6\u9519\u8bef\u3002\u9700\u8981\u5185\u90e8\u6216\u8005\u5916\u90e8\u5e93\u624d\u80fd\u8bfb\u53d6
+error.jpegload.exifreadfailed=Exif\u8bfb\u53d6\u9519\u8bef\n\u9700\u8981\u5185\u90e8\u6216\u8005\u5916\u90e8\u5e93\u624d\u80fd\u8bfb\u53d6
error.audioload.nofilesfound=\u672a\u627e\u5230\u58f0\u97f3\u6587\u4ef6
error.gpsload.unknown=\u672a\u77e5\u9519\u8bef
error.undofailed.title=\u64a4\u9500\u5931\u8d25
@@ -810,4 +851,7 @@ error.cache.notthere=\u672a\u627e\u5230\u533a\u57df\u6570\u636e\u7f13\u5b58\u658
error.cache.empty=\u533a\u57df\u6570\u636e\u6587\u4ef6\u5939\u7a7a
error.cache.cannotdelete=\u65e0\u53ef\u5220\u9664\u533a\u57df\u6570\u636e
error.interpolate.invalidparameter=\u8f93\u5165\u70b9\u6570\u91cf\u5fc5\u987b\u57281\u52301000\u4e4b\u95f4
-error.learnestimationparams.failed=\u65e0\u6cd5\u4ece\u6b64\u8f68\u8ff9\u5f97\u5230\u53c2\u6570\u3002 \n \u5c1d\u8bd5\u8f7d\u5165\u66f4\u591a\u8f68\u8ff9\u3002
+error.learnestimationparams.failed=\u65e0\u6cd5\u4ece\u6b64\u8f68\u8ff9\u5f97\u5230\u53c2\u6570\n \u5c1d\u8bd5\u8f7d\u5165\u66f4\u591a\u8f68\u8ff9
+error.tracksplit.nosplit=\u6b64\u8f68\u8ff9\u65e0\u6cd5\u5206\u5272
+error.downloadsrtm.nocache=\u6587\u4ef6\u65e0\u6cd5\u4fdd\u5b58\n\u8bf7\u68c0\u67e5\u78c1\u76d8\u7f13\u5b58
+error.sewsegments.nothingdone=\u8f68\u8ff9\u7247\u6bb5\u65e0\u6cd5\u5408\u5e76\n\u8be5\u8f68\u8ff9\u73b0\u5305\u542b %d \u4e2a\u7247\u6bb5
diff --git a/tim/prune/load/BabelFileFormats.java b/tim/prune/load/BabelFileFormats.java
index 2d1c9e8..39359c1 100644
--- a/tim/prune/load/BabelFileFormats.java
+++ b/tim/prune/load/BabelFileFormats.java
@@ -11,7 +11,7 @@ public abstract class BabelFileFormats
/**
* @return an object array for the format descriptions
*/
- public static Object[] getDescriptions() {
+ public static String[] getDescriptions() {
return getColumn(0);
}
@@ -26,8 +26,10 @@ public abstract class BabelFileFormats
{
final String[] suffixes = getColumn(2);
for (int i=0; i<suffixes.length; i++)
+ {
if (suffixes[i] != null && suffixes[i].equalsIgnoreCase(inSuffix))
return i;
+ }
}
return -1;
}
diff --git a/tim/prune/load/BabelLoadFromFile.java b/tim/prune/load/BabelLoadFromFile.java
index 1897e0c..23ce909 100644
--- a/tim/prune/load/BabelLoadFromFile.java
+++ b/tim/prune/load/BabelLoadFromFile.java
@@ -41,7 +41,7 @@ public class BabelLoadFromFile extends BabelLoader
// Label for filename
private JLabel _inputFileLabel = null;
// Dropdown for format of file
- private JComboBox _formatDropdown = null;
+ private JComboBox<String> _formatDropdown = null;
// Last used file suffix
private String _lastSuffix = null;
@@ -133,7 +133,7 @@ public class BabelLoadFromFile extends BabelLoader
grid.add(_inputFileLabel);
JLabel formatLabel = new JLabel(I18nManager.getText("dialog.gpsload.format"));
grid.add(formatLabel);
- _formatDropdown = new JComboBox(BabelFileFormats.getDescriptions());
+ _formatDropdown = new JComboBox<String>(BabelFileFormats.getDescriptions());
grid.add(_formatDropdown);
gridPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 20));
@@ -201,19 +201,33 @@ public class BabelLoadFromFile extends BabelLoader
}
/**
+ * @return the suffix of the selected filename
+ */
+ private String getSelectedSuffix()
+ {
+ String filename = _inputFile.getName();
+ if (filename == null) {return "";}
+ int dotPos = filename.lastIndexOf('.');
+ return (dotPos > 0 ? filename.substring(dotPos) : "");
+ }
+
+ /**
* Initialise dialog
*/
protected void initDialog()
{
_inputFileLabel.setText(_inputFile.getName());
// Get suffix of filename and compare with previous one
- String filename = _inputFile.getName();
- int dotPos = filename.lastIndexOf('.');
- String suffix = (dotPos > 0 ? filename.substring(dotPos) : null);
- if (suffix != null && !suffix.equals(".") && (_lastSuffix == null || !suffix.equalsIgnoreCase(_lastSuffix)))
+ String suffix = getSelectedSuffix();
+ if (_lastSuffix == null || !suffix.equalsIgnoreCase(_lastSuffix))
{
- // New suffix chosen, so select first appropriate format (if any)
+ // New suffix has been chosen, so select first appropriate format (if any)
int selIndex = BabelFileFormats.getIndexForFileSuffix(suffix);
+ if (selIndex < 0)
+ {
+ // Use the previous one from the Config (if any)
+ selIndex = Config.getConfigInt(Config.KEY_IMPORT_FILE_FORMAT);
+ }
if (selIndex >= 0) {
_formatDropdown.setSelectedIndex(selIndex);
}
@@ -226,10 +240,15 @@ public class BabelLoadFromFile extends BabelLoader
*/
protected void saveConfigValues()
{
- // Save the filter string (but don't remove it if it's now blank)
+ // Save the filter string, clear it if it's now blank
final String filter = _filterPanel.getFilterString();
- if (filter != null && !filter.equals("")) {
- Config.setConfigString(Config.KEY_GPSBABEL_FILTER, filter);
+ Config.setConfigString(Config.KEY_GPSBABEL_FILTER, filter);
+
+ // Check if there is a standard file type for the selected suffix
+ int selIndex = BabelFileFormats.getIndexForFileSuffix(getSelectedSuffix());
+ // If there is none, then get the index which the user chose and set in the Config
+ if (selIndex < 0) {
+ Config.setConfigInt(Config.KEY_IMPORT_FILE_FORMAT, _formatDropdown.getSelectedIndex());
}
}
}
diff --git a/tim/prune/load/FileCacher.java b/tim/prune/load/FileCacher.java
index da354aa..a151c2e 100644
--- a/tim/prune/load/FileCacher.java
+++ b/tim/prune/load/FileCacher.java
@@ -47,7 +47,9 @@ public class FileCacher
}
while (currLine != null)
{
- if (currLine.indexOf('\0') >= 0) {
+ if (currLine.indexOf('\0') >= 0)
+ {
+ reader.close();
return; // it's a binary file, shouldn't use this cacher
}
if (currLine.trim().length() > 0)
diff --git a/tim/prune/load/MediaLoadProgressDialog.java b/tim/prune/load/MediaLoadProgressDialog.java
index a052b55..9372704 100644
--- a/tim/prune/load/MediaLoadProgressDialog.java
+++ b/tim/prune/load/MediaLoadProgressDialog.java
@@ -1,32 +1,17 @@
package tim.prune.load;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JDialog;
import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JProgressBar;
-import tim.prune.I18nManager;
import tim.prune.function.Cancellable;
+import tim.prune.gui.GenericProgressDialog;
/**
* Class to show a progress dialog for loading media.
- * Used for regular photo / audio loads plus the async
- * loading function.
+ * Used for regular photo / audio loads plus the async loading function.
+ * Maybe this class isn't really needed...
*/
-public class MediaLoadProgressDialog
+public class MediaLoadProgressDialog extends GenericProgressDialog
{
- private JDialog _progressDialog = null;
- private JProgressBar _progressBar = null;
- private JFrame _parentFrame = null;
- private Cancellable _function = null;
-
/**
* Constructor
* @param inParentFrame parent frame for creating dialog
@@ -34,74 +19,6 @@ public class MediaLoadProgressDialog
*/
public MediaLoadProgressDialog(JFrame inParentFrame, Cancellable inFunction)
{
- _parentFrame = inParentFrame;
- _function = inFunction;
- }
-
- /**
- * Create the dialog to show the progress
- */
- private void createProgressDialog()
- {
- _progressDialog = new JDialog(_parentFrame, I18nManager.getText("dialog.jpegload.progress.title"));
- _progressDialog.setLocationRelativeTo(_parentFrame);
- _progressBar = new JProgressBar(0, 100);
- _progressBar.setValue(0);
- _progressBar.setStringPainted(true);
- _progressBar.setString("");
- JPanel panel = new JPanel();
- panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
- panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
- panel.add(new JLabel(I18nManager.getText("dialog.jpegload.progress")));
- panel.add(_progressBar);
- JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
- cancelButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _function.cancel();
- }
- });
- panel.add(cancelButton);
- _progressDialog.getContentPane().add(panel);
- _progressDialog.pack();
- _progressDialog.setVisible(true);
- }
-
- /**
- * Show the dialog in indeterminate mode, before limits are calculated
- */
- public void show()
- {
- if (_progressDialog == null)
- {
- createProgressDialog();
- _progressBar.setIndeterminate(true);
- }
- }
-
- /**
- * Update the progress bar
- * @param inCurrent current value
- * @param inMax maximum value
- */
- public void showProgress(int inCurrent, int inMax)
- {
- if (_progressDialog == null)
- createProgressDialog();
- if (_progressBar.isIndeterminate())
- _progressBar.setIndeterminate(false);
- if (inMax > 0)
- _progressBar.setMaximum(inMax);
- _progressBar.setValue(inCurrent);
- _progressBar.setString("" + inCurrent + " / " + _progressBar.getMaximum());
- }
-
- /**
- * Close the dialog
- */
- public void close()
- {
- if (_progressDialog != null)
- _progressDialog.dispose();
+ super("dialog.jpegload.progress.title", "dialog.jpegload.progress", inParentFrame, inFunction);
}
}
diff --git a/tim/prune/load/TextFileLoader.java b/tim/prune/load/TextFileLoader.java
index 53d6d2a..8fd30b2 100644
--- a/tim/prune/load/TextFileLoader.java
+++ b/tim/prune/load/TextFileLoader.java
@@ -49,13 +49,13 @@ public class TextFileLoader
private JLabel _statusLabel = null;
private DelimiterInfo[] _delimiterInfos = null;
private FileCacher _fileCacher = null;
- private JList _snippetBox = null;
+ private JList<String> _snippetBox = null;
private FileExtractTableModel _fileExtractTableModel = null;
private JTable _fieldTable;
private FieldSelectionTableModel _fieldTableModel = null;
- private JComboBox _altitudeUnitsDropdown = null;
- private JComboBox _hSpeedUnitsDropdown = null;
- private JComboBox _vSpeedUnitsDropdown = null;
+ private JComboBox<String> _altitudeUnitsDropdown = null;
+ private JComboBox<String> _hSpeedUnitsDropdown = null;
+ private JComboBox<String> _vSpeedUnitsDropdown = null;
private JRadioButton _vSpeedUpwardsRadio = null;
private ComponentHider _componentHider = null;
private int _selectedField = -1;
@@ -326,7 +326,7 @@ public class TextFileLoader
delimsPanel.add(_statusLabel);
firstCard.add(delimsPanel, BorderLayout.SOUTH);
// load snippet to show first few lines
- _snippetBox = new JList(_fileCacher.getSnippet(SNIPPET_SIZE, MAX_SNIPPET_WIDTH));
+ _snippetBox = new JList<String>(_fileCacher.getSnippet(SNIPPET_SIZE, MAX_SNIPPET_WIDTH));
_snippetBox.setEnabled(false);
firstCard.add(makeLabelledPanel("dialog.openoptions.filesnippet", _snippetBox), BorderLayout.CENTER);
@@ -414,7 +414,7 @@ public class TextFileLoader
JLabel altLabel = new JLabel(I18nManager.getText("dialog.openoptions.altitudeunits") + ": ");
altGrid.add(altLabel);
String[] altUnits = {I18nManager.getText("units.metres"), I18nManager.getText("units.feet")};
- _altitudeUnitsDropdown = new JComboBox(altUnits);
+ _altitudeUnitsDropdown = new JComboBox<String>(altUnits);
altGrid.add(_altitudeUnitsDropdown);
holderPanel.add(altUnitsPanel);
// Horizontal speed
@@ -423,7 +423,7 @@ public class TextFileLoader
speedPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("fieldname.speed")));
JLabel speedLabel = new JLabel(I18nManager.getText("dialog.openoptions.speedunits") + ": ");
speedGrid.add(speedLabel);
- _hSpeedUnitsDropdown = new JComboBox();
+ _hSpeedUnitsDropdown = new JComboBox<String>();
for (Unit spUnit : UnitSetLibrary.ALL_SPEED_UNITS) {
_hSpeedUnitsDropdown.addItem(I18nManager.getText(spUnit.getNameKey()));
}
@@ -435,7 +435,7 @@ public class TextFileLoader
vSpeedPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("fieldname.verticalspeed")));
JLabel vSpeedLabel = new JLabel(I18nManager.getText("dialog.openoptions.vertspeedunits") + ": ");
vSpeedGrid.add(vSpeedLabel);
- _vSpeedUnitsDropdown = new JComboBox();
+ _vSpeedUnitsDropdown = new JComboBox<String>();
for (Unit spUnit : UnitSetLibrary.ALL_SPEED_UNITS) {
_vSpeedUnitsDropdown.addItem(I18nManager.getText(spUnit.getNameKey()));
}
@@ -587,7 +587,7 @@ public class TextFileLoader
_fieldTableModel.updateData(startFieldArray);
_fieldTable.setModel(_fieldTableModel);
// add dropdowns to second column
- JComboBox fieldTypesBox = new JComboBox();
+ JComboBox<String> fieldTypesBox = new JComboBox<String>();
String[] fieldNames = Field.getFieldNames();
for (int i=0; i<fieldNames.length; i++)
{
diff --git a/tim/prune/load/babel/DiscardFilter.java b/tim/prune/load/babel/DiscardFilter.java
index f310031..22f36f7 100644
--- a/tim/prune/load/babel/DiscardFilter.java
+++ b/tim/prune/load/babel/DiscardFilter.java
@@ -31,7 +31,7 @@ public class DiscardFilter extends FilterDefinition
private WholeNumberField _hdopField = null;
private WholeNumberField _vdopField = null;
- private JComboBox _combineDopsCombo = null;
+ private JComboBox<String> _combineDopsCombo = null;
private WholeNumberField _numSatsField = null;
private JCheckBox _noFixCheckbox = null;
private JCheckBox _unknownFixCheckbox = null;
@@ -65,7 +65,7 @@ public class DiscardFilter extends FilterDefinition
_hdopField = new WholeNumberField(2);
_hdopField.addKeyListener(_paramChangeListener);
dopPanel.add(_hdopField);
- _combineDopsCombo = new JComboBox(new String[] {I18nManager.getText("logic.and"), I18nManager.getText("logic.or")});
+ _combineDopsCombo = new JComboBox<String>(new String[] {I18nManager.getText("logic.and"), I18nManager.getText("logic.or")});
dopPanel.add(_combineDopsCombo);
dopPanel.add(new JLabel(I18nManager.getText("dialog.gpsbabel.filter.discard.vdop"), SwingConstants.RIGHT));
_vdopField = new WholeNumberField(2);
diff --git a/tim/prune/load/babel/DistanceFilter.java b/tim/prune/load/babel/DistanceFilter.java
index 7554160..4826bc8 100644
--- a/tim/prune/load/babel/DistanceFilter.java
+++ b/tim/prune/load/babel/DistanceFilter.java
@@ -27,7 +27,7 @@ public class DistanceFilter extends FilterDefinition
}
private DecimalNumberField _distField = null;
- private JComboBox _distUnitsCombo = null;
+ private JComboBox<String> _distUnitsCombo = null;
private WholeNumberField _secondsField = null;
@@ -54,7 +54,7 @@ public class DistanceFilter extends FilterDefinition
_distField = new DecimalNumberField();
_distField.addKeyListener(_paramChangeListener);
gridPanel.add(_distField);
- _distUnitsCombo = new JComboBox(new String[] {I18nManager.getText("units.metres"), I18nManager.getText("units.feet")});
+ _distUnitsCombo = new JComboBox<String>(new String[] {I18nManager.getText("units.metres"), I18nManager.getText("units.feet")});
gridPanel.add(_distUnitsCombo);
gridPanel.add(new JLabel(I18nManager.getText("dialog.gpsbabel.filter.distance.time")));
_secondsField = new WholeNumberField(4);
diff --git a/tim/prune/load/babel/InterpolateFilter.java b/tim/prune/load/babel/InterpolateFilter.java
index a169846..9670fb2 100644
--- a/tim/prune/load/babel/InterpolateFilter.java
+++ b/tim/prune/load/babel/InterpolateFilter.java
@@ -28,7 +28,7 @@ public class InterpolateFilter extends FilterDefinition
}
private DecimalNumberField _distField = null;
- private JComboBox _distUnitsCombo = null;
+ private JComboBox<String> _distUnitsCombo = null;
private WholeNumberField _secondsField = null;
@@ -55,7 +55,7 @@ public class InterpolateFilter extends FilterDefinition
_distField = new DecimalNumberField();
_distField.addKeyListener(_paramChangeListener);
gridPanel.add(_distField);
- _distUnitsCombo = new JComboBox(new String[] {I18nManager.getText("units.kilometres"), I18nManager.getText("units.miles")});
+ _distUnitsCombo = new JComboBox<String>(new String[] {I18nManager.getText("units.kilometres"), I18nManager.getText("units.miles")});
gridPanel.add(_distUnitsCombo);
gridPanel.add(new JLabel(I18nManager.getText("dialog.gpsbabel.filter.interpolate.time")));
_secondsField = new WholeNumberField(4);
diff --git a/tim/prune/load/babel/SimplifyFilter.java b/tim/prune/load/babel/SimplifyFilter.java
index ebbc9f4..1567c88 100644
--- a/tim/prune/load/babel/SimplifyFilter.java
+++ b/tim/prune/load/babel/SimplifyFilter.java
@@ -31,7 +31,7 @@ public class SimplifyFilter extends FilterDefinition
private WholeNumberField _maxPointsField = null;
private DecimalNumberField _distField = null;
- private JComboBox _distUnitsCombo = null;
+ private JComboBox<String> _distUnitsCombo = null;
private JRadioButton _crossTrackRadio = null;
private JRadioButton _lengthRadio = null;
private JRadioButton _relativeRadio = null;
@@ -65,7 +65,7 @@ public class SimplifyFilter extends FilterDefinition
_distField = new DecimalNumberField();
_distField.addKeyListener(_paramChangeListener);
gridPanel.add(_distField);
- _distUnitsCombo = new JComboBox(new String[] {
+ _distUnitsCombo = new JComboBox<String>(new String[] {
I18nManager.getText(UnitSetLibrary.UNITS_KILOMETRES.getNameKey()),
I18nManager.getText(UnitSetLibrary.UNITS_MILES.getNameKey())
});
diff --git a/tim/prune/load/xml/GzipFileLoader.java b/tim/prune/load/xml/GzipFileLoader.java
index 3ebd4ca..4be2bab 100644
--- a/tim/prune/load/xml/GzipFileLoader.java
+++ b/tim/prune/load/xml/GzipFileLoader.java
@@ -3,8 +3,6 @@ package tim.prune.load.xml;
import java.io.File;
import java.io.FileInputStream;
import java.util.zip.GZIPInputStream;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
import tim.prune.App;
import tim.prune.I18nManager;
import tim.prune.data.SourceInfo;
@@ -42,8 +40,8 @@ public class GzipFileLoader
{
istream = new GZIPInputStream(new FileInputStream(inFile));
_xmlLoader.reset();
- SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
- saxParser.parse(istream, _xmlLoader);
+ // Parse the stream using either Xerces or java classes
+ _xmlLoader.parseXmlStream(istream);
XmlHandler handler = _xmlLoader.getHandler();
if (handler == null) {
_app.showErrorMessage("error.load.dialogtitle", "error.load.noread");
diff --git a/tim/prune/load/xml/XmlFileLoader.java b/tim/prune/load/xml/XmlFileLoader.java
index 783ac79..5af471a 100644
--- a/tim/prune/load/xml/XmlFileLoader.java
+++ b/tim/prune/load/xml/XmlFileLoader.java
@@ -2,14 +2,19 @@ package tim.prune.load.xml;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
import tim.prune.App;
import tim.prune.I18nManager;
@@ -66,13 +71,19 @@ public class XmlFileLoader extends DefaultHandler implements Runnable
public void run()
{
FileInputStream inStream = null;
+ boolean success = false;
try
{
- // Construct a SAXParser and use this as a default handler
- SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
inStream = new FileInputStream(_file);
- saxParser.parse(inStream, this);
+ success = parseXmlStream(inStream);
+ }
+ catch (FileNotFoundException fnfe) {}
+
+ // Clean up the stream, don't need it any more
+ try {inStream.close();} catch (IOException e2) {}
+ if (success)
+ {
// Check whether handler was properly instantiated
if (_handler == null)
{
@@ -90,18 +101,47 @@ public class XmlFileLoader extends DefaultHandler implements Runnable
new MediaLinkInfo(_handler.getLinkArray()));
}
}
- catch (Exception e)
+ }
+
+
+ /**
+ * Try both Xerces and the built-in java classes to parse the given xml stream
+ * @param inStream input stream from file / zip / gzip
+ * @return true on success, false if both xerces and built-in parser failed
+ */
+ public boolean parseXmlStream(InputStream inStream)
+ {
+ boolean success = false;
+ // Firstly, try to use xerces to parse the xml (will throw an exception if not available)
+ try
{
- // Show error dialog
- _app.showErrorMessageNoLookup("error.load.dialogtitle",
- I18nManager.getText("error.load.othererror") + " " + e.getMessage());
+ XMLReader xmlReader = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
+ xmlReader.setContentHandler(this);
+ xmlReader.parse(new InputSource(inStream));
+ success = true; // worked
}
- finally {
- try {inStream.close();} catch (IOException e2) {}
+ catch (Exception e) {} // don't care too much if it didn't work, there's a backup
+
+ // If that didn't work, try the built-in classes (which work for xml1.0 but handling for 1.1 contains bugs)
+ if (!success)
+ {
+ try
+ {
+ // Construct a SAXParser and use this as a default handler
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ saxParser.parse(inStream, this);
+ success = true;
+ }
+ catch (Exception e)
+ {
+ // Show error dialog
+ _app.showErrorMessageNoLookup("error.load.dialogtitle",
+ I18nManager.getText("error.load.othererror") + " " + e.getMessage());
+ }
}
+ return success;
}
-
/**
* Receive a tag
* @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
diff --git a/tim/prune/load/xml/ZipFileLoader.java b/tim/prune/load/xml/ZipFileLoader.java
index 74e48f9..eb2f9a6 100644
--- a/tim/prune/load/xml/ZipFileLoader.java
+++ b/tim/prune/load/xml/ZipFileLoader.java
@@ -56,8 +56,8 @@ public class ZipFileLoader
if (suffix.equals(".kml") || suffix.equals(".gpx") || suffix.equals(".xml"))
{
_xmlLoader.reset();
- SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
- saxParser.parse(file.getInputStream(entry), _xmlLoader);
+ // Parse the stream using either Xerces or java classes
+ _xmlLoader.parseXmlStream(file.getInputStream(entry));
XmlHandler handler = _xmlLoader.getHandler();
if (handler == null) {
_app.showErrorMessage("error.load.dialogtitle", "error.load.othererror");
diff --git a/tim/prune/readme.txt b/tim/prune/readme.txt
index 8a47ebe..d586911 100644
--- a/tim/prune/readme.txt
+++ b/tim/prune/readme.txt
@@ -1,11 +1,11 @@
-GpsPrune version 15.1
+GpsPrune version 16.2
=====================
GpsPrune is an application for viewing, editing and managing coordinate data from GPS systems,
including format conversion, charting and photo correlation.
Full details can be found at http://activityworkshop.net/software/gpsprune/
-GpsPrune is copyright 2006-2013 activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
+GpsPrune is copyright 2006-2014 activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
You may freely use the software, and may help others to freely use it too. For further information
on your rights and how they are protected, see the included license.txt file.
@@ -17,7 +17,7 @@ Running
=======
To run GpsPrune from the jar file, simply call it from a command prompt or shell:
- java -jar gpsprune_15.1.jar
+ java -jar gpsprune_16.2.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,20 +25,37 @@ in a file manager window to execute it. A shortcut, menu item, alias, desktop i
or other link can of course be made should you wish.
To specify a language other than the default, use an additional parameter, eg:
- java -jar gpsprune_15.1.jar --lang=DE
+ java -jar gpsprune_16.2.jar --lang=DE
-New with version 15.1
+New with version 16.2
=====================
+The following fixes were added since version 16.1:
+ - Fix for Gpx-slicing UTF8 files
+ - Conversion of sunrise/sunset times to local timezone
+ - Removal of Cloudmade maps
+ - Additional translations
+
+New with version 16.1
+=====================
+The following fixes were added since version 16:
+ - Caching of terrain information for three-dimensional views
+ - Additional translations
+ - Improved void filling by interpolation
+ - Remembering file type of imported files
+
+New with version 16
+===================
The following features were added since version 15:
- - Improved translations
- - Fixed bug with speed charts using gnuplot
- - Fixed bug with dragging a mid-point within a selection
- - Fixed bug with duplicate entries in profile popup menu
- - Fixed bug with loading zoom level of custom map sources
+ - Extend povray output using terrain and/or map image
+ - Extend java3d output using terrain and/or map image
+ - Weather forecasts
+ - Splitting a track into segments based on distance or time
+ - Sewing track segments together
+ - Function to download and save SRTM tiles
New with version 15
-===================
+=====================
The following features were added since version 14:
- Extend povray output using map image on base plane
- Export an image of the map and track at a selected zoom level
@@ -52,7 +69,7 @@ The following features were added since version 14:
- Allow loading of speeds and vertical speeds from text files
New with version 14
-===================
+=====================
The following features were added since version 13:
- Dragging of existing points
- Creation of new points by dragging the halfway point between two points
diff --git a/tim/prune/save/BaseImageConfigDialog.java b/tim/prune/save/BaseImageConfigDialog.java
index 514ebd7..0265569 100644
--- a/tim/prune/save/BaseImageConfigDialog.java
+++ b/tim/prune/save/BaseImageConfigDialog.java
@@ -1,7 +1,6 @@
package tim.prune.save;
import java.awt.BorderLayout;
-import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
@@ -18,22 +17,23 @@ import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
+import javax.swing.JProgressBar;
-import tim.prune.DataSubscriber;
import tim.prune.I18nManager;
import tim.prune.config.Config;
import tim.prune.data.Track;
import tim.prune.gui.map.MapSource;
import tim.prune.gui.map.MapSourceLibrary;
+import tim.prune.threedee.ImageDefinition;
/**
* Dialog to let you choose the parameters for a base image
- * (source and zoom)
+ * (source and zoom) including preview
*/
public class BaseImageConfigDialog implements Runnable
{
/** Parent to notify */
- private DataSubscriber _parent = null;
+ private BaseImageConsumer _parent = null;
/** Parent dialog for position */
private JDialog _parentDialog = null;
/** Track to use for preview image */
@@ -45,25 +45,27 @@ public class BaseImageConfigDialog implements Runnable
/** Panel to hold the other controls */
private JPanel _mainPanel = null;
/** Dropdown for map source */
- private JComboBox _mapSourceDropdown = null;
+ private JComboBox<String> _mapSourceDropdown = null;
/** Dropdown for zoom levels */
- private JComboBox _zoomDropdown = null;
- /** Warning label that image is incomplete */
- private JLabel _imageIncompleteLabel = null;
+ private JComboBox<String> _zoomDropdown = null;
+ /** Button to trigger a download of the missing map tiles */
+ private JButton _downloadTilesButton = null;
+ /** Progress bar for downloading additional tiles */
+ private JProgressBar _progressBar = null;
/** Label for number of tiles found */
private JLabel _tilesFoundLabel = null;
/** Label for image size in pixels */
private JLabel _imageSizeLabel = null;
/** Image preview panel */
private ImagePreviewPanel _previewPanel = null;
+ /** Grouter, used to avoid regenerating images */
+ private MapGrouter _grouter = new MapGrouter();
/** OK button, needs to be enabled/disabled */
private JButton _okButton = null;
/** Flag for rebuilding dialog, don't bother refreshing and recalculating */
private boolean _rebuilding = false;
/** Cached values to allow cancellation of dialog */
- private boolean _useImage = false;
- private int _sourceIndex = 0;
- private int _zoomLevel = 0;
+ private ImageDefinition _imageDef = new ImageDefinition();
/**
@@ -72,7 +74,7 @@ public class BaseImageConfigDialog implements Runnable
* @param inParentDialog parent dialog
* @param inTrack track object
*/
- public BaseImageConfigDialog(DataSubscriber inParent, JDialog inParentDialog, Track inTrack)
+ public BaseImageConfigDialog(BaseImageConsumer inParent, JDialog inParentDialog, Track inTrack)
{
_parent = inParent;
_parentDialog = inParentDialog;
@@ -84,6 +86,17 @@ public class BaseImageConfigDialog implements Runnable
}
/**
+ * @param inDefinition image definition object from previous dialog
+ */
+ public void setImageDefinition(ImageDefinition inDefinition)
+ {
+ _imageDef = inDefinition;
+ if (_imageDef == null) {
+ _imageDef = new ImageDefinition();
+ }
+ }
+
+ /**
* Begin the function
*/
public void begin()
@@ -110,26 +123,29 @@ public class BaseImageConfigDialog implements Runnable
private void initDialog()
{
_rebuilding = true;
- _useImageCheckbox.setSelected(_useImage);
+ _useImageCheckbox.setSelected(_imageDef.getUseImage());
// Populate the dropdown of map sources from the library in case it has changed
_mapSourceDropdown.removeAllItems();
for (int i=0; i<MapSourceLibrary.getNumSources(); i++)
{
_mapSourceDropdown.addItem(MapSourceLibrary.getSource(i).getName());
}
- if (_sourceIndex < 0 || _sourceIndex >= _mapSourceDropdown.getItemCount()) {
- _sourceIndex = 0;
+ int sourceIndex = _imageDef.getSourceIndex();
+ if (sourceIndex < 0 || sourceIndex >= _mapSourceDropdown.getItemCount()) {
+ sourceIndex = 0;
}
- _mapSourceDropdown.setSelectedIndex(_sourceIndex);
+ _mapSourceDropdown.setSelectedIndex(sourceIndex);
// Zoom level
- if (_useImage)
+ int zoomLevel = _imageDef.getZoom();
+ if (_imageDef.getUseImage())
{
for (int i=0; i<_zoomDropdown.getItemCount(); i++)
{
String item = _zoomDropdown.getItemAt(i).toString();
try {
- if (Integer.parseInt(item) == _zoomLevel) {
+ if (Integer.parseInt(item) == zoomLevel)
+ {
_zoomDropdown.setSelectedIndex(i);
break;
}
@@ -154,8 +170,11 @@ public class BaseImageConfigDialog implements Runnable
currentZoom = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
}
catch (Exception nfe) {}
+ // First time in, the dropdown might be empty but we still might have a zoom in the definition
+ if (_zoomDropdown.getItemCount() == 0) {
+ currentZoom = _imageDef.getZoom();
+ }
// Get the extent of the track so we can work out how big the images are going to be for each zoom level
- // System.out.println("Ranges are: x=" + _track.getXRange().getRange() + ", y=" + _track.getYRange().getRange());
final double xyExtent = Math.max(_track.getXRange().getRange(), _track.getYRange().getRange());
int zoomToSelect = -1;
@@ -248,24 +267,10 @@ public class BaseImageConfigDialog implements Runnable
/**
- * @return true if image has been selected
+ * @return image definition object
*/
- public boolean useImage() {
- return _useImage;
- }
-
- /**
- * @return index of selected image source
- */
- public int getSourceIndex() {
- return _sourceIndex;
- }
-
- /**
- * @return selected zoom level
- */
- public int getZoomLevel() {
- return _zoomLevel;
+ public ImageDefinition getImageDefinition() {
+ return _imageDef;
}
/**
@@ -296,7 +301,7 @@ public class BaseImageConfigDialog implements Runnable
JLabel sourceLabel = new JLabel(I18nManager.getText("dialog.baseimage.mapsource") + ": ");
sourceLabel.setHorizontalAlignment(JLabel.RIGHT);
controlsPanel.add(sourceLabel);
- _mapSourceDropdown = new JComboBox();
+ _mapSourceDropdown = new JComboBox<String>();
_mapSourceDropdown.addItem("name of map source");
// Add listener to dropdown to change zoom levels
_mapSourceDropdown.addActionListener(new ActionListener() {
@@ -309,7 +314,7 @@ public class BaseImageConfigDialog implements Runnable
JLabel zoomLabel = new JLabel(I18nManager.getText("dialog.baseimage.zoom") + ": ");
zoomLabel.setHorizontalAlignment(JLabel.RIGHT);
controlsPanel.add(zoomLabel);
- _zoomDropdown = new JComboBox();
+ _zoomDropdown = new JComboBox<String>();
// Add action listener to enable ok button when zoom changed
_zoomDropdown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
@@ -331,10 +336,21 @@ public class BaseImageConfigDialog implements Runnable
// Label panel on right
JPanel labelPanel = new JPanel();
labelPanel.setLayout(new BorderLayout());
- _imageIncompleteLabel = new JLabel(I18nManager.getText("dialog.baseimage.incomplete"));
- _imageIncompleteLabel.setForeground(Color.RED);
- _imageIncompleteLabel.setVisible(false);
- labelPanel.add(_imageIncompleteLabel, BorderLayout.NORTH);
+ JPanel downloadPanel = new JPanel();
+ downloadPanel.setLayout(new BorderLayout(4, 4));
+ _downloadTilesButton = new JButton(I18nManager.getText("button.load"));
+ _downloadTilesButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ downloadRemainingTiles();
+ }
+ });
+ _downloadTilesButton.setVisible(false);
+ downloadPanel.add(_downloadTilesButton, BorderLayout.NORTH);
+ _progressBar = new JProgressBar();
+ _progressBar.setIndeterminate(true);
+ _progressBar.setVisible(false);
+ downloadPanel.add(_progressBar, BorderLayout.SOUTH);
+ labelPanel.add(downloadPanel, BorderLayout.NORTH);
JPanel labelGridPanel = new JPanel();
labelGridPanel.setLayout(new GridLayout(0, 2, 10, 4));
labelGridPanel.add(new JLabel(I18nManager.getText("dialog.baseimage.tiles") + ": "));
@@ -401,7 +417,7 @@ public class BaseImageConfigDialog implements Runnable
private void updateImagePreview()
{
// Clear labels
- _imageIncompleteLabel.setVisible(false);
+ _downloadTilesButton.setVisible(false);
_tilesFoundLabel.setText("");
_imageSizeLabel.setText("");
if (_useImageCheckbox.isSelected() && _mapSourceDropdown.getSelectedIndex() >= 0
@@ -423,17 +439,11 @@ public class BaseImageConfigDialog implements Runnable
private void storeValues()
{
// Store values of controls in variables
- _useImage = _useImageCheckbox.isSelected();
- _sourceIndex = _mapSourceDropdown.getSelectedIndex();
- try {
- String zoomStr = _zoomDropdown.getSelectedItem().toString();
- _zoomLevel = Integer.parseInt(zoomStr);
- }
- catch (Exception nfe) {
- _zoomLevel = 0;
- }
- // Call parent to retrieve values
- _parent.dataUpdated(DataSubscriber.ALL);
+ _imageDef.setUseImage(_useImageCheckbox.isSelected(),
+ _mapSourceDropdown.getSelectedIndex(),
+ getSelectedZoomLevel());
+ // Inform parent that details have changed
+ _parent.baseImageChanged();
}
/**
@@ -447,16 +457,11 @@ public class BaseImageConfigDialog implements Runnable
final int zoomIndex = _zoomDropdown.getSelectedIndex();
if (!_useImageCheckbox.isSelected() || mapIndex < 0 || zoomIndex < 0) {return;}
- // Get the map source and zoom level
+ // Get the map source from the index
MapSource mapSource = MapSourceLibrary.getSource(mapIndex);
- int zoomLevel = 0;
- try {
- zoomLevel = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
- }
- catch (Exception e) {}
// Use the Grouter to create an image (slow, blocks thread)
- GroutedImage groutedImage = MapGrouter.createMapImage(_track, mapSource, zoomLevel);
+ GroutedImage groutedImage = _grouter.createMapImage(_track, mapSource, getSelectedZoomLevel());
// If the dialog hasn't changed, pass the generated image to the preview panel
if (_useImageCheckbox.isSelected()
@@ -465,8 +470,11 @@ public class BaseImageConfigDialog implements Runnable
&& groutedImage != null)
{
_previewPanel.setImage(groutedImage);
+ final int numTilesRemaining = groutedImage.getNumTilesTotal() - groutedImage.getNumTilesUsed();
+ final boolean offerDownload = numTilesRemaining > 0 && numTilesRemaining < 50;
// Set values of labels
- _imageIncompleteLabel.setVisible(!groutedImage.isComplete());
+ _downloadTilesButton.setVisible(offerDownload);
+ _downloadTilesButton.setEnabled(offerDownload);
_tilesFoundLabel.setText(groutedImage.getNumTilesUsed() + " / " + groutedImage.getNumTilesTotal());
if (groutedImage.getImageSize() > 0) {
_imageSizeLabel.setText("" + groutedImage.getImageSize());
@@ -479,17 +487,78 @@ public class BaseImageConfigDialog implements Runnable
{
_previewPanel.setImage(null);
// Clear labels
- _imageIncompleteLabel.setVisible(false);
+ _downloadTilesButton.setVisible(false);
_tilesFoundLabel.setText("");
_imageSizeLabel.setText("");
}
}
/**
+ * @return zoom level selected in the dropdown
+ */
+ private int getSelectedZoomLevel()
+ {
+ int zoomLevel = 0;
+ try {
+ zoomLevel = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
+ }
+ catch (Exception e) {
+ System.err.println("Exception: " + e.getClass().getName() + " : " + e.getMessage());
+ }
+ return zoomLevel;
+ }
+
+ /**
* @return true if any map data has been found for the image
*/
public boolean getFoundData()
{
- return _useImage && _zoomLevel > 0 && _previewPanel != null && _previewPanel.getTilesFound();
+ return _imageDef.getUseImage() && _imageDef.getZoom() > 0
+ && _previewPanel != null && _previewPanel.getTilesFound();
+ }
+
+ /**
+ * @return true if selected zoom is valid for the current track (based only on pixel size)
+ */
+ public boolean isSelectedZoomValid()
+ {
+ final double xyExtent = Math.max(_track.getXRange().getRange(), _track.getYRange().getRange());
+ // How many pixels does this give?
+ final int zoomFactor = 1 << _imageDef.getZoom();
+ final int pixCount = (int) (xyExtent * zoomFactor * 256);
+ return (pixCount > 100 // less than this isn't worth it
+ && pixCount < 4000); // don't want to run out of memory
+ }
+
+ /**
+ * @return the map grouter for retrieval of generated image
+ */
+ public MapGrouter getGrouter()
+ {
+ return _grouter;
+ }
+
+ /**
+ * @return method triggered by "download" button, to asynchronously download the missing tiles
+ */
+ private void downloadRemainingTiles()
+ {
+ _downloadTilesButton.setEnabled(false);
+ new Thread(new Runnable() {
+ public void run()
+ {
+ _progressBar.setVisible(true);
+ // Use a grouter to get all tiles from the TileManager, including downloading
+ MapGrouter grouter = new MapGrouter();
+ final int mapIndex = _mapSourceDropdown.getSelectedIndex();
+ if (!_useImageCheckbox.isSelected() || mapIndex < 0) {return;}
+ MapSource mapSource = MapSourceLibrary.getSource(mapIndex);
+ grouter.createMapImage(_track, mapSource, getSelectedZoomLevel(), true);
+ _progressBar.setVisible(false);
+ // And then refresh the dialog
+ _grouter.clearMapImage();
+ updateImagePreview();
+ }
+ }).start();
}
}
diff --git a/tim/prune/save/BaseImageConsumer.java b/tim/prune/save/BaseImageConsumer.java
new file mode 100644
index 0000000..3930099
--- /dev/null
+++ b/tim/prune/save/BaseImageConsumer.java
@@ -0,0 +1,10 @@
+package tim.prune.save;
+
+/**
+ * Interface used to inform consumers that the base image has been changed
+ */
+public interface BaseImageConsumer
+{
+ /** Notify consumer that base image has changed */
+ public void baseImageChanged();
+}
diff --git a/tim/prune/save/ExifSaver.java b/tim/prune/save/ExifSaver.java
index 3c1a7b8..d17c5d4 100644
--- a/tim/prune/save/ExifSaver.java
+++ b/tim/prune/save/ExifSaver.java
@@ -265,20 +265,17 @@ public class ExifSaver implements Runnable
}
_progressBar.setVisible(false);
// Show confirmation
- UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.saveexif.ok1") + " "
- + numSaved + " " + I18nManager.getText("confirm.saveexif.ok2"));
+ UpdateMessageBroker.informSubscribers(I18nManager.getTextWithNumber("confirm.saveexif.ok", numSaved));
if (numFailed > 0)
{
JOptionPane.showMessageDialog(_parentFrame,
- I18nManager.getText("error.saveexif.failed1") + " " + numFailed + " "
- + I18nManager.getText("error.saveexif.failed2"),
+ I18nManager.getTextWithNumber("error.saveexif.failed", numFailed),
I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE);
}
if (numForced > 0)
{
JOptionPane.showMessageDialog(_parentFrame,
- I18nManager.getText("error.saveexif.forced1") + " " + numForced + " "
- + I18nManager.getText("error.saveexif.forced2"),
+ I18nManager.getTextWithNumber("error.saveexif.forced", numForced),
I18nManager.getText("dialog.saveexif.title"), JOptionPane.WARNING_MESSAGE);
}
// close dialog, all finished
diff --git a/tim/prune/save/GpxExporter.java b/tim/prune/save/GpxExporter.java
index 8d9df8a..b0cb283 100644
--- a/tim/prune/save/GpxExporter.java
+++ b/tim/prune/save/GpxExporter.java
@@ -11,7 +11,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
-import java.nio.charset.Charset;
import javax.swing.BorderFactory;
import javax.swing.Box;
@@ -67,7 +66,6 @@ public class GpxExporter extends GenericFunction implements Runnable
private JPanel _encodingsPanel = null;
private JRadioButton _useSystemRadio = null, _forceUtf8Radio = null;
private File _exportFile = null;
- private static String _systemEncoding = null;
/** this program name */
private static final String GPX_CREATOR = "GpsPrune v" + GpsPrune.VERSION_NUMBER + " activityworkshop.net";
@@ -99,15 +97,16 @@ public class GpxExporter extends GenericFunction implements Runnable
_dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
_dialog.setLocationRelativeTo(_parentFrame);
_dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
- _systemEncoding = getSystemEncoding();
_dialog.getContentPane().add(makeDialogComponents());
_dialog.pack();
}
_pointTypeSelector.init(_app.getTrackInfo());
- _encodingsPanel.setVisible(!isSystemUtf8());
- if (!isSystemUtf8()) {
+ _encodingsPanel.setVisible(!XmlUtils.isSystemUtf8());
+ if (!XmlUtils.isSystemUtf8())
+ {
+ String systemEncoding = XmlUtils.getSystemEncoding();
_useSystemRadio.setText(I18nManager.getText("dialog.exportgpx.encoding.system")
- + " (" + (_systemEncoding == null ? "unknown" : _systemEncoding) + ")");
+ + " (" + (systemEncoding == null ? "unknown" : systemEncoding) + ")");
}
_dialog.setVisible(true);
}
@@ -148,7 +147,7 @@ public class GpxExporter extends GenericFunction implements Runnable
mainPanel.add(checkPanel);
// panel for selecting character encoding
_encodingsPanel = new JPanel();
- if (!isSystemUtf8())
+ if (!XmlUtils.isSystemUtf8())
{
// only add this panel if system isn't utf8 (or can't be identified yet)
_encodingsPanel.setBorder(BorderFactory.createCompoundBorder(
@@ -486,7 +485,9 @@ public class GpxExporter extends GenericFunction implements Runnable
// get the source from the point (if any)
String pointSource = getPointSource(inCachers, point);
// Clear point source if it's the wrong type of point (eg changed from waypoint or route point)
- if (pointSource != null && !pointSource.toLowerCase().startsWith(inPointTag)) {pointSource = null;}
+ if (pointSource != null && !pointSource.trim().toLowerCase().startsWith(inPointTag)) {
+ pointSource = null;
+ }
if (pointSource != null || !inOnlyCopies)
{
// restart track segment if necessary
@@ -626,62 +627,9 @@ public class GpxExporter extends GenericFunction implements Runnable
*/
private static String getXmlHeaderString(OutputStreamWriter inWriter)
{
- return "<?xml version=\"1.0\" encoding=\"" + getEncoding(inWriter) + "\"?>\n";
- }
-
-
- /**
- * Get the default system encoding using a writer
- * @param inWriter writer object
- * @return string defining encoding
- */
- private static String getEncoding(OutputStreamWriter inWriter)
- {
- String encoding = inWriter.getEncoding();
- try {
- encoding = Charset.forName(encoding).name();
- }
- catch (Exception e) {} // ignore failure to find encoding
- return encoding;
- }
-
-
- /**
- * Use a temporary file to obtain the name of the default system encoding
- * @return name of default system encoding, or null if write failed
- */
- private static String getSystemEncoding()
- {
- File tempFile = null;
- String encoding = null;
- try
- {
- tempFile = File.createTempFile("prune", null);
- OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile));
- encoding = getEncoding(writer);
- writer.close();
- }
- catch (IOException e) {} // value stays null
- // Delete temp file
- if (tempFile != null && tempFile.exists()) {
- if (!tempFile.delete()) {
- System.err.println("Cannot delete temp file: " + tempFile.getAbsolutePath());
- }
- }
- // If writing failed (eg permissions) then just ask system for default
- if (encoding == null) encoding = Charset.defaultCharset().name();
- return encoding;
+ return "<?xml version=\"1.0\" encoding=\"" + XmlUtils.getEncoding(inWriter) + "\"?>\n";
}
- /**
- * Creates temp file if necessary to check system encoding
- * @return true if system uses UTF-8 by default
- */
- private static boolean isSystemUtf8()
- {
- if (_systemEncoding == null) _systemEncoding = getSystemEncoding();
- return (_systemEncoding != null && _systemEncoding.toUpperCase().equals("UTF-8"));
- }
/**
* Get the header string for the gpx tag
@@ -694,6 +642,7 @@ public class GpxExporter extends GenericFunction implements Runnable
if (inCachers != null) {gpxHeader = inCachers.getFirstHeader();}
if (gpxHeader == null || gpxHeader.length() < 5)
{
+ // TODO: Consider changing this to default to GPX 1.1
// Create default (1.0) header
gpxHeader = "<gpx version=\"1.0\" creator=\"" + GPX_CREATOR
+ "\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
diff --git a/tim/prune/save/GroutedImage.java b/tim/prune/save/GroutedImage.java
index 4400067..d5cbc05 100644
--- a/tim/prune/save/GroutedImage.java
+++ b/tim/prune/save/GroutedImage.java
@@ -38,13 +38,6 @@ public class GroutedImage
}
/**
- * @return true if all the required tiles were found
- */
- public boolean isComplete() {
- return _numTilesMissing == 0;
- }
-
- /**
* @return the pixel dimensions of the result image
*/
public int getImageSize()
diff --git a/tim/prune/save/ImageExporter.java b/tim/prune/save/ImageExporter.java
index fabed33..41bc0e6 100644
--- a/tim/prune/save/ImageExporter.java
+++ b/tim/prune/save/ImageExporter.java
@@ -16,6 +16,7 @@ import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
@@ -23,10 +24,8 @@ import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
-import javax.swing.border.EtchedBorder;
import tim.prune.App;
-import tim.prune.DataSubscriber;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.config.ColourScheme;
@@ -34,24 +33,26 @@ import tim.prune.config.Config;
import tim.prune.data.DataPoint;
import tim.prune.data.DoubleRange;
import tim.prune.data.Track;
+import tim.prune.gui.BaseImageDefinitionPanel;
import tim.prune.gui.GuiGridLayout;
import tim.prune.gui.WholeNumberField;
import tim.prune.gui.map.MapSource;
import tim.prune.gui.map.MapSourceLibrary;
import tim.prune.gui.map.MapUtils;
import tim.prune.load.GenericFileFilter;
+import tim.prune.threedee.ImageDefinition;
/**
* Class to handle the exporting of map images, optionally with track data drawn on top.
* This allows images larger than the screen to be generated.
*/
-public class ImageExporter extends GenericFunction implements DataSubscriber
+public class ImageExporter extends GenericFunction implements BaseImageConsumer
{
private JDialog _dialog = null;
private JCheckBox _drawDataCheckbox = null;
+ private JCheckBox _drawTrackPointsCheckbox = null;
private WholeNumberField _textScaleField = null;
- private JLabel _baseImageLabel = null;
- private BaseImageConfigDialog _baseImageConfig = null;
+ private BaseImageDefinitionPanel _baseImagePanel = null;
private JFileChooser _fileChooser = null;
private JButton _okButton = null;
@@ -83,10 +84,6 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
_dialog.pack();
_textScaleField.setValue(100);
}
- // Make base image dialog too
- if (_baseImageConfig == null) {
- _baseImageConfig = new BaseImageConfigDialog(this, _dialog, _app.getTrackInfo().getTrack());
- }
// Check if there is a cache to use
if (!BaseImageConfigDialog.isImagePossible())
@@ -95,7 +92,8 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
return;
}
- updateBaseImageDetails();
+ _baseImagePanel.updateBaseImageDetails();
+ baseImageChanged();
// Show dialog
_dialog.setVisible(true);
}
@@ -111,6 +109,15 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
// Checkbox for drawing track or not
_drawDataCheckbox = new JCheckBox(I18nManager.getText("dialog.exportimage.drawtrack"));
_drawDataCheckbox.setSelected(true); // draw by default
+ // Also whether to draw track points or not
+ _drawTrackPointsCheckbox = new JCheckBox(I18nManager.getText("dialog.exportimage.drawtrackpoints"));
+ _drawTrackPointsCheckbox.setSelected(true);
+ // Add listener to en/disable trackpoints checkbox
+ _drawDataCheckbox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ _drawTrackPointsCheckbox.setEnabled(_drawDataCheckbox.isSelected());
+ }
+ });
// TODO: Maybe have other controls such as line width, symbol scale factor
JPanel controlsPanel = new JPanel();
@@ -128,7 +135,7 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
public void actionPerformed(ActionEvent e)
{
doExport();
- MapGrouter.clearMapImage();
+ _baseImagePanel.getGrouter().clearMapImage();
_dialog.dispose();
}
});
@@ -137,7 +144,7 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- MapGrouter.clearMapImage();
+ _baseImagePanel.getGrouter().clearMapImage();
_dialog.dispose();
}
});
@@ -150,96 +157,41 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
{
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
_dialog.dispose();
- MapGrouter.clearMapImage();
+ _baseImagePanel.getGrouter().clearMapImage();
}
}
};
_drawDataCheckbox.addKeyListener(closer);
// Panel for the base image
- JPanel imagePanel = new JPanel();
- imagePanel.setLayout(new BorderLayout(10, 4));
- imagePanel.add(new JLabel(I18nManager.getText("dialog.exportpov.baseimage") + ": "), BorderLayout.WEST);
- _baseImageLabel = new JLabel("Typical sourcename");
- imagePanel.add(_baseImageLabel, BorderLayout.CENTER);
- JButton baseImageButton = new JButton(I18nManager.getText("button.edit"));
- baseImageButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent event) {
- changeBaseImage();
- }
- });
- baseImageButton.addKeyListener(closer);
- imagePanel.add(baseImageButton, BorderLayout.EAST);
- imagePanel.setBorder(BorderFactory.createCompoundBorder(
- BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
- );
+ _baseImagePanel = new BaseImageDefinitionPanel(this, _dialog, _app.getTrackInfo().getTrack());
+
+ // Panel for the checkboxes at the top
+ JPanel checkPanel = new JPanel();
+ checkPanel.setLayout(new BoxLayout(checkPanel, BoxLayout.Y_AXIS));
+ checkPanel.add(_drawDataCheckbox);
+ checkPanel.add(_drawTrackPointsCheckbox);
// add these panels to the holder panel
JPanel holderPanel = new JPanel();
holderPanel.setLayout(new BorderLayout(5, 5));
- holderPanel.add(_drawDataCheckbox, BorderLayout.NORTH);
+ holderPanel.add(checkPanel, BorderLayout.NORTH);
holderPanel.add(controlsPanel, BorderLayout.CENTER);
- holderPanel.add(imagePanel, BorderLayout.SOUTH);
+ holderPanel.add(_baseImagePanel, BorderLayout.SOUTH);
holderPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
panel.add(holderPanel, BorderLayout.NORTH);
return panel;
}
- /**
- * Change the base image by calling the BaseImageConfigDialog
- */
- private void changeBaseImage()
- {
- // Check if there is a cache to use
- if (BaseImageConfigDialog.isImagePossible())
- {
- // Show new dialog to choose image details
- _baseImageConfig.beginWithImageYes();
- }
- }
-
- /**
- * Callback from base image config dialog
- */
- public void dataUpdated(byte inUpdateType)
- {
- updateBaseImageDetails();
- }
-
- /** Not required */
- public void actionCompleted(String inMessage) {
- }
-
- /**
- * Update the description label according to the selected base image details
- */
- private void updateBaseImageDetails()
- {
- String desc = null;
- if (_baseImageConfig.useImage())
- {
- MapSource source = MapSourceLibrary.getSource(_baseImageConfig.getSourceIndex());
- if (source != null) {
- desc = source.getName() + " ("
- + _baseImageConfig.getZoomLevel() + ")";
- }
- }
- if (desc == null) {
- desc = I18nManager.getText("dialog.about.no");
- }
- _baseImageLabel.setText(desc);
- _okButton.setEnabled(_baseImageConfig.useImage() && _baseImageConfig.getFoundData()
- && MapGrouter.isZoomLevelOk(_app.getTrackInfo().getTrack(), _baseImageConfig.getZoomLevel()));
- }
/**
* Select the file and export data to it
*/
private void doExport()
{
- // OK pressed, so choose output file
_okButton.setEnabled(false);
+ // OK pressed, so choose output file
if (_fileChooser == null)
{
_fileChooser = new JFileChooser();
@@ -296,9 +248,11 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
private boolean exportFile(File inPngFile)
{
// Get the image file from the grouter
- MapSource source = MapSourceLibrary.getSource(_baseImageConfig.getSourceIndex());
- GroutedImage baseImage = MapGrouter.getMapImage(_app.getTrackInfo().getTrack(), source,
- _baseImageConfig.getZoomLevel());
+ ImageDefinition imageDef = _baseImagePanel.getImageDefinition();
+ MapSource source = MapSourceLibrary.getSource(imageDef.getSourceIndex());
+ MapGrouter grouter = _baseImagePanel.getGrouter();
+ GroutedImage baseImage = grouter.getMapImage(_app.getTrackInfo().getTrack(), source,
+ imageDef.getZoom());
if (baseImage == null || !baseImage.isValid())
{
_app.showErrorMessage(getNameKey(), "dialog.exportpov.cannotmakebaseimage");
@@ -332,9 +286,9 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
// Work out x, y limits for drawing
DoubleRange xRange = inImage.getXRange();
DoubleRange yRange = inImage.getYRange();
- int zoomFactor = 1 << _baseImageConfig.getZoomLevel();
+ final int zoomFactor = 1 << _baseImagePanel.getImageDefinition().getZoom();
Graphics g = inImage.getImage().getGraphics();
- // TODO: Set colour, line width
+ // TODO: Set line width, style etc
g.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT));
// Loop over points
@@ -355,8 +309,11 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
// draw from previous point to this one
g.drawLine(prevX, prevY, px, py);
}
- // draw this point
- g.drawRect(px-2, py-2, 3, 3);
+ // Only draw points if requested
+ if (_drawTrackPointsCheckbox.isSelected())
+ {
+ g.drawRect(px-2, py-2, 3, 3);
+ }
// save coordinates
prevX = px; prevY = py;
}
@@ -451,4 +408,16 @@ public class ImageExporter extends GenericFunction implements DataSubscriber
// Note: Differences from main map: No mapPosition (modifying position and visible points),
// no selection, no opacities, maybe different scale/text factors
}
+
+ /**
+ * Base image has changed, need to enable/disable ok button
+ */
+ public void baseImageChanged()
+ {
+ final boolean useImage = _baseImagePanel.getImageDefinition().getUseImage();
+ final int zoomLevel = _baseImagePanel.getImageDefinition().getZoom();
+ final boolean okEnabled = useImage && _baseImagePanel.getFoundData()
+ && MapGrouter.isZoomLevelOk(_app.getTrackInfo().getTrack(), zoomLevel);
+ _okButton.setEnabled(okEnabled);
+ }
}
diff --git a/tim/prune/save/KmlExporter.java b/tim/prune/save/KmlExporter.java
index 6fbf749..8021834 100644
--- a/tim/prune/save/KmlExporter.java
+++ b/tim/prune/save/KmlExporter.java
@@ -525,7 +525,7 @@ public class KmlExporter extends GenericFunction implements Runnable
}
// Make a blob with description for each photo
// Photos have already been written so picture sizes already known
- if (point.getPhoto() != null && writePhotos && writeCurrentPoint)
+ if (point.getPhoto() != null && point.getPhoto().isValid() && writePhotos && writeCurrentPoint)
{
if (!writtenPhotoHeader)
{
@@ -876,21 +876,21 @@ public class KmlExporter extends GenericFunction implements Runnable
ImageWriter imageWriter = writers.next();
// Check selection checkbox
- boolean justSelection = _pointTypeSelector.getJustSelection();
+ final boolean justSelection = _pointTypeSelector.getJustSelection();
int selStart = -1, selEnd = -1;
if (justSelection) {
selStart = _trackInfo.getSelection().getStart();
selEnd = _trackInfo.getSelection().getEnd();
}
- int numPoints = _track.getNumPoints();
+ final int numPoints = _track.getNumPoints();
DataPoint point = null;
int photoNum = 0;
// Loop over all points in track
for (int i=0; i<numPoints && !_cancelPressed; i++)
{
point = _track.getPoint(i);
- if (point.getPhoto() != null && (!justSelection || (i>=selStart && i<=selEnd)))
+ if (point.getPhoto() != null && point.getPhoto().isValid() && (!justSelection || (i>=selStart && i<=selEnd)))
{
photoNum++;
// Make a new entry in zip file
diff --git a/tim/prune/save/MapGrouter.java b/tim/prune/save/MapGrouter.java
index 65e23c0..263a306 100644
--- a/tim/prune/save/MapGrouter.java
+++ b/tim/prune/save/MapGrouter.java
@@ -1,11 +1,11 @@
package tim.prune.save;
-import tim.prune.config.Config;
import tim.prune.data.DoubleRange;
import tim.prune.data.Track;
import tim.prune.data.TrackExtents;
-import tim.prune.gui.map.DiskTileCacher;
import tim.prune.gui.map.MapSource;
+import tim.prune.gui.map.MapTileManager;
+import tim.prune.gui.map.TileConsumer;
import java.awt.Color;
import java.awt.Graphics;
@@ -17,15 +17,15 @@ import java.awt.image.BufferedImage;
* Class to handle the sticking together (grouting) of map tiles
* to create a single map image for the current track
*/
-public abstract class MapGrouter
+public class MapGrouter implements TileConsumer
{
/** The most recently produced image */
- private static GroutedImage _lastGroutedImage = null;
+ private GroutedImage _lastGroutedImage = null;
/**
* Clear the last image, it's not needed any more
*/
- public static void clearMapImage() {
+ public void clearMapImage() {
_lastGroutedImage = null;
}
@@ -36,7 +36,20 @@ public abstract class MapGrouter
* @param inZoom selected zoom level
* @return grouted image, or null if no image could be created
*/
- public static GroutedImage createMapImage(Track inTrack, MapSource inMapSource, int inZoom)
+ public GroutedImage createMapImage(Track inTrack, MapSource inMapSource, int inZoom)
+ {
+ return createMapImage(inTrack, inMapSource, inZoom, false);
+ }
+
+ /**
+ * Grout the required map tiles together according to the track's extent
+ * @param inTrack track object
+ * @param inMapSource map source to use (may have one or two layers)
+ * @param inZoom selected zoom level
+ * @param inDownload true to download tiles, false (by default) to just pull from disk
+ * @return grouted image, or null if no image could be created
+ */
+ public GroutedImage createMapImage(Track inTrack, MapSource inMapSource, int inZoom, boolean inDownload)
{
// Get the extents of the track including a standard (10%) border around the data
TrackExtents extents = new TrackExtents(inTrack);
@@ -44,8 +57,6 @@ public abstract class MapGrouter
DoubleRange xRange = extents.getXRange();
DoubleRange yRange = extents.getYRange();
- // Get path to disk cache
- final String cachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
// Work out which tiles are required
final int zoomFactor = 1 << inZoom;
final int minTileX = (int) (xRange.getMinimum() * zoomFactor);
@@ -55,7 +66,7 @@ public abstract class MapGrouter
// Work out how big the final image will be, create a BufferedImage
final int pixCount = (int) (extents.getXRange().getRange() * zoomFactor * 256);
- if (pixCount < 2) {return null;}
+ if (pixCount < 2 || inZoom == 0) {return null;}
BufferedImage resultImage = new BufferedImage(pixCount, pixCount, BufferedImage.TYPE_INT_RGB);
Graphics g = resultImage.getGraphics();
g.setColor(Color.WHITE);
@@ -63,8 +74,16 @@ public abstract class MapGrouter
// Work out where to start drawing the tiles on the image
int xOffset = (int) ((minTileX - xRange.getMinimum() * zoomFactor) * 256);
+ // Make a map tile manager to load (or download) the tiles
+ MapTileManager tileManager = new MapTileManager(this);
+ tileManager.setMapSource(inMapSource);
+ tileManager.enableTileDownloading(inDownload);
+ tileManager.setReturnIncompleteImages();
+ tileManager.setZoom(inZoom);
+
int numTilesUsed = 0;
int numTilesMissing = 0;
+
// Loop over the tiles
for (int x = minTileX; x <= maxTileX; x++)
{
@@ -73,13 +92,25 @@ public abstract class MapGrouter
{
for (int layer=0; layer < inMapSource.getNumLayers(); layer++)
{
- Image tile = DiskTileCacher.getTile(cachePath, inMapSource.makeFilePath(layer, inZoom, x, y), false);
+ Image tile = tileManager.getTile(layer, x, y, true);
+ // If we're downloading tiles, wait until the tile isn't null
+ int waitCount = 0;
+ while (tile == null && inDownload && waitCount < 3)
+ {
+ // System.out.println("wait " + waitCount + " for tile to be not null");
+ try {Thread.sleep(250);} catch (InterruptedException e) {}
+ tile = tileManager.getTile(layer, x, y, false); // don't request another download
+ waitCount++;
+ }
+ // See if there's a tile or not
if (tile != null)
{
// Wait until tile is available (loaded asynchronously)
- while (tile.getWidth(null) < 0) {
+ while (tile.getWidth(null) < 0 && !inDownload)
+ {
+ // System.out.println("Wait for tile width");
try {
- Thread.sleep(100);
+ Thread.sleep(inDownload ? 500 : 100);
}
catch (InterruptedException ie) {}
}
@@ -88,7 +119,11 @@ public abstract class MapGrouter
numTilesUsed++;
g.drawImage(tile, xOffset, yOffset, null);
}
- else numTilesMissing++;
+ else
+ {
+ // null tile, that means it's either not available or really slow to start downloading
+ numTilesMissing++;
+ }
}
yOffset += 256;
}
@@ -112,7 +147,7 @@ public abstract class MapGrouter
* @param inZoom selected zoom level
* @return grouted image, or null if no image could be created
*/
- public static GroutedImage getMapImage(Track inTrack, MapSource inMapSource, int inZoom)
+ public synchronized GroutedImage getMapImage(Track inTrack, MapSource inMapSource, int inZoom)
{
if (_lastGroutedImage == null) {
_lastGroutedImage = createMapImage(inTrack, inMapSource, inZoom);
@@ -136,4 +171,10 @@ public abstract class MapGrouter
final int pixCount = (int) (extents.getXRange().getRange() * zoomFactor * 256);
return pixCount > 2 && pixCount < 4000;
}
+
+ /** React to tiles being updated by the tile manager */
+ public void tilesUpdated(boolean inIsOk)
+ {
+ // Doesn't need any action
+ }
}
diff --git a/tim/prune/save/PovExporter.java b/tim/prune/save/PovExporter.java
index ba66058..19e4485 100644
--- a/tim/prune/save/PovExporter.java
+++ b/tim/prune/save/PovExporter.java
@@ -26,27 +26,32 @@ import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
-import javax.swing.border.EtchedBorder;
import tim.prune.App;
-import tim.prune.DataSubscriber;
+import tim.prune.FunctionLibrary;
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.function.srtm.LookupSrtmFunction;
+import tim.prune.gui.BaseImageDefinitionPanel;
import tim.prune.gui.DialogCloser;
+import tim.prune.gui.TerrainDefinitionPanel;
import tim.prune.gui.map.MapSource;
import tim.prune.gui.map.MapSourceLibrary;
import tim.prune.load.GenericFileFilter;
+import tim.prune.threedee.ImageDefinition;
+import tim.prune.threedee.TerrainCache;
+import tim.prune.threedee.TerrainDefinition;
+import tim.prune.threedee.TerrainHelper;
import tim.prune.threedee.ThreeDModel;
/**
* Class to export a 3d scene of the track to a specified Pov file
- * Note: Subscriber interface only used for callback from image config dialog
*/
-public class PovExporter extends Export3dFunction implements DataSubscriber
+public class PovExporter extends Export3dFunction
{
private Track _track = null;
private JDialog _dialog = null;
@@ -55,9 +60,10 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
private JTextField _cameraXField = null, _cameraYField = null, _cameraZField = null;
private JTextField _fontName = null, _altitudeFactorField = null;
private JRadioButton _ballsAndSticksButton = null;
- private JLabel _baseImageLabel = null;
- private JButton _baseImageButton = null;
- private BaseImageConfigDialog _baseImageConfig = null;
+ /** Panel for defining the base image */
+ private BaseImageDefinitionPanel _baseImagePanel = null;
+ /** Component for defining the terrain */
+ private TerrainDefinitionPanel _terrainPanel = null;
// defaults
private static final double DEFAULT_CAMERA_DISTANCE = 30.0;
@@ -107,17 +113,17 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
*/
public void begin()
{
- // Make dialog window to select angles, colours etc
+ // Make dialog window to select inputs
if (_dialog == null)
{
_dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
_dialog.setLocationRelativeTo(_parentFrame);
_dialog.getContentPane().add(makeDialogComponents());
}
- // Make base image dialog
- if (_baseImageConfig == null)
- {
- _baseImageConfig = new BaseImageConfigDialog(this, _dialog, _track);
+ // Get exaggeration factor from config
+ final int exaggFactor = Config.getConfigInt(Config.KEY_HEIGHT_EXAGGERATION);
+ if (exaggFactor > 0) {
+ _altFactor = exaggFactor / 100.0;
}
// Set angles
@@ -125,7 +131,14 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
_cameraYField.setText(_cameraY);
_cameraZField.setText(_cameraZ);
_altitudeFactorField.setText("" + _altFactor);
- updateBaseImageDetails();
+ // Pass terrain and image def parameters (if any) to the panels
+ if (_terrainDef != null) {
+ _terrainPanel.initTerrainParameters(_terrainDef);
+ }
+ if (_imageDef != null) {
+ _baseImagePanel.initImageParameters(_imageDef);
+ }
+ _baseImagePanel.updateBaseImageDetails();
// Show dialog
_dialog.pack();
_dialog.setVisible(true);
@@ -150,8 +163,14 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- doExport();
- MapGrouter.clearMapImage();
+ // Need to launch export in new thread
+ new Thread(new Runnable() {
+ public void run()
+ {
+ doExport();
+ _baseImagePanel.getGrouter().clearMapImage();
+ }
+ }).start();
_dialog.dispose();
}
});
@@ -160,7 +179,7 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- MapGrouter.clearMapImage();
+ _baseImagePanel.getGrouter().clearMapImage();
_dialog.dispose();
}
});
@@ -223,26 +242,10 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
group.add(_ballsAndSticksButton); group.add(tubesButton);
stylePanel.add(radioPanel);
- // Panel for the base image
- JPanel imagePanel = new JPanel();
- imagePanel.setLayout(new BorderLayout(10, 4));
- imagePanel.add(new JLabel(I18nManager.getText("dialog.exportpov.baseimage") + ": "), BorderLayout.WEST);
- _baseImageLabel = new JLabel("Typical sourcename");
- imagePanel.add(_baseImageLabel, BorderLayout.CENTER);
- _baseImageButton = new JButton(I18nManager.getText("button.edit"));
- _baseImageButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent event) {
- changeBaseImage();
- }
- });
- imagePanel.add(_baseImageButton, BorderLayout.EAST);
- // Put these image controls inside a holder panel with an outline
- JPanel imageHolderPanel = new JPanel();
- imageHolderPanel.setBorder(BorderFactory.createCompoundBorder(
- BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
- );
- imageHolderPanel.setLayout(new BorderLayout());
- imageHolderPanel.add(imagePanel, BorderLayout.NORTH);
+ // Panel for the base image (parent is null because we don't need callback)
+ _baseImagePanel = new BaseImageDefinitionPanel(null, _dialog, _track);
+ // Panel for the terrain definition
+ _terrainPanel = new TerrainDefinitionPanel();
// add these panels to the holder panel
JPanel holderPanel = new JPanel();
@@ -253,48 +256,15 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
boxPanel.add(Box.createVerticalStrut(4));
boxPanel.add(stylePanel);
boxPanel.add(Box.createVerticalStrut(4));
- boxPanel.add(imageHolderPanel);
+ boxPanel.add(_terrainPanel);
+ boxPanel.add(Box.createVerticalStrut(4));
+ boxPanel.add(_baseImagePanel);
holderPanel.add(boxPanel, BorderLayout.CENTER);
panel.add(holderPanel, BorderLayout.CENTER);
return panel;
}
- /**
- * Change the base image by calling the BaseImageConfigDialog
- */
- private void changeBaseImage()
- {
- // Check if there is a cache to use
- if (BaseImageConfigDialog.isImagePossible())
- {
- // Show new dialog to choose image details
- _baseImageConfig.begin();
- }
- else {
- _app.showErrorMessage(getNameKey(), "dialog.exportimage.noimagepossible");
- }
- }
-
- /**
- * Update the description label according to the selected base image details
- */
- private void updateBaseImageDetails()
- {
- String desc = null;
- if (_baseImageConfig.useImage())
- {
- MapSource source = MapSourceLibrary.getSource(_baseImageConfig.getSourceIndex());
- if (source != null) {
- desc = source.getName() + " ("
- + _baseImageConfig.getZoomLevel() + ")";
- }
- }
- if (desc == null) {
- desc = I18nManager.getText("dialog.about.no");
- }
- _baseImageLabel.setText(desc);
- }
/**
* Select the file and export data to it
@@ -333,20 +303,26 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
}
final int nameLen = povFile.getName().length() - 4;
final File imageFile = new File(povFile.getParentFile(), povFile.getName().substring(0, nameLen) + "_base.png");
- final boolean imageExists = _baseImageConfig.useImage() && imageFile.exists();
+ final File terrainFile = new File(povFile.getParentFile(), povFile.getName().substring(0, nameLen) + "_terrain.png");
+ final boolean imageExists = _baseImagePanel.getImageDefinition().getUseImage() && imageFile.exists();
+ final boolean terrainFileExists = _terrainPanel.getUseTerrain() && terrainFile.exists();
+
// Check if files exist and if necessary prompt for overwrite
Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
- if ((!povFile.exists() && !imageExists) || JOptionPane.showOptionDialog(_parentFrame,
+ if ((!povFile.exists() && !imageExists && !terrainFileExists)
+ || 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(povFile, imageFile))
+ // Export the file(s)
+ if (exportFiles(povFile, imageFile, terrainFile))
{
// file saved - store directory in config for later
Config.setConfigString(Config.KEY_TRACK_DIR, povFile.getParentFile().getAbsolutePath());
+ // also store exaggeration
+ Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_altFactor * 100));
}
else
{
@@ -365,12 +341,13 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
/**
- * Export the track data to the specified file
+ * Export the data to the specified file(s)
* @param inPovFile File object to save pov file to
* @param inImageFile file object to save image to
+ * @param inTerrainFile file object to save terrain to
* @return true if successful
*/
- private boolean exportFile(File inPovFile, File inImageFile)
+ private boolean exportFiles(File inPovFile, File inImageFile, File inTerrainFile)
{
FileWriter writer = null;
// find out the line separator for this system
@@ -383,20 +360,23 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
try
{
// try to use given altitude cap
- double altFactor = Double.parseDouble(_altitudeFactorField.getText());
- model.setAltitudeFactor(altFactor);
+ double givenFactor = Double.parseDouble(_altitudeFactorField.getText());
+ if (givenFactor > 0.0) _altFactor = givenFactor;
}
catch (NumberFormatException nfe) { // parse failed, reset
- _altitudeFactorField.setText("1.0");
+ _altitudeFactorField.setText("" + _altFactor);
}
- model.scale();
+ model.setAltitudeFactor(_altFactor);
- boolean useImage = _baseImageConfig.useImage();
+ // Write base image if necessary
+ ImageDefinition imageDef = _baseImagePanel.getImageDefinition();
+ boolean useImage = imageDef.getUseImage();
if (useImage)
{
// Get base image from grouter
- MapSource mapSource = MapSourceLibrary.getSource(_baseImageConfig.getSourceIndex());
- GroutedImage baseImage = MapGrouter.getMapImage(_track, mapSource, _baseImageConfig.getZoomLevel());
+ MapSource mapSource = MapSourceLibrary.getSource(imageDef.getSourceIndex());
+ MapGrouter grouter = _baseImagePanel.getGrouter();
+ GroutedImage baseImage = grouter.getMapImage(_track, mapSource, imageDef.getZoom());
try
{
useImage = ImageIO.write(baseImage.getImage(), "png", inImageFile);
@@ -410,9 +390,49 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
}
}
+ boolean useTerrain = _terrainPanel.getUseTerrain();
+ if (useTerrain)
+ {
+ TerrainHelper terrainHelper = new TerrainHelper(_terrainPanel.getGridSize());
+ // See if there's a previously saved terrain track we can reuse
+ TerrainDefinition terrainDef = new TerrainDefinition(_terrainPanel.getUseTerrain(), _terrainPanel.getGridSize());
+ Track terrainTrack = TerrainCache.getTerrainTrack(_app.getCurrentDataStatus(), terrainDef);
+ if (terrainTrack == null)
+ {
+ // Construct the terrain track according to these extents and the grid size
+ terrainTrack = terrainHelper.createGridTrack(_track);
+ // Get the altitudes from SRTM for all the points in the track
+ LookupSrtmFunction srtmLookup = (LookupSrtmFunction) FunctionLibrary.FUNCTION_LOOKUP_SRTM;
+ srtmLookup.begin(terrainTrack);
+ while (srtmLookup.isRunning())
+ {
+ try {
+ Thread.sleep(750); // just polling in a wait loop isn't ideal but simple
+ }
+ catch (InterruptedException e) {}
+ }
+ // Fix the voids
+ terrainHelper.fixVoids(terrainTrack);
+
+ // Store this back in the cache, maybe we'll need it again
+ TerrainCache.storeTerrainTrack(terrainTrack, _app.getCurrentDataStatus(), terrainDef);
+ }
+
+ model.setTerrain(terrainTrack);
+ model.scale();
+
+ // Call TerrainHelper to write out the data from the model
+ terrainHelper.writeHeightMap(model, inTerrainFile);
+ }
+ else
+ {
+ // No terrain required, so just scale the model as it is
+ model.scale();
+ }
+
// Create file and write basics
writer = new FileWriter(inPovFile);
- writeStartOfFile(writer, lineSeparator, useImage ? inImageFile : null);
+ writeStartOfFile(writer, lineSeparator, useImage ? inImageFile : null, useTerrain ? inTerrainFile : null);
// write out points
if (_ballsAndSticksButton.isSelected()) {
@@ -451,12 +471,15 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
* @param inWriter Writer to use for writing file
* @param inLineSeparator line separator to use
* @param inImageFile image file to reference (or null if none)
+ * @param inTerrainFile terrain file to reference (or null if none)
* @throws IOException on file writing error
*/
- private void writeStartOfFile(FileWriter inWriter, String inLineSeparator, File inImageFile)
+ private void writeStartOfFile(FileWriter inWriter, String inLineSeparator, File inImageFile, File inTerrainFile)
throws IOException
{
- inWriter.write("// Pov file produced by GpsPrune - see http://activityworkshop.net/");
+ inWriter.write("// Pov file produced by GpsPrune - see http://gpsprune.activityworkshop.net/");
+ inWriter.write(inLineSeparator);
+ inWriter.write("#version 3.6;");
inWriter.write(inLineSeparator);
inWriter.write(inLineSeparator);
// Select font based on user input
@@ -471,17 +494,20 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
// Make the definition of the base plane depending on whether there's an image or not
final boolean useImage = (inImageFile != null);
- final String boxDefinition = (inImageFile == null ?
- " <-10.0, -0.15, -10.0>," + inLineSeparator
- + " <10.0, 0.15, 10.0>" + inLineSeparator
- + " pigment { color rgb <0.5 0.75 0.8> }"
- :
+ final boolean useImageOnBox = useImage && (inTerrainFile == null);
+ final String boxDefinition = (useImageOnBox ?
" <0, 0, 0>, <1, 1, 0.001>" + inLineSeparator
+ " pigment {image_map { png \"" + inImageFile.getName() + "\" map_type 0 interpolate 2 once } }" + inLineSeparator
+ " scale 20.0 rotate <90, 0, 0>" + inLineSeparator
- + " translate <-10.0, 0, -10.0>");
+ + " translate <-10.0, 0, -10.0>"
+ : " <-10.0, -0.15, -10.0>," + inLineSeparator
+ + " <10.0, 0.0, 10.0>" + inLineSeparator
+ + " pigment { color rgb <0.5 0.75 0.8> }");
// TODO: Maybe could use the same geometry for the imageless case, would simplify code a bit
+ // Definition of terrain shape if any
+ final String terrainDefinition = makeTerrainString(inTerrainFile, inImageFile, inLineSeparator);
+
// Set up output
String[] outputLines = {
"global_settings { ambient_light rgb <4, 4, 4> }", "",
@@ -574,6 +600,8 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
"box {",
boxDefinition,
"}", "",
+ // terrain
+ terrainDefinition,
// write cardinals
"// Cardinal letters N,S,E,W",
"text {",
@@ -612,6 +640,31 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
}
}
+ /**
+ * Make a description of the height_field object for the terrain, depending on terrain and image
+ * @param inTerrainFile terrain file, or null if none
+ * @param inImageFile image file, or null if none
+ * @param inLineSeparator line separator
+ * @return String for inserting into pov file
+ */
+ private static String makeTerrainString(File inTerrainFile, File inImageFile, String inLineSeparator)
+ {
+ if (inTerrainFile == null) {return "";}
+ StringBuilder sb = new StringBuilder();
+ sb.append("//Terrain").append(inLineSeparator)
+ .append("height_field {").append(inLineSeparator)
+ .append("\tpng \"").append(inTerrainFile.getName()).append("\" smooth").append(inLineSeparator)
+ .append("\tfinish {diffuse 0.7 phong 0.2}").append(inLineSeparator);
+ if (inImageFile != null) {
+ sb.append("\tpigment {image_map { png \"").append(inImageFile.getName()).append("\" } rotate x*90}").append(inLineSeparator);
+ }
+ else {
+ sb.append("\tpigment {color rgb <0.55 0.7 0.55> }").append(inLineSeparator);
+ }
+ sb.append("\tscale 20.0").append(inLineSeparator)
+ .append("\ttranslate <-10.0, 0, -10.0>").append(inLineSeparator).append("}");
+ return sb.toString();
+ }
/**
* Write out all the data points to the file in the balls-and-sticks style
@@ -668,7 +721,6 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
inWriter.write("// Data points:");
inWriter.write(inLineSeparator);
int numPoints = inModel.getNumPoints();
- int numTrackPoints = 0;
// Loop over all points and write out waypoints as balls
for (int i=0; i<numPoints; i++)
{
@@ -686,7 +738,6 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
}
inWriter.write(inLineSeparator);
}
- else {numTrackPoints++;}
}
inWriter.write(inLineSeparator);
@@ -878,16 +929,4 @@ public class PovExporter extends Export3dFunction implements DataSubscriber
}
return segmentList;
}
-
- /**
- * Callback from base image config dialog
- */
- public void dataUpdated(byte inUpdateType)
- {
- updateBaseImageDetails();
- }
-
- /** Not required */
- public void actionCompleted(String inMessage) {
- }
}
diff --git a/tim/prune/save/SvgExporter.java b/tim/prune/save/SvgExporter.java
index 33f42f9..d623b4a 100644
--- a/tim/prune/save/SvgExporter.java
+++ b/tim/prune/save/SvgExporter.java
@@ -86,13 +86,18 @@ public class SvgExporter extends Export3dFunction
*/
public void begin()
{
- // Make dialog window to select angles, colours etc
+ // Make dialog window to select input parameters
if (_dialog == null)
{
_dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
_dialog.setLocationRelativeTo(_parentFrame);
_dialog.getContentPane().add(makeDialogComponents());
}
+ // Get exaggeration factor from config
+ final int exaggFactor = Config.getConfigInt(Config.KEY_HEIGHT_EXAGGERATION);
+ if (exaggFactor > 0) {
+ _altFactor = exaggFactor / 100.0;
+ }
// Set angles
NumberFormat threeDP = NumberFormat.getNumberInstance();
@@ -228,6 +233,8 @@ public class SvgExporter extends Export3dFunction
{
// file saved - store directory in config for later
Config.setConfigString(Config.KEY_TRACK_DIR, file.getParentFile().getAbsolutePath());
+ // also store exaggeration
+ Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_altFactor * 100));
}
else {
// export failed so need to choose again
diff --git a/tim/prune/save/xml/ByteBuffer.java b/tim/prune/save/xml/ByteBuffer.java
new file mode 100644
index 0000000..67091e1
--- /dev/null
+++ b/tim/prune/save/xml/ByteBuffer.java
@@ -0,0 +1,88 @@
+package tim.prune.save.xml;
+
+import java.nio.charset.Charset;
+
+/**
+ * Class to collect the bytes from an input stream
+ * and turn them into a String
+ */
+public class ByteBuffer
+{
+ // Array of bytes
+ private byte[] _bytes = new byte[1024];
+ // Current position to append
+ private int _currPos = 0;
+ // Flag for recognising utf8 encoded streams
+ private boolean _streamUtf8 = false;
+ // Flag for whether system is utf8 or not
+ private final boolean _systemUtf8 = XmlUtils.isSystemUtf8();
+
+ /**
+ * Append the given byte to the buffer
+ * @param inB byte to append
+ */
+ public void appendByte(byte inB)
+ {
+ // Resize array if necessary
+ if (_currPos >= _bytes.length)
+ {
+ byte[] bigger = new byte[_bytes.length * 2];
+ System.arraycopy(_bytes, 0, bigger, 0, _bytes.length);
+ _bytes = bigger;
+ }
+ // Append byte and increment counter
+ _bytes[_currPos] = inB;
+ _currPos++;
+ }
+
+ /**
+ * Clear the buffer and reset
+ */
+ public void clear()
+ {
+ _currPos = 0;
+ // Reduce size back to default if it's got too big
+ if (_bytes.length > 5000) {
+ _bytes = new byte[1024];
+ }
+ }
+
+ /**
+ * Set the flag that this stream is encoded with utf8
+ */
+ public void setEncodingUtf8() {
+ _streamUtf8 = true;
+ }
+
+ /**
+ * @return contents of buffer as a String
+ */
+ public String toString()
+ {
+ // Sometimes the encoding of the read file isn't the default encoding of the system
+ if (_streamUtf8 && !_systemUtf8)
+ {
+ return new String(_bytes, 0, _currPos, Charset.forName("UTF-8"));
+ }
+ // Otherwise just use system encoding
+ return new String(_bytes, 0, _currPos);
+ }
+
+ /**
+ * Look for the given character sequence in the last characters read
+ * @param inChars sequence to look for
+ * @return true if sequence found
+ */
+ public boolean foundSequence(char[] inChars)
+ {
+ final int numChars = inChars.length;
+ if (_currPos < numChars) {return false;}
+ for (int i=0; i<numChars; i++)
+ {
+ char searchChar = inChars[numChars - 1 - i];
+ char sourceChar = (char) _bytes[_currPos - 1 - i];
+ if (searchChar != sourceChar) {return false;}
+ }
+ return true;
+ }
+}
diff --git a/tim/prune/save/xml/GpxSlicer.java b/tim/prune/save/xml/GpxSlicer.java
index efc58b4..d18d760 100644
--- a/tim/prune/save/xml/GpxSlicer.java
+++ b/tim/prune/save/xml/GpxSlicer.java
@@ -12,8 +12,6 @@ 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();
@@ -27,7 +25,6 @@ public class GpxSlicer
private static final char[] CDATA_START = "<![CDATA[".toCharArray();
private static final char[] CDATA_END = "]]>".toCharArray();
-
/**
* Constructor
* @param inReceiver listener for tags
@@ -43,7 +40,8 @@ public class GpxSlicer
*/
public void slice(InputStream inStream)
{
- _builder = new StringBuilder(100);
+ StringBuffer beginBuffer = new StringBuffer(200);
+ ByteBuffer byteBuffer = new ByteBuffer();
boolean insideTag = false;
boolean insideCdata = false;
char[] endTag = null;
@@ -53,37 +51,49 @@ public class GpxSlicer
{
while ((b = inStream.read()) >= 0)
{
- if (!insideTag && !insideCdata) {
- if (b == '<') _builder.setLength(0);
- }
// copy character
- _builder.append((char)b);
+ byteBuffer.appendByte((byte) b);
+ // clear buffer if necessary
+ if (!insideTag && !insideCdata && (b == '>' || b == '\n'))
+ {
+ byteBuffer.clear();
+ continue;
+ }
+ // if we're still at the beginning, copy to the begin buffer as well
+ if (beginBuffer != null) {beginBuffer.append((char) b);}
if (insideCdata) {
// Just look for end of cdata block
- if (foundSequence(CDATA_END)) {insideCdata = false;}
+ if (byteBuffer.foundSequence(CDATA_END)) {insideCdata = false;}
}
else
{
if (!insideTag)
{
// Look for start of one of the tags
- if (!foundHeader && foundSequence(GPX_START)) {
+ if (!foundHeader && byteBuffer.foundSequence(GPX_START))
+ {
insideTag = true;
foundHeader = true;
endTag = GPX_END;
+ // Check begin buffer for utf8 encoding
+ if (beginBuffer != null && beginBuffer.toString().toLowerCase().indexOf("encoding=\"utf-8\"") > 0)
+ {
+ byteBuffer.setEncodingUtf8();
+ }
+ beginBuffer = null; // don't need it any more
}
else if (b == 't')
{
- if (foundSequence(TRKPT_START)) {
+ if (byteBuffer.foundSequence(TRKPT_START)) {
insideTag = true;
endTag = TRKPT_END;
}
- else if (foundSequence(WPT_START)) {
+ else if (byteBuffer.foundSequence(WPT_START)) {
insideTag = true;
endTag = WPT_END;
}
- else if (foundSequence(RTEPT_START)) {
+ else if (byteBuffer.foundSequence(RTEPT_START)) {
insideTag = true;
endTag = RTEPT_END;
}
@@ -92,37 +102,21 @@ public class GpxSlicer
else
{
// Look for end of found tag
- if (foundSequence(endTag)) {
- _receiver.reportTag(_builder.toString());
- _builder.setLength(0);
+ if (byteBuffer.foundSequence(endTag))
+ {
+ String tag = byteBuffer.toString();
+ _receiver.reportTag(tag);
+ byteBuffer.clear();
insideTag = false;
}
}
// Look for start of cdata block
- if (foundSequence(CDATA_START)) {insideCdata = true;}
+ if (byteBuffer.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/XmlUtils.java b/tim/prune/save/xml/XmlUtils.java
index a4f9cae..dc8284c 100644
--- a/tim/prune/save/xml/XmlUtils.java
+++ b/tim/prune/save/xml/XmlUtils.java
@@ -1,5 +1,11 @@
package tim.prune.save.xml;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
+
/**
* Collection of utility functions for handling XML
*/
@@ -9,6 +15,8 @@ public abstract class XmlUtils
private static final String CDATA_START = "<![CDATA[";
/** End of Cdata sequence */
private static final String CDATA_END = "]]>";
+ /** Cached copy of system encoding string */
+ private static String _systemEncoding = null;
/**
* Fix the CDATA blocks in the given String to give valid xml
@@ -34,4 +42,68 @@ public abstract class XmlUtils
}
return CDATA_START + result + CDATA_END;
}
+
+
+ /**
+ * @return true if system uses UTF-8 by default
+ */
+ public static boolean isSystemUtf8()
+ {
+ String systemEncoding = getSystemEncoding();
+ return (systemEncoding != null && systemEncoding.toUpperCase().equals("UTF-8"));
+ }
+
+ /**
+ * @return name of the system's character encoding
+ */
+ public static String getSystemEncoding()
+ {
+ if (_systemEncoding == null) {
+ _systemEncoding = determineSystemEncoding();
+ }
+ return _systemEncoding;
+ }
+
+ /**
+ * Use a temporary file to obtain the name of the default system encoding
+ * @return name of default system encoding, or null if write failed
+ */
+ private static String determineSystemEncoding()
+ {
+ File tempFile = null;
+ String encoding = null;
+ try
+ {
+ tempFile = File.createTempFile("gpsprune", null);
+ OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile));
+ encoding = getEncoding(writer);
+ writer.close();
+ }
+ catch (IOException e) {} // value stays null
+ // Delete temp file
+ if (tempFile != null && tempFile.exists()) {
+ if (!tempFile.delete()) {
+ System.err.println("Cannot delete temp file: " + tempFile.getAbsolutePath());
+ }
+ }
+ // If writing failed (eg permissions) then just ask system for default
+ if (encoding == null) encoding = Charset.defaultCharset().name();
+ return encoding;
+ }
+
+
+ /**
+ * Get the default system encoding using a writer
+ * @param inWriter writer object
+ * @return string defining encoding
+ */
+ public static String getEncoding(OutputStreamWriter inWriter)
+ {
+ String encoding = inWriter.getEncoding();
+ try {
+ encoding = Charset.forName(encoding).name();
+ }
+ catch (Exception e) {} // ignore failure to find encoding
+ return encoding;
+ }
}
diff --git a/tim/prune/threedee/ImageDefinition.java b/tim/prune/threedee/ImageDefinition.java
new file mode 100644
index 0000000..a456f37
--- /dev/null
+++ b/tim/prune/threedee/ImageDefinition.java
@@ -0,0 +1,66 @@
+package tim.prune.threedee;
+
+/**
+ * Holds the definition of the image to use
+ * (whether or not to use an image, and the source index and zoom)
+ */
+public class ImageDefinition
+{
+ private boolean _useImage = false;
+ private int _sourceIndex = 0;
+ private int _zoom = 0;
+
+
+ /**
+ * Empty constructor specifying no image
+ */
+ public ImageDefinition()
+ {
+ this(false, 0, 0);
+ }
+
+ /**
+ * Constructor
+ * @param inUse true to use an image
+ * @param inSourceIndex index of map source
+ * @param inZoom zoom level
+ */
+ public ImageDefinition(boolean inUse, int inSourceIndex, int inZoom)
+ {
+ setUseImage(inUse, inSourceIndex, inZoom);
+ }
+
+ /**
+ * Set the parameters
+ * @param inUse true to use an image
+ * @param inSourceIndex index of map source
+ * @param inZoom zoom level
+ */
+ public void setUseImage(boolean inUse, int inSourceIndex, int inZoom)
+ {
+ _useImage = inUse;
+ _sourceIndex = inSourceIndex;
+ _zoom = inZoom;
+ }
+
+ /**
+ * @return true if image should be used, false otherwise
+ */
+ public boolean getUseImage() {
+ return _useImage && _sourceIndex >= 0 && _zoom > 2;
+ }
+
+ /**
+ * @return source index
+ */
+ public int getSourceIndex() {
+ return _sourceIndex;
+ }
+
+ /**
+ * @return zoom level
+ */
+ public int getZoom() {
+ return _zoom;
+ }
+}
diff --git a/tim/prune/threedee/Java3DWindow.java b/tim/prune/threedee/Java3DWindow.java
index cfac9af..de52bd6 100644
--- a/tim/prune/threedee/Java3DWindow.java
+++ b/tim/prune/threedee/Java3DWindow.java
@@ -1,7 +1,7 @@
package tim.prune.threedee;
-import java.awt.FlowLayout;
import java.awt.BorderLayout;
+import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
@@ -18,14 +18,18 @@ import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.Font3D;
import javax.media.j3d.FontExtrusion;
+import javax.media.j3d.GeometryArray;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.Group;
import javax.media.j3d.Material;
import javax.media.j3d.PointLight;
+import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Text3D;
+import javax.media.j3d.Texture;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
+import javax.media.j3d.TriangleStripArray;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
@@ -34,19 +38,26 @@ import javax.vecmath.Color3f;
import javax.vecmath.Matrix3d;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
+import javax.vecmath.TexCoord2f;
import javax.vecmath.Vector3d;
+import tim.prune.DataStatus;
+import tim.prune.FunctionLibrary;
+import tim.prune.I18nManager;
+import tim.prune.data.Track;
+import tim.prune.function.Export3dFunction;
+import tim.prune.function.srtm.LookupSrtmFunction;
+import tim.prune.gui.map.MapSourceLibrary;
+import tim.prune.save.GroutedImage;
+import tim.prune.save.MapGrouter;
+
import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.geometry.Cylinder;
import com.sun.j3d.utils.geometry.Sphere;
+import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.universe.SimpleUniverse;
-import tim.prune.FunctionLibrary;
-import tim.prune.I18nManager;
-import tim.prune.data.Track;
-import tim.prune.function.Export3dFunction;
-
/**
* Class to hold main window for java3d view of data
@@ -58,7 +69,11 @@ public class Java3DWindow implements ThreeDWindow
private JFrame _frame = null;
private ThreeDModel _model = null;
private OrbitBehavior _orbit = null;
- private double _altFactor = 5.0;
+ private double _altFactor = -1.0;
+ private ImageDefinition _imageDefinition = null;
+ private GroutedImage _baseImage = null;
+ private TerrainDefinition _terrainDefinition = null;
+ private DataStatus _dataStatus = null;
/** only prompt about big track size once */
private static boolean TRACK_SIZE_WARNING_GIVEN = false;
@@ -90,23 +105,52 @@ public class Java3DWindow implements ThreeDWindow
_track = inTrack;
}
+ /**
+ * @param inFactor altitude factor to use
+ */
+ public void setAltitudeFactor(double inFactor)
+ {
+ _altFactor = inFactor;
+ }
+
+ /**
+ * Set the parameters for the base image and do the grouting already
+ * (setTrack should already be called by now)
+ */
+ public void setBaseImageParameters(ImageDefinition inDefinition)
+ {
+ _imageDefinition = inDefinition;
+ if (inDefinition != null && inDefinition.getUseImage())
+ {
+ _baseImage = new MapGrouter().createMapImage(_track, MapSourceLibrary.getSource(inDefinition.getSourceIndex()),
+ inDefinition.getZoom());
+ }
+ else _baseImage = null;
+ }
+
+ /**
+ * Set the terrain parameters
+ */
+ public void setTerrainParameters(TerrainDefinition inDefinition)
+ {
+ _terrainDefinition = inDefinition;
+ }
+
+ /**
+ * Set the current data status
+ */
+ public void setDataStatus(DataStatus inStatus)
+ {
+ _dataStatus = inStatus;
+ }
/**
* Show the window
*/
public void show() throws ThreeDException
{
- // 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, _altFactor);
- if (factorString == null) return;
- try {
- _altFactor = Double.parseDouble(factorString.toString());
- }
- catch (Exception e) {} // Ignore parse errors
- if (_altFactor < 1.0) {_altFactor = 1.0;}
+ // Make sure altitude exaggeration is positive
+ if (_altFactor < 0.0) {_altFactor = 1.0;}
// Set up the graphics config
GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
@@ -270,6 +314,71 @@ public class Java3DWindow implements ThreeDWindow
plane = new Box(10f, 0.04f, 10f, planeAppearance);
objTrans.addChild(plane);
+ // Image on top of base plane, if specified
+ final boolean showTerrain = _terrainDefinition != null && _terrainDefinition.getUseTerrain();
+ if (_baseImage != null && !showTerrain)
+ {
+ QuadArray baseSquare = new QuadArray (4, QuadArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2);
+ baseSquare.setCoordinate(0, new Point3f(-10f, 0.05f, -10f));
+ baseSquare.setCoordinate(1, new Point3f(-10f, 0.05f, 10f));
+ baseSquare.setCoordinate(2, new Point3f( 10f, 0.05f, 10f));
+ baseSquare.setCoordinate(3, new Point3f( 10f, 0.05f, -10f));
+ // and set anchor points for the texture
+ baseSquare.setTextureCoordinate(0, 0, new TexCoord2f(0.0f, 1.0f));
+ baseSquare.setTextureCoordinate(0, 1, new TexCoord2f(0.0f, 0.0f));
+ baseSquare.setTextureCoordinate(0, 2, new TexCoord2f(1.0f, 0.0f));
+ baseSquare.setTextureCoordinate(0, 3, new TexCoord2f(1.0f, 1.0f));
+ // Set appearance including image
+ Appearance baseAppearance = new Appearance();
+ Texture mapImage = new TextureLoader(_baseImage.getImage(), _frame).getTexture();
+ baseAppearance.setTexture(mapImage);
+ objTrans.addChild(new Shape3D(baseSquare, baseAppearance));
+ }
+
+ // Create model containing track information
+ _model = new ThreeDModel(_track);
+ _model.setAltitudeFactor(_altFactor);
+
+ if (showTerrain)
+ {
+ TerrainHelper terrainHelper = new TerrainHelper(_terrainDefinition.getGridSize());
+ // See if there's a previously saved terrain track we can reuse
+ Track terrainTrack = TerrainCache.getTerrainTrack(_dataStatus, _terrainDefinition);
+ if (terrainTrack == null)
+ {
+ // Construct the terrain track according to these extents and the grid size
+ terrainTrack = terrainHelper.createGridTrack(_track);
+ // Get the altitudes from SRTM for all the points in the track
+ LookupSrtmFunction srtmLookup = (LookupSrtmFunction) FunctionLibrary.FUNCTION_LOOKUP_SRTM;
+ srtmLookup.begin(terrainTrack);
+ while (srtmLookup.isRunning())
+ {
+ try {
+ Thread.sleep(750); // just polling in a wait loop isn't ideal but simple
+ }
+ catch (InterruptedException e) {}
+ }
+
+ // Fix the voids
+ terrainHelper.fixVoids(terrainTrack);
+
+ // Store this back in the cache, maybe we'll need it again
+ TerrainCache.storeTerrainTrack(terrainTrack, _dataStatus, _terrainDefinition);
+ }
+ else System.out.println("Yay - reusing the cached track!");
+
+ // Give the terrain definition to the _model as well
+ _model.setTerrain(terrainTrack);
+ _model.scale();
+
+ objTrans.addChild(createTerrain(_model, terrainHelper, _baseImage));
+ }
+ else
+ {
+ // No terrain, so just scale the model as it is
+ _model.scale();
+ }
+
// N, S, E, W
GeneralPath bevelPath = new GeneralPath();
bevelPath.moveTo(0.0f, 0.0f);
@@ -289,11 +398,6 @@ public class Java3DWindow implements ThreeDWindow
objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.w"), new Point3f(-11f, 0f, 0f), compassFont));
objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.e"), new Point3f(10f, 0f, 0f), compassFont));
- // create and scale model
- _model = new ThreeDModel(_track);
- _model.setAltitudeFactor(_altFactor);
- _model.scale();
-
// Add points to model
objTrans.addChild(createDataPoints(_model));
@@ -407,7 +511,9 @@ public class Java3DWindow implements ThreeDWindow
}
- /** @return track point object */
+ /**
+ * @return track point object
+ */
private static Group createTrackpoint(Point3d inPointPos, byte inHeightCode)
{
Material mat = getTrackpointMaterial(inHeightCode);
@@ -417,7 +523,9 @@ public class Java3DWindow implements ThreeDWindow
}
- /** @return Material object for track points with the appropriate colour for the height */
+ /**
+ * @return Material object for track points with the appropriate colour for the height
+ */
private static Material getTrackpointMaterial(byte inHeightCode)
{
// create default material
@@ -473,6 +581,50 @@ public class Java3DWindow implements ThreeDWindow
return group;
}
+ /**
+ * Create a java3d Shape for the terrain
+ * @param inModel threedModel
+ * @param inHelper terrain helper
+ * @param inBaseImage base image for shape, or null for no image
+ * @return Shape3D object
+ */
+ private static Shape3D createTerrain(ThreeDModel inModel, TerrainHelper inHelper, GroutedImage inBaseImage)
+ {
+ final int numNodes = inHelper.getGridSize();
+ final int RESULT_SIZE = numNodes * (numNodes * 2 - 2);
+ final int GEOMETRY_COLOURING_TYPE = (inBaseImage == null ? GeometryArray.COLOR_3 : GeometryArray.TEXTURE_COORDINATE_2);
+
+ int[] stripData = inHelper.getStripLengths();
+ TriangleStripArray tsa = new TriangleStripArray(RESULT_SIZE, GeometryArray.COORDINATES | GEOMETRY_COLOURING_TYPE,
+ stripData);
+ // Get the scaled terrainTrack coordinates (or just heights) from the model
+ final int nSquared = numNodes * numNodes;
+ Point3d[] rawPoints = new Point3d[nSquared];
+ for (int i=0; i<nSquared; i++)
+ {
+ double height = inModel.getScaledTerrainValue(i) * MODEL_SCALE_FACTOR;
+ rawPoints[i] = new Point3d(inModel.getScaledTerrainHorizValue(i) * MODEL_SCALE_FACTOR,
+ Math.max(height, 0.05), // make sure it's above the box
+ -inModel.getScaledTerrainVertValue(i) * MODEL_SCALE_FACTOR);
+ }
+ tsa.setCoordinates(0, inHelper.getTerrainCoordinates(rawPoints));
+
+ Appearance tAppearance = new Appearance();
+ if (inBaseImage != null)
+ {
+ tsa.setTextureCoordinates(0, 0, inHelper.getTextureCoordinates());
+ Texture mapImage = new TextureLoader(inBaseImage.getImage()).getTexture();
+ tAppearance.setTexture(mapImage);
+ }
+ else
+ {
+ Color3f[] colours = new Color3f[RESULT_SIZE];
+ Color3f terrainColour = new Color3f(0.1f, 0.2f, 0.2f);
+ for (int i=0; i<RESULT_SIZE; i++) {colours[i] = terrainColour;}
+ tsa.setColors(0, colours);
+ }
+ return new Shape3D(tsa, tAppearance);
+ }
/**
* Calculate the angles and call them back to the app
@@ -495,9 +647,13 @@ public class Java3DWindow implements ThreeDWindow
Point3d result = new Point3d();
secondTran.transform(point, result);
firstTran.transform(result);
- // Callback settings to pov export function
+
+ // Give the settings to the rendering function
inFunction.setCameraCoordinates(result.x, result.y, result.z);
inFunction.setAltitudeExaggeration(_altFactor);
+ inFunction.setTerrainDefinition(_terrainDefinition); // ignored by svg, used by pov
+ inFunction.setImageDefinition(_imageDefinition); // ignored by svg, used by pov
+
inFunction.begin();
}
}
diff --git a/tim/prune/threedee/TerrainCache.java b/tim/prune/threedee/TerrainCache.java
new file mode 100644
index 0000000..5f5733f
--- /dev/null
+++ b/tim/prune/threedee/TerrainCache.java
@@ -0,0 +1,57 @@
+package tim.prune.threedee;
+
+import tim.prune.DataStatus;
+import tim.prune.data.Track;
+
+/**
+ * This abstract class acts as a singleton to store a single
+ * terrain model (as a Track) for a given data status and terrain definition.
+ * When the data or the definition changes, this track becomes invalid.
+ */
+public abstract class TerrainCache
+{
+ /** The data status at the time this terrain was generated */
+ private static DataStatus _dataStatus = null;
+ /** The definition (grid size) for this terrain */
+ private static TerrainDefinition _terrainDef = null;
+ /** The generated grid of points with altitudes */
+ private static Track _terrainTrack = null;
+
+
+ /**
+ * Get the stored terrain track if it's still valid
+ * @param inCurrStatus current data status
+ * @param inTerrainDef currently selected terrain definition
+ * @return stored terrain track if it's valid, null otherwise
+ */
+ public static Track getTerrainTrack(DataStatus inCurrStatus, TerrainDefinition inTerrainDef)
+ {
+ if (_dataStatus == null || _terrainDef == null || _terrainTrack == null)
+ {
+ return null; // nothing stored
+ }
+ if (inCurrStatus == null || inTerrainDef == null || !inTerrainDef.getUseTerrain())
+ {
+ return null; // nonsense requested
+ }
+ if (inCurrStatus.hasDataChanged(_dataStatus) || !inTerrainDef.equals(_terrainDef))
+ {
+ return null; // stored track is out of date
+ }
+ // we have a match
+ return _terrainTrack;
+ }
+
+ /**
+ * Now that a terrain track has been generated, store it for possible reuse
+ * @param inTrack terrain track to store
+ * @param inCurrStatus current data status
+ * @param inTerrainDef terrain definition
+ */
+ public static void storeTerrainTrack(Track inTrack, DataStatus inCurrStatus, TerrainDefinition inTerrainDef)
+ {
+ _terrainTrack = inTrack;
+ _dataStatus = inCurrStatus;
+ _terrainDef = inTerrainDef;
+ }
+}
diff --git a/tim/prune/threedee/TerrainDefinition.java b/tim/prune/threedee/TerrainDefinition.java
new file mode 100644
index 0000000..d6cf385
--- /dev/null
+++ b/tim/prune/threedee/TerrainDefinition.java
@@ -0,0 +1,68 @@
+package tim.prune.threedee;
+
+/**
+ * Holds the definition of the terrain to use
+ * (whether or not to use a terrain, and the resolution)
+ */
+public class TerrainDefinition
+{
+ private boolean _useTerrain = false;
+ private int _gridSize = 0;
+
+ /**
+ * Empty constructor specifying no terrain
+ */
+ public TerrainDefinition()
+ {
+ this(false, 0);
+ }
+
+ /**
+ * Constructor
+ * @param inUse true to use a terrain
+ * @param inGridSize size of grid
+ */
+ public TerrainDefinition(boolean inUse, int inGridSize)
+ {
+ setUseTerrain(inUse, inGridSize);
+ }
+
+ /**
+ * Set the parameters
+ * @param inUse true to use a terrain
+ * @param inGridSize size of grid
+ */
+ public void setUseTerrain(boolean inUse, int inGridSize)
+ {
+ _useTerrain = inUse;
+ _gridSize = inGridSize;
+ }
+
+ /**
+ * @return true if terrain should be used, false otherwise
+ */
+ public boolean getUseTerrain() {
+ return _useTerrain && _gridSize > 2;
+ }
+
+ /**
+ * @return grid size
+ */
+ public int getGridSize() {
+ return _gridSize;
+ }
+
+ @Override
+ /**
+ * Compare two TerrainDefinitions to see if they're equal
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == null || !(obj instanceof TerrainDefinition)) {
+ return false;
+ }
+ TerrainDefinition other = (TerrainDefinition) obj;
+ return _useTerrain == other._useTerrain
+ && _gridSize == other._gridSize;
+ }
+}
diff --git a/tim/prune/threedee/TerrainHelper.java b/tim/prune/threedee/TerrainHelper.java
new file mode 100644
index 0000000..0be2216
--- /dev/null
+++ b/tim/prune/threedee/TerrainHelper.java
@@ -0,0 +1,477 @@
+package tim.prune.threedee;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.vecmath.Point3d;
+import javax.vecmath.TexCoord2f;
+
+import tim.prune.data.Altitude;
+import tim.prune.data.Coordinate;
+import tim.prune.data.DataPoint;
+import tim.prune.data.DoubleRange;
+import tim.prune.data.Field;
+import tim.prune.data.FieldList;
+import tim.prune.data.Latitude;
+import tim.prune.data.Longitude;
+import tim.prune.data.Track;
+import tim.prune.data.TrackExtents;
+import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.map.MapUtils;
+
+/**
+ * Helper for generating the arrays needed for the 3d terrain
+ */
+public class TerrainHelper
+{
+ /** Number of nodes on each side of the square grid */
+ private int _gridSize = 0;
+
+ /**
+ * Constructor
+ * @param inGridSize grid size
+ */
+ public TerrainHelper(int inGridSize) {
+ _gridSize = inGridSize;
+ }
+
+ /**
+ * @return grid size
+ */
+ public int getGridSize() {
+ return _gridSize;
+ }
+
+
+ /**
+ * Convert the terrain coordinates from raw form to TriangleStripArray form
+ * (with repeated nodes)
+ * @param inRawPoints array of raw points as formed from the track
+ * @return point coordinates as array
+ */
+ public Point3d[] getTerrainCoordinates(Point3d[] inRawPoints)
+ {
+ final int numNodes = _gridSize * _gridSize;
+ if (_gridSize <= 1 || inRawPoints == null || inRawPoints.length != numNodes) {return null;}
+ // Put these nodes into a new result array (repeating nodes as necessary)
+ final int resultSize = _gridSize * (_gridSize * 2 - 2);
+ Point3d[] result = new Point3d[resultSize];
+ final int numStrips = _gridSize - 1;
+ int resultIndex = 0;
+ for (int strip=0; strip<numStrips; strip++)
+ {
+ for (int col=0; col<_gridSize; col++)
+ {
+ int bottomNodeIndex = strip * _gridSize + col;
+ int topNodeIndex = bottomNodeIndex + _gridSize;
+ result[resultIndex++] = inRawPoints[bottomNodeIndex];
+ result[resultIndex++] = inRawPoints[topNodeIndex];
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Get the texture coordinates as an array
+ * @return texture coordinates as array
+ */
+ public TexCoord2f[] getTextureCoordinates()
+ {
+ if (_gridSize <= 1) {return null;}
+ final int numNodes = _gridSize * _gridSize;
+ final float gridStep = 1.0f / (_gridSize - 1);
+ // Build all the required nodes
+ TexCoord2f[] nodes = new TexCoord2f[numNodes];
+ for (int i=0; i<_gridSize; i++)
+ {
+ for (int j=0; j<_gridSize; j++)
+ {
+ nodes[j * _gridSize + i] = new TexCoord2f(gridStep * i, 1.0f - gridStep * j);
+ }
+ }
+ // Now put these nodes into a new result array (repeating nodes as necessary)
+ final int resultSize = _gridSize * (_gridSize * 2 - 2);
+ TexCoord2f[] result = new TexCoord2f[resultSize];
+ final int numStrips = _gridSize - 1;
+ int resultIndex = 0;
+ for (int strip=0; strip<numStrips; strip++)
+ {
+ for (int col=0; col<_gridSize; col++)
+ {
+ int bottomNodeIndex = strip * _gridSize + col;
+ int topNodeIndex = bottomNodeIndex + _gridSize;
+ result[resultIndex++] = nodes[bottomNodeIndex];
+ result[resultIndex++] = nodes[topNodeIndex];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return strip lengths as array
+ */
+ public int[] getStripLengths()
+ {
+ final int numStrips = _gridSize - 1;
+ final int nodesPerStrip = _gridSize * 2;
+ int[] result = new int[numStrips];
+ for (int i=0; i<numStrips; i++) {
+ result[i] = nodesPerStrip;
+ }
+ return result;
+ }
+
+ /**
+ * Create a grid of points in a new Track
+ * @param inDataTrack track from which the extents should be obtained
+ * @return Track containing all the points in the grid
+ */
+ public Track createGridTrack(Track inDataTrack)
+ {
+ // Work out the size of the current track
+ TrackExtents extents = new TrackExtents(inDataTrack);
+ extents.applySquareBorder();
+ DoubleRange xRange = extents.getXRange();
+ DoubleRange yRange = extents.getYRange();
+ // Create the array of points
+ final int numPoints = _gridSize * _gridSize;
+ final double xStep = xRange.getRange() / (_gridSize - 1);
+ final double yStep = yRange.getRange() / (_gridSize - 1);
+ DataPoint[] points = new DataPoint[numPoints];
+ for (int i=0; i<_gridSize; i++)
+ {
+ double pY = yRange.getMinimum() + i * yStep;
+ for (int j=0; j<_gridSize; j++)
+ {
+ // Create a new point with the appropriate lat and long, with no altitude
+ double pX = xRange.getMinimum() + j * xStep;
+ DataPoint point = new DataPoint(
+ new Latitude(MapUtils.getLatitudeFromY(pY), Coordinate.FORMAT_NONE),
+ new Longitude(MapUtils.getLongitudeFromX(pX), Coordinate.FORMAT_NONE),
+ null);
+ //System.out.println("Created point at " + point.getLatitude().output(Coordinate.FORMAT_DEG_MIN_SEC)
+ // + ", " + point.getLongitude().output(Coordinate.FORMAT_DEG_MIN_SEC));
+ points[i * _gridSize + j] = point;
+ }
+ }
+ // Put these into a new track
+ Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE};
+ Track grid = new Track(new FieldList(fields), points);
+ return grid;
+ }
+
+ /**
+ * Write the given terrain track out to an indexed png file
+ * @param inModel three-d data model with terrain
+ * @param inPngFile file to write to
+ */
+ public void writeHeightMap(ThreeDModel inModel, File inPngFile)
+ {
+ BufferedImage image = new BufferedImage(_gridSize, _gridSize, BufferedImage.TYPE_BYTE_INDEXED);
+ for (int y=0; y<_gridSize; y++)
+ {
+ for (int x=0; x<_gridSize; x++)
+ {
+ double heightValue = inModel.getScaledTerrainValue(y * _gridSize + x) * 256;
+ // Need to ask colour model what rgb to use for this index (a little round-the-houses)
+ image.setRGB(x, y, image.getColorModel().getRGB((int) heightValue));
+ }
+ }
+ try
+ {
+ ImageIO.write(image, "PNG", inPngFile);
+ }
+ catch (IOException ioe) {System.err.println(ioe.getClass().getName() + " - " + ioe.getMessage());}
+ }
+
+
+ /**
+ * Try to fix the voids in the given terrain track by averaging neighbour values where possible
+ * @param inTerrainTrack terrain track to fix
+ */
+ public void fixVoids(Track inTerrainTrack)
+ {
+ int numVoids = countVoids(inTerrainTrack);
+ if (numVoids == 0) {return;}
+ //System.out.println("Starting to fix, num voids = " + numVoids);
+ // Fix the holes which are surrounded on all four sides by non-holes
+ fixSingleHoles(inTerrainTrack);
+ //System.out.println("Fixed single holes, now num voids = " + countVoids(inTerrainTrack));
+ // Maybe there is something to do in the corners?
+ fixCornersAndEdges(inTerrainTrack);
+ //System.out.println("Fixed corners, now num voids = " + countVoids(inTerrainTrack));
+ // Now fix the bigger holes, which should fix everything left
+ fixBiggerHoles(inTerrainTrack);
+ final int numHolesLeft = countVoids(inTerrainTrack);
+ if (numHolesLeft > 0) {
+ System.out.println("Fixed bigger holes, now num voids = " + countVoids(inTerrainTrack));
+ }
+ }
+
+ /**
+ * @param inTerrainTrack terrain track
+ * @return number of voids (points without altitudes)
+ */
+ private static int countVoids(Track inTerrainTrack)
+ {
+ // DEBUG: Show state of voids first
+// final int gridSize = (int) Math.sqrt(inTerrainTrack.getNumPoints());
+// StringBuilder sb = new StringBuilder();
+// for (int i=0; i<inTerrainTrack.getNumPoints(); i++)
+// {
+// if ((i%gridSize) == 0) sb.append('\n');
+// if (inTerrainTrack.getPoint(i).hasAltitude()) {
+// sb.append('A');
+// } else {
+// sb.append(' ');
+// }
+// }
+// System.out.println("Voids:" + sb.toString());
+ // END DEBUG
+
+ int numVoids = 0;
+ if (inTerrainTrack != null)
+ {
+ for (int i=0; i<inTerrainTrack.getNumPoints(); i++) {
+ if (!inTerrainTrack.getPoint(i).hasAltitude()) {
+ numVoids++;
+ }
+ }
+ }
+ return numVoids;
+ }
+
+ /**
+ * Just deal with single holes surrounded by at least four direct neighbours
+ * @param inTerrainTrack terrain track to fix
+ */
+ private void fixSingleHoles(Track inTerrainTrack)
+ {
+ // Holes with neighbours in all directions
+ final int startIndex = 1, endIndex = _gridSize - 2;
+ for (int x = startIndex; x <= endIndex; x++)
+ {
+ for (int y = startIndex; y <= endIndex; y++)
+ {
+ int pIndex = x * _gridSize + y;
+ // Get the point and its neighbours
+ final DataPoint p = inTerrainTrack.getPoint(pIndex);
+ if (!p.hasAltitude())
+ {
+ final DataPoint pl = inTerrainTrack.getPoint(pIndex - 1);
+ final DataPoint pr = inTerrainTrack.getPoint(pIndex + 1);
+ final DataPoint pu = inTerrainTrack.getPoint(pIndex + _gridSize);
+ final DataPoint pd = inTerrainTrack.getPoint(pIndex - _gridSize);
+ // Check if the points are null??
+ if (pl == null || pr == null || pu == null || pd == null)
+ {
+ System.err.println("Woah. Got a null point in fixSingleHoles. x=" + x + ", y=" + y + ", grid=" + _gridSize);
+ System.err.println("index=" + pIndex);
+ if (pl == null) System.err.println("pl is null");
+ if (pr == null) System.err.println("pr is null");
+ if (pu == null) System.err.println("pu is null");
+ if (pd == null) System.err.println("pd is null");
+ continue;
+ }
+ // Check that all the neighbours have altitudes
+ if (pl.hasAltitude() && pr.hasAltitude() && pu.hasAltitude() && pd.hasAltitude())
+ {
+ // Now check the double-neighbours
+ final DataPoint pll = inTerrainTrack.getPoint(pIndex - 2);
+ final DataPoint prr = inTerrainTrack.getPoint(pIndex + 2);
+ final DataPoint puu = inTerrainTrack.getPoint(pIndex + 2 * _gridSize);
+ final DataPoint pdd = inTerrainTrack.getPoint(pIndex - 2 * _gridSize);
+
+ double altitude = 0.0;
+ if (pll != null && pll.hasAltitude() && prr != null && prr.hasAltitude()
+ && puu != null && puu.hasAltitude() && pdd != null && pdd.hasAltitude())
+ {
+ // Use the double-neighbours too to take into account the gradients
+ altitude = (
+ pl.getAltitude().getMetricValue() * 1.5
+ - pll.getAltitude().getMetricValue() * 0.5
+ + pr.getAltitude().getMetricValue() * 1.5
+ - prr.getAltitude().getMetricValue() * 0.5
+ + pd.getAltitude().getMetricValue() * 1.5
+ - pdd.getAltitude().getMetricValue() * 0.5
+ + pu.getAltitude().getMetricValue() * 1.5
+ - puu.getAltitude().getMetricValue() * 0.5) / 4.0;
+ }
+ else
+ {
+ // no double-neighbours, just use neighbours
+ altitude = (
+ pl.getAltitude().getMetricValue()
+ + pr.getAltitude().getMetricValue()
+ + pd.getAltitude().getMetricValue()
+ + pu.getAltitude().getMetricValue()) / 4.0;
+ }
+ // Set this altitude in the point
+ p.setFieldValue(Field.ALTITUDE, "" + altitude, false);
+ // force value to metres
+ p.getAltitude().reset(new Altitude((int) altitude, UnitSetLibrary.UNITS_METRES));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Try to fix the corners and edges, if they're blank
+ * @param inTerrainTrack terrain track
+ */
+ private void fixCornersAndEdges(Track inTerrainTrack)
+ {
+ fixCorner(inTerrainTrack, 0, 1, 1);
+ fixCorner(inTerrainTrack, _gridSize-1, -1, 1);
+ fixCorner(inTerrainTrack, (_gridSize-1)*_gridSize, 1, -1);
+ fixCorner(inTerrainTrack, _gridSize*_gridSize-1, -1, -1);
+ fixEdge(inTerrainTrack, 0, 1);
+ fixEdge(inTerrainTrack, _gridSize-1, _gridSize);
+ fixEdge(inTerrainTrack, (_gridSize-1)*_gridSize, -_gridSize);
+ fixEdge(inTerrainTrack, _gridSize*_gridSize-1, -1);
+ }
+
+ /**
+ * Fix a single corner by searching along adjacent edges and averaging the nearest neighbours
+ * @param inTerrainTrack terrain track
+ * @param inCornerIndex index of corner to fill
+ * @param inXinc increment in x direction (+1 or -1)
+ * @param inYinc increment in y direction (+1 or -1)
+ */
+ private void fixCorner(Track inTerrainTrack, int inCornerIndex, int inXinc, int inYinc)
+ {
+ DataPoint corner = inTerrainTrack.getPoint(inCornerIndex);
+ if (corner == null || corner.hasAltitude()) {return;}
+ // Corner hasn't got an altitude, we'll have to look for it
+ int sIndex1 = inCornerIndex, sIndex2 = inCornerIndex;
+ Altitude alt1 = null, alt2 = null;
+
+ for (int i=1; i<_gridSize && !corner.hasAltitude(); i++)
+ {
+ sIndex1 += inXinc;
+ sIndex2 += (inYinc * _gridSize);
+ // System.out.println("To fill corner " + inCornerIndex + ", looking at indexes " + sIndex1 + " and " + sIndex2);
+ if (alt1 == null)
+ {
+ DataPoint source1 = inTerrainTrack.getPoint(sIndex1);
+ if (source1 != null && source1.hasAltitude()) {alt1 = source1.getAltitude();}
+ }
+ if (alt2 == null)
+ {
+ DataPoint source2 = inTerrainTrack.getPoint(sIndex2);
+ if (source2 != null && source2.hasAltitude()) {alt2 = source2.getAltitude();}
+ }
+ // Can we average these?
+ if (alt1 != null && alt2 != null)
+ {
+ // System.out.println("Averaging values " + alt1.getMetricValue() + " and " + alt2.getMetricValue());
+ int newAltitude = (int) ((alt1.getMetricValue() + alt2.getMetricValue()) / 2.0);
+ corner.setFieldValue(Field.ALTITUDE, "" + newAltitude, false);
+ }
+ }
+ }
+
+ /**
+ * Fix any holes found in the specified edge
+ * @param inTerrainTrack terrain track
+ * @param inCornerIndex index of corner to start from
+ * @param inInc increment along edge
+ */
+ private void fixEdge(Track inTerrainTrack, int inCornerIndex, int inInc)
+ {
+ int prevIndexWithAlt = -1;
+ int sIndex = inCornerIndex;
+ if (inTerrainTrack.getPoint(sIndex).hasAltitude()) {prevIndexWithAlt = 0;}
+ for (int i=1; i<_gridSize; i++)
+ {
+ sIndex += inInc;
+ if (inTerrainTrack.getPoint(sIndex).hasAltitude())
+ {
+ if (prevIndexWithAlt >= 0 && prevIndexWithAlt < (i-1))
+ {
+ final int gapLen = i - prevIndexWithAlt;
+ final double alt1 = inTerrainTrack.getPoint(prevIndexWithAlt).getAltitude().getMetricValue();
+ final double alt2 = inTerrainTrack.getPoint(i).getAltitude().getMetricValue();
+ for (int j = 1; j < gapLen; j++)
+ {
+ // System.out.println("Fill in " + (prevIndexWithAlt + j) + " using " + prevIndexWithAlt + " and " + i);
+ final double alt = alt1 + (alt2-alt1) * j / gapLen;
+ final DataPoint p = inTerrainTrack.getPoint(inCornerIndex + (prevIndexWithAlt + j) * inInc);
+ p.setFieldValue(Field.ALTITUDE, "" + (int) alt, false);
+ }
+ }
+ prevIndexWithAlt = i;
+ }
+ }
+ }
+
+ /**
+ * Try to fix bigger holes by interpolating between neighbours
+ * @param inTerrainTrack terrain track
+ */
+ private void fixBiggerHoles(Track inTerrainTrack)
+ {
+ double[] altitudes = new double[inTerrainTrack.getNumPoints()];
+ for (int i=0; i<_gridSize; i++)
+ {
+ int prevHoriz = -1, prevVert = -1;
+ for (int j=0; j<_gridSize; j++)
+ {
+ if (inTerrainTrack.getPoint(i * _gridSize + j).hasAltitude())
+ {
+ if (prevHoriz > -1 && prevHoriz != (j-1))
+ {
+// System.out.println("Found a gap for y=" + i +" between x=" + prevHoriz + " and " + j + " (" + (j-prevHoriz-1) + ")");
+ double startVal = inTerrainTrack.getPoint(i * _gridSize + prevHoriz).getAltitude().getMetricValue();
+ double endVal = inTerrainTrack.getPoint(i * _gridSize + j).getAltitude().getMetricValue();
+ for (int k=prevHoriz + 1; k< j; k++)
+ {
+ double val = startVal + (k-prevHoriz) * (endVal-startVal) / (j-prevHoriz);
+ if (altitudes[i * _gridSize + k] > 0.0) {
+ altitudes[i * _gridSize + k] = (altitudes[i * _gridSize + k] + val) / 2.0;
+ }
+ else {
+ altitudes[i * _gridSize + k] = val;
+ }
+ }
+ }
+ prevHoriz = j;
+ }
+ if (inTerrainTrack.getPoint(j * _gridSize + i).hasAltitude())
+ {
+ if (prevVert > -1 && prevVert != (j-1))
+ {
+// System.out.println("Found a gap for x=" + i +" between y=" + prevVert + " and " + j + " (" + (j-prevVert-1) + ")");
+ double startVal = inTerrainTrack.getPoint(prevVert * _gridSize + i).getAltitude().getMetricValue();
+ double endVal = inTerrainTrack.getPoint(j * _gridSize + i).getAltitude().getMetricValue();
+ for (int k=prevVert + 1; k< j; k++)
+ {
+ double val = startVal + (k-prevVert) * (endVal-startVal) / (j-prevVert);
+ if (altitudes[k * _gridSize + i] > 0.0) {
+ altitudes[k * _gridSize + i] = (altitudes[k * _gridSize + i] + val) / 2.0;
+ }
+ else {
+ altitudes[k * _gridSize + i] = val;
+ }
+ }
+ }
+ prevVert = j;
+ }
+ }
+ }
+ // Now the doubles have been set and/or averaged, we can set the values in the points
+ for (int i=0; i<inTerrainTrack.getNumPoints(); i++)
+ {
+ DataPoint p = inTerrainTrack.getPoint(i);
+ if (!p.hasAltitude() && altitudes[i] > 0.0)
+ {
+ p.setFieldValue(Field.ALTITUDE, "" + altitudes[i], false);
+ p.getAltitude().reset(new Altitude((int) altitudes[i], UnitSetLibrary.UNITS_METRES));
+ }
+ }
+ }
+}
diff --git a/tim/prune/threedee/ThreeDModel.java b/tim/prune/threedee/ThreeDModel.java
index ddf9162..abe166c 100644
--- a/tim/prune/threedee/ThreeDModel.java
+++ b/tim/prune/threedee/ThreeDModel.java
@@ -12,6 +12,7 @@ import tim.prune.data.Track;
public class ThreeDModel
{
private Track _track = null;
+ private Track _terrainTrack = null;
private PointScaler _scaler = null;
private double _scaleFactor = 1.0;
private double _altFactor = 1.0;
@@ -37,6 +38,14 @@ public class ThreeDModel
/**
+ * @param inTrack terrain track to set
+ */
+ public void setTerrain(Track inTrack)
+ {
+ _terrainTrack = inTrack;
+ }
+
+ /**
* @return the number of points in the model
*/
public int getNumPoints()
@@ -50,9 +59,7 @@ public class ThreeDModel
*/
public void setAltitudeFactor(double inFactor)
{
- if (inFactor >= 1.0) {
- _altFactor = inFactor;
- }
+ _altFactor = inFactor;
}
/**
@@ -70,6 +77,7 @@ public class ThreeDModel
{
// Use PointScaler to sort out x and y values
_scaler = new PointScaler(_track);
+ _scaler.addTerrain(_terrainTrack);
_scaler.scale(); // Add 10% border
// cap altitude scale factor if it's too big
@@ -77,7 +85,7 @@ public class ThreeDModel
if (maxAlt > 0.5)
{
// capped
- //System.out.println("Capped alt factor from " + _altFactor + " to " + (_altFactor * 0.5 / maxAlt));
+ // System.out.println("Capped alt factor from " + _altFactor + " to " + (_altFactor * 0.5 / maxAlt));
_altFactor = _altFactor * 0.5 / maxAlt;
}
@@ -134,13 +142,48 @@ public class ThreeDModel
{
// if no altitude, just return 0
double altVal = _scaler.getAltValue(inIndex);
- if (altVal < 0) return 0;
+ if (altVal <= 0.0) return 0.0;
// scale according to exaggeration factor
return altVal * _altFactor * _externalScaleFactor;
}
/**
+ * Get the scaled horizontal value for the specified terrain point
+ * @param inIndex index of point
+ * @return scaled horizontal value
+ */
+ public double getScaledTerrainHorizValue(int inIndex)
+ {
+ return _scaler.getTerrainHorizValue(inIndex) * _scaleFactor * _externalScaleFactor;
+ }
+
+ /**
+ * Get the scaled vertical value for the specified terrain point
+ * @param inIndex index of point
+ * @return scaled vertical value
+ */
+ public double getScaledTerrainVertValue(int inIndex)
+ {
+ return _scaler.getTerrainVertValue(inIndex) * _scaleFactor * _externalScaleFactor;
+ }
+
+ /**
+ * Get the scaled altitude value for the specified terrain point
+ * @param inIndex index of point
+ * @return scaled altitude value
+ */
+ public double getScaledTerrainValue(int inIndex)
+ {
+ // if no altitude, just return 0
+ double altVal = _scaler.getTerrainAltValue(inIndex);
+ if (altVal <= 0.0) return 0.0;
+ // don't scale by scale factor, needs to be unscaled
+ return altVal * _altFactor;
+ }
+
+
+ /**
* @param inIndex index of point, starting at 0
* @return point type, either POINT_TYPE_WAYPOINT or POINT_TYPE_NORMAL_POINT
*/
diff --git a/tim/prune/threedee/ThreeDWindow.java b/tim/prune/threedee/ThreeDWindow.java
index df72bc7..f4b3ed3 100644
--- a/tim/prune/threedee/ThreeDWindow.java
+++ b/tim/prune/threedee/ThreeDWindow.java
@@ -1,5 +1,6 @@
package tim.prune.threedee;
+import tim.prune.DataStatus;
import tim.prune.data.Track;
/**
@@ -14,6 +15,25 @@ public interface ThreeDWindow
*/
public void setTrack(Track inTrack);
+ /**
+ * @param inFactor altitude factor to use
+ */
+ public void setAltitudeFactor(double inFactor);
+
+ /**
+ * @param inDefinition image definition (image or not, source, zoom)
+ */
+ public void setBaseImageParameters(ImageDefinition inDefinition);
+
+ /**
+ * @param inDefinition terrain definition (terrain or not, resolution)
+ */
+ public void setTerrainParameters(TerrainDefinition inDefinition);
+
+ /**
+ * @param inStatus current data status for caching
+ */
+ public void setDataStatus(DataStatus inStatus);
/**
* Show the window
diff --git a/tim/prune/threedee/WindowFactory.java b/tim/prune/threedee/WindowFactory.java
index 549c11a..42ff8a4 100644
--- a/tim/prune/threedee/WindowFactory.java
+++ b/tim/prune/threedee/WindowFactory.java
@@ -45,9 +45,13 @@ public abstract class WindowFactory
{
// no java3d classes available
}
+ catch (NoClassDefFoundError nfe)
+ {
+ // no java3d classes available
+ }
catch (UnsatisfiedLinkError ule)
{
- // java3d available but somehow incompatible?
+ // java3d classes found but no native components
}
return has3d;
}
diff --git a/tim/prune/tips/TipDefinition.java b/tim/prune/tips/TipDefinition.java
new file mode 100644
index 0000000..c58fe5c
--- /dev/null
+++ b/tim/prune/tips/TipDefinition.java
@@ -0,0 +1,66 @@
+package tim.prune.tips;
+
+/**
+ * Definition of a tip, including key and whether the tip
+ * has already been shown or not.
+ * This class is only visible within this package
+ */
+class TipDefinition
+{
+ /** Key of message to show when fired */
+ private String _messageKey = null;
+ /** Threshold of calls before tip is shown */
+ private int _threshold = 0;
+ /** Number of times this tip has been hit */
+ private int _hitCount = 0;
+ /** Flag whether tip is active or has already been shown */
+ private boolean _active = true;
+
+ /**
+ * Constructor
+ * @param inKey key for message to show
+ */
+ TipDefinition(String inKey)
+ {
+ this(inKey, 0);
+ }
+
+ /**
+ * Constructor
+ * @param inKey message key
+ * @param inThreshold threshold
+ */
+ TipDefinition(String inKey, int inThreshold)
+ {
+ _messageKey = inKey;
+ _threshold = inThreshold;
+ }
+
+ /**
+ * Hit this definition and check the threshold
+ * @return true if the message should be shown
+ */
+ boolean shouldShowMessage()
+ {
+ if (_active)
+ {
+ boolean overThreshold = (_hitCount >= _threshold);
+ if (!overThreshold) {
+ _hitCount++;
+ }
+ else {
+ _active = false; // only fire once
+ }
+ return overThreshold;
+ }
+ // not active
+ return false;
+ }
+
+ /**
+ * @return message key
+ */
+ String getMessageKey() {
+ return _messageKey;
+ }
+}
diff --git a/tim/prune/tips/TipManager.java b/tim/prune/tips/TipManager.java
new file mode 100644
index 0000000..24c12bc
--- /dev/null
+++ b/tim/prune/tips/TipManager.java
@@ -0,0 +1,45 @@
+package tim.prune.tips;
+
+/**
+ * Class to manage the showing of tips according
+ * to the fixed TipDefinitions
+ */
+public abstract class TipManager
+{
+ public static final int Tip_UseAMapCache = 0;
+ public static final int Tip_LearnTimeParams = 1;
+ public static final int Tip_DownloadSrtm = 2;
+ public static final int Tip_UseSrtmFor3d = 3;
+ public static final int Tip_ManuallyCorrelateOne = 4;
+ private static final int Number_Tips = Tip_ManuallyCorrelateOne + 1;
+
+ /** Array of tip definitions */
+ private static TipDefinition[] TIPDEFS = new TipDefinition[Number_Tips];
+
+ /** Static block to initialise tip definitions */
+ static
+ {
+ TIPDEFS[Tip_UseAMapCache] = new TipDefinition("tip.useamapcache", 150);
+ TIPDEFS[Tip_LearnTimeParams] = new TipDefinition("tip.learntimeparams");
+ TIPDEFS[Tip_DownloadSrtm] = new TipDefinition("tip.downloadsrtm", 5);
+ TIPDEFS[Tip_UseSrtmFor3d] = new TipDefinition("tip.usesrtmfor3d");
+ TIPDEFS[Tip_ManuallyCorrelateOne] = new TipDefinition("tip.manuallycorrelateone");
+ }
+
+ /**
+ * Fire a trigger for the specified tip and get the message key if tip should be shown
+ * @param inTipNumber number of tip from constants
+ * @return message key if a message should be shown, or null otherwise
+ */
+ public static String fireTipTrigger(int inTipNumber)
+ {
+ try {
+ TipDefinition tip = TIPDEFS[inTipNumber];
+ if (tip.shouldShowMessage()) {
+ return tip.getMessageKey();
+ }
+ }
+ catch (ArrayIndexOutOfBoundsException obe) {} // unrecognised tip given
+ return null;
+ }
+}
diff --git a/tim/prune/undo/UndoAddAltitudeOffset.java b/tim/prune/undo/UndoAddAltitudeOffset.java
index 9a9f7a4..ffaabdd 100644
--- a/tim/prune/undo/UndoAddAltitudeOffset.java
+++ b/tim/prune/undo/UndoAddAltitudeOffset.java
@@ -57,8 +57,7 @@ public class UndoAddAltitudeOffset implements UndoOperation
for (int i=0; i<numPoints; i++)
{
DataPoint point = inTrackInfo.getTrack().getPoint(i+_startIndex);
- point.getAltitude().reset(_altitudes[i]);
- point.setModified(true);
+ point.resetAltitude(_altitudes[i]);
}
_altitudes = null;
inTrackInfo.getSelection().markInvalid();
diff --git a/tim/prune/undo/UndoCutAndMove.java b/tim/prune/undo/UndoCutAndMove.java
index 4287bab..2a6f9ca 100644
--- a/tim/prune/undo/UndoCutAndMove.java
+++ b/tim/prune/undo/UndoCutAndMove.java
@@ -89,6 +89,7 @@ public class UndoCutAndMove implements UndoOperation
if (_moveTrackPoint != null) {
_moveTrackPoint.setSegmentStart(_moveToSegmentFlag);
}
+ inTrackInfo.getSelection().clearAll();
UpdateMessageBroker.informSubscribers();
}
}
diff --git a/tim/prune/undo/UndoDeleteAudio.java b/tim/prune/undo/UndoDeleteAudio.java
index 0a49732..2b69865 100644
--- a/tim/prune/undo/UndoDeleteAudio.java
+++ b/tim/prune/undo/UndoDeleteAudio.java
@@ -9,7 +9,7 @@ import tim.prune.data.TrackInfo;
/**
* Operation to undo a delete of a single audio item, either with or without point
*/
-public class UndoDeleteAudio implements UndoOperation
+public class UndoDeleteAudio extends UndoDeleteOperation
{
private int _audioIndex = -1;
private AudioClip _audio = null;
@@ -55,6 +55,8 @@ public class UndoDeleteAudio implements UndoOperation
if (!inTrackInfo.getTrack().insertPoint(_point, _pointIndex)) {
throw new UndoException(getDescription());
}
+ // Change the current point/range selection if required
+ modifySelection(inTrackInfo, _pointIndex, _pointIndex);
}
else
{
diff --git a/tim/prune/undo/UndoDeleteOperation.java b/tim/prune/undo/UndoDeleteOperation.java
new file mode 100644
index 0000000..d99a9f6
--- /dev/null
+++ b/tim/prune/undo/UndoDeleteOperation.java
@@ -0,0 +1,38 @@
+package tim.prune.undo;
+
+import tim.prune.data.TrackInfo;
+
+/**
+ * Abstract class to hold the selection handling required by all
+ * Undo operations which have undeleted something
+ */
+public abstract class UndoDeleteOperation implements UndoOperation
+{
+ /**
+ * Modify the current point/range selection after the delete operation is undone
+ * @param inTrackInfo track info object
+ * @param inStartIndex start index of reinserted range
+ * @param inEndIndex end index of reinserted range
+ */
+ protected static void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex)
+ {
+ final int numPointsInserted = inEndIndex - inStartIndex + 1;
+ // See if there is a currently selected point, if so does it need to be modified
+ final int currentPoint = inTrackInfo.getSelection().getCurrentPointIndex();
+ if (currentPoint >= inStartIndex)
+ {
+ inTrackInfo.selectPoint(currentPoint + numPointsInserted);
+ }
+ // Same for currently selected range
+ int rangeStart = inTrackInfo.getSelection().getStart();
+ int rangeEnd = inTrackInfo.getSelection().getEnd();
+ if (rangeEnd >= inStartIndex && rangeEnd > rangeStart)
+ {
+ rangeEnd += numPointsInserted;
+ if (rangeStart >= inStartIndex) {
+ rangeStart += numPointsInserted;
+ }
+ inTrackInfo.getSelection().selectRange(rangeStart, rangeEnd);
+ }
+ }
+}
diff --git a/tim/prune/undo/UndoDeletePhoto.java b/tim/prune/undo/UndoDeletePhoto.java
index 5edd37b..45be6ad 100644
--- a/tim/prune/undo/UndoDeletePhoto.java
+++ b/tim/prune/undo/UndoDeletePhoto.java
@@ -9,7 +9,7 @@ import tim.prune.data.TrackInfo;
/**
* Operation to undo a delete of a single photo, either with or without point
*/
-public class UndoDeletePhoto implements UndoOperation
+public class UndoDeletePhoto extends UndoDeleteOperation
{
private int _photoIndex = -1;
private Photo _photo = null;
@@ -58,6 +58,8 @@ public class UndoDeletePhoto implements UndoOperation
{
throw new UndoException(getDescription());
}
+ // Change the current point/range selection if required
+ modifySelection(inTrackInfo, _pointIndex, _pointIndex);
}
else
{
diff --git a/tim/prune/undo/UndoDeletePoint.java b/tim/prune/undo/UndoDeletePoint.java
index 9919d0d..beddfbf 100644
--- a/tim/prune/undo/UndoDeletePoint.java
+++ b/tim/prune/undo/UndoDeletePoint.java
@@ -7,7 +7,7 @@ import tim.prune.data.TrackInfo;
/**
* Operation to undo a delete of a single point
*/
-public class UndoDeletePoint implements UndoOperation
+public class UndoDeletePoint extends UndoDeleteOperation
{
private int _pointIndex = -1;
private DataPoint _point = null;
@@ -89,5 +89,7 @@ public class UndoDeletePoint implements UndoOperation
nextTrackPoint.setSegmentStart(false);
}
}
+ // If there's a current point or range selected, maybe need to adjust start and/or end
+ modifySelection(inTrackInfo, _pointIndex, _pointIndex);
}
}
diff --git a/tim/prune/undo/UndoDeleteRange.java b/tim/prune/undo/UndoDeleteRange.java
index 58f09e9..da42bc4 100644
--- a/tim/prune/undo/UndoDeleteRange.java
+++ b/tim/prune/undo/UndoDeleteRange.java
@@ -9,7 +9,7 @@ import tim.prune.data.TrackInfo;
/**
* Operation to undo a delete of a range of points
*/
-public class UndoDeleteRange implements UndoOperation
+public class UndoDeleteRange extends UndoDeleteOperation
{
/**
* Inner class to hold a single range information set
@@ -40,6 +40,14 @@ public class UndoDeleteRange implements UndoOperation
{
return _startIndex >= 0 && _points != null && _points.length > 0;
}
+
+ /**
+ * @return end index of range
+ */
+ public int getEndIndex()
+ {
+ return _startIndex + _points.length - 1;
+ }
}
@@ -141,6 +149,13 @@ public class UndoDeleteRange implements UndoOperation
// Undo both the ranges
performUndo(inTrackInfo, _rangeInfo1);
performUndo(inTrackInfo, _rangeInfo2);
+ // If there's a current point/range selected, maybe need to adjust start and/or end
+ if (_rangeInfo1 != null && _rangeInfo1.isValid()) {
+ modifySelection(inTrackInfo, _rangeInfo1._startIndex, _rangeInfo1.getEndIndex());
+ }
+ if (_rangeInfo2 != null && _rangeInfo2.isValid()) {
+ modifySelection(inTrackInfo, _rangeInfo2._startIndex, _rangeInfo2.getEndIndex());
+ }
}
/**
diff --git a/tim/prune/undo/UndoReorder.java b/tim/prune/undo/UndoReorder.java
index e1d5539..38c460e 100644
--- a/tim/prune/undo/UndoReorder.java
+++ b/tim/prune/undo/UndoReorder.java
@@ -42,5 +42,6 @@ public abstract class UndoReorder implements UndoOperation
{
// restore track to previous values
inTrackInfo.getTrack().replaceContents(_contents);
+ inTrackInfo.getSelection().clearAll();
}
-}
\ No newline at end of file
+}
diff --git a/tim/prune/undo/UndoSewSegments.java b/tim/prune/undo/UndoSewSegments.java
new file mode 100644
index 0000000..fbcf853
--- /dev/null
+++ b/tim/prune/undo/UndoSewSegments.java
@@ -0,0 +1,44 @@
+package tim.prune.undo;
+
+import tim.prune.data.DataPoint;
+import tim.prune.data.Track;
+import tim.prune.data.TrackInfo;
+
+/**
+ * Operation to undo the sewing together of track segments
+ */
+public class UndoSewSegments extends UndoReorder
+{
+ /** All segment start flags need to be remembered as well */
+ private boolean[] _segmentStartFlags = null;
+
+ /**
+ * Constructor
+ * @param inTrack track contents to copy
+ */
+ public UndoSewSegments(Track inTrack)
+ {
+ super(inTrack, "undo.sewsegments");
+ // Also remember segment start flags, as they may have been changed by reversals
+ final int numPoints = inTrack.getNumPoints();
+ _segmentStartFlags = new boolean[numPoints];
+ for (int i=0; i<numPoints; i++) {
+ _segmentStartFlags[i] = inTrack.getPoint(i).getSegmentStart();
+ }
+ }
+
+ /** Perform the undo */
+ public void performUndo(TrackInfo inTrackInfo) throws UndoException
+ {
+ // Put all the points back in the right order
+ super.performUndo(inTrackInfo);
+ // And then restore the segment flags
+ for (int i=0; i<_segmentStartFlags.length; i++)
+ {
+ DataPoint point = inTrackInfo.getTrack().getPoint(i);
+ if (point != null && !point.isWaypoint()) {
+ point.setSegmentStart(_segmentStartFlags[i]);
+ }
+ }
+ }
+}
diff --git a/tim/prune/undo/UndoSplitSegments.java b/tim/prune/undo/UndoSplitSegments.java
new file mode 100644
index 0000000..85968b7
--- /dev/null
+++ b/tim/prune/undo/UndoSplitSegments.java
@@ -0,0 +1,23 @@
+package tim.prune.undo;
+
+import tim.prune.I18nManager;
+import tim.prune.data.Track;
+
+/**
+ * Undo splitting of track segments
+ */
+public class UndoSplitSegments extends UndoMergeTrackSegments
+{
+ /** Constructor */
+ public UndoSplitSegments(Track inTrack) {
+ super(inTrack, 0, inTrack.getNumPoints()-1);
+ }
+
+ /**
+ * @return description of operation
+ */
+ public String getDescription()
+ {
+ return I18nManager.getText("undo.splitsegments");
+ }
+}
diff --git a/tim/prune/undo/UndoStack.java b/tim/prune/undo/UndoStack.java
new file mode 100644
index 0000000..bca92b1
--- /dev/null
+++ b/tim/prune/undo/UndoStack.java
@@ -0,0 +1,24 @@
+package tim.prune.undo;
+
+import java.util.Stack;
+
+/**
+ * Stack of undo operations
+ * which also remembers how many times it's been cleared
+ */
+public class UndoStack extends Stack<UndoOperation>
+{
+ private int _numTimesDeleted = 0;
+
+ /** @return number of times this stack has been deleted */
+ public int getNumTimesDeleted() {
+ return _numTimesDeleted;
+ }
+
+ @Override
+ public void clear()
+ {
+ _numTimesDeleted++;
+ super.clear();
+ }
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/gpsprune.git
More information about the Pkg-grass-devel
mailing list