From 326f489e36aa7f235bc19409a57bf4955cd50f24 Mon Sep 17 00:00:00 2001 From: activityworkshop Date: Sun, 15 Feb 2015 17:05:07 +0100 Subject: [PATCH] Version 16, February 2014 --- tim/prune/App.java | 21 +- 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 | 26 + 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 | 17 +- tim/prune/function/AddMapSourceDialog.java | 12 +- 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 +- .../function/GetWikipediaXmlHandler.java | 4 +- tim/prune/function/MapSourceListModel.java | 4 +- tim/prune/function/PasteCoordinates.java | 4 +- tim/prune/function/SelectTracksFunction.java | 4 +- tim/prune/function/SetLanguage.java | 8 +- tim/prune/function/SetMapBgFunction.java | 4 +- tim/prune/function/ShowThreeDFunction.java | 124 ++++- .../function/browser/BrowserLauncher.java | 11 +- .../function/cache/ManageCacheFunction.java | 3 +- .../compress/CompressTrackFunction.java | 28 +- .../MarkPointsInRectangleFunction.java | 35 +- tim/prune/function/estimate/EstimateTime.java | 16 +- .../estimate/EstimationParameters.java | 13 + .../function/estimate/LearnParameters.java | 2 +- tim/prune/function/sew/CandidateSorter.java | 29 ++ tim/prune/function/sew/SegmentEnd.java | 197 +++++++ .../sew/SewTrackSegmentsFunction.java | 331 ++++++++++++ tim/prune/function/sew/SplitPoint.java | 101 ++++ .../function/sew/SplitSegmentsFunction.java | 285 +++++++++++ .../function/srtm/DownloadSrtmFunction.java | 217 ++++++++ .../function/srtm/LookupSrtmFunction.java | 207 ++++++-- tim/prune/function/srtm/SrtmTile.java | 13 +- .../weather/GetWeatherForecastFunction.java | 480 ++++++++++++++++++ tim/prune/function/weather/IconRenderer.java | 37 ++ .../function/weather/OWMCurrentHandler.java | 92 ++++ .../function/weather/OWMForecastHandler.java | 102 ++++ tim/prune/function/weather/ResultSet.java | 83 +++ .../function/weather/SingleForecast.java | 185 +++++++ .../function/weather/WeatherResults.java | 133 +++++ .../function/weather/WeatherTableModel.java | 125 +++++ tim/prune/gui/BaseImageDefinitionPanel.java | 157 ++++++ tim/prune/gui/DecimalNumberField.java | 3 +- tim/prune/gui/DetailsDisplay.java | 17 +- tim/prune/gui/GenericProgressDialog.java | 117 +++++ 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/add_photo_icon.png | Bin tim/prune/gui/images/add_textfile_icon.png | Bin 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/DiskTileCacher.java | 13 +- tim/prune/gui/map/MapCanvas.java | 55 +- tim/prune/gui/map/MapTileManager.java | 110 ++-- tim/prune/gui/map/TileConsumer.java | 10 + tim/prune/gui/map/TileDownloader.java | 3 + tim/prune/lang/prune-texts_af.properties | 11 +- tim/prune/lang/prune-texts_cz.properties | 75 ++- tim/prune/lang/prune-texts_de.properties | 73 ++- tim/prune/lang/prune-texts_de_CH.properties | 91 +++- tim/prune/lang/prune-texts_en.properties | 71 ++- tim/prune/lang/prune-texts_es.properties | 55 +- tim/prune/lang/prune-texts_fr.properties | 45 +- tim/prune/lang/prune-texts_hu.properties | 179 ++++++- tim/prune/lang/prune-texts_it.properties | 63 ++- tim/prune/lang/prune-texts_ja.properties | 22 +- tim/prune/lang/prune-texts_ko.properties | 23 +- tim/prune/lang/prune-texts_nl.properties | 73 ++- tim/prune/lang/prune-texts_pl.properties | 49 +- tim/prune/lang/prune-texts_pt.properties | 181 ++++++- tim/prune/lang/prune-texts_ro.properties | 97 +++- tim/prune/lang/prune-texts_ru.properties | 24 +- tim/prune/lang/prune-texts_sv.properties | 58 +++ tim/prune/lang/prune-texts_tr.properties | 17 +- tim/prune/lang/prune-texts_uk.properties | 234 +++++++++ tim/prune/lang/prune-texts_zh.properties | 89 +++- tim/prune/load/BabelFileFormats.java | 2 +- tim/prune/load/BabelLoadFromFile.java | 10 +- tim/prune/load/FileCacher.java | 2 +- 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 | 27 +- tim/prune/save/BaseImageConfigDialog.java | 197 ++++--- tim/prune/save/BaseImageConsumer.java | 10 + tim/prune/save/ExifSaver.java | 9 +- tim/prune/save/GpxExporter.java | 1 + tim/prune/save/GroutedImage.java | 7 - tim/prune/save/ImageExporter.java | 139 ++--- tim/prune/save/MapGrouter.java | 69 ++- tim/prune/save/PovExporter.java | 241 +++++---- tim/prune/save/SvgExporter.java | 9 +- tim/prune/threedee/ImageDefinition.java | 66 +++ tim/prune/threedee/Java3DWindow.java | 191 ++++++- tim/prune/threedee/TerrainDefinition.java | 54 ++ tim/prune/threedee/TerrainHelper.java | 423 +++++++++++++++ tim/prune/threedee/ThreeDModel.java | 53 +- tim/prune/threedee/ThreeDWindow.java | 14 + 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 + 144 files changed, 6432 insertions(+), 1037 deletions(-) create mode 100644 tim/prune/function/sew/CandidateSorter.java create mode 100644 tim/prune/function/sew/SegmentEnd.java create mode 100644 tim/prune/function/sew/SewTrackSegmentsFunction.java create mode 100644 tim/prune/function/sew/SplitPoint.java create mode 100644 tim/prune/function/sew/SplitSegmentsFunction.java create mode 100644 tim/prune/function/srtm/DownloadSrtmFunction.java create mode 100644 tim/prune/function/weather/GetWeatherForecastFunction.java create mode 100644 tim/prune/function/weather/IconRenderer.java create mode 100644 tim/prune/function/weather/OWMCurrentHandler.java create mode 100644 tim/prune/function/weather/OWMForecastHandler.java create mode 100644 tim/prune/function/weather/ResultSet.java create mode 100644 tim/prune/function/weather/SingleForecast.java create mode 100644 tim/prune/function/weather/WeatherResults.java create mode 100644 tim/prune/function/weather/WeatherTableModel.java create mode 100644 tim/prune/gui/BaseImageDefinitionPanel.java create mode 100644 tim/prune/gui/GenericProgressDialog.java create mode 100644 tim/prune/gui/TerrainDefinitionPanel.java mode change 100755 => 100644 tim/prune/gui/images/add_photo_icon.png mode change 100755 => 100644 tim/prune/gui/images/add_textfile_icon.png create mode 100644 tim/prune/gui/images/weather-clear-day.png create mode 100644 tim/prune/gui/images/weather-clear-night.png create mode 100644 tim/prune/gui/images/weather-clouds-day.png create mode 100644 tim/prune/gui/images/weather-clouds-night.png create mode 100644 tim/prune/gui/images/weather-clouds.png create mode 100644 tim/prune/gui/images/weather-extreme.png create mode 100644 tim/prune/gui/images/weather-fog.png create mode 100644 tim/prune/gui/images/weather-hail.png create mode 100644 tim/prune/gui/images/weather-lightrain.png create mode 100644 tim/prune/gui/images/weather-rain.png create mode 100644 tim/prune/gui/images/weather-snow.png create mode 100644 tim/prune/gui/images/weather-storm.png create mode 100644 tim/prune/gui/map/TileConsumer.java create mode 100644 tim/prune/lang/prune-texts_sv.properties create mode 100644 tim/prune/lang/prune-texts_uk.properties create mode 100644 tim/prune/save/BaseImageConsumer.java create mode 100644 tim/prune/threedee/ImageDefinition.java create mode 100644 tim/prune/threedee/TerrainDefinition.java create mode 100644 tim/prune/threedee/TerrainHelper.java create mode 100644 tim/prune/tips/TipDefinition.java create mode 100644 tim/prune/tips/TipManager.java create mode 100644 tim/prune/undo/UndoDeleteOperation.java create mode 100644 tim/prune/undo/UndoSewSegments.java create mode 100644 tim/prune/undo/UndoSplitSegments.java diff --git a/tim/prune/App.java b/tim/prune/App.java index 686b3de..71bd354 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.*; @@ -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 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 5cc5c90..0503dda 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.2"; + public static final String VERSION_NUMBER = "16"; /** Build number, just used for about screen */ - public static final String BUILD_NUMBER = "283b"; + public static final String BUILD_NUMBER = "301"; /** 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..655847c 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; /** @@ -80,6 +81,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 +145,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(); @@ -167,9 +172,30 @@ public abstract class Config 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 _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(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; } @@ -151,15 +150,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 */ @@ -169,14 +159,6 @@ public class Selection return _movingSeconds; } - /** - * @return distance of Selection in specified units - */ - public double getDistance() - { - return Distance.convertRadiansToDistance(_angDistance); - } - /** * @return moving distance of Selection in current units */ 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..70a9eb3 100644 --- a/tim/prune/function/AboutScreen.java +++ b/tim/prune/function/AboutScreen.java @@ -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 _oZoomCombo = null; // controls for cloudmade panel private JTextField _cNameField = null; private JTextField _cStyleField = null; - private JComboBox _cZoomCombo = null; + private JComboBox _cZoomCombo = null; private JButton _okButton = null; /** array of file types */ @@ -181,9 +181,9 @@ 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(); for (int i=10; i<=20; i++) { - _oZoomCombo.addItem("" + i); + _oZoomCombo.addItem(i); } // zoom dropdown needs listener to call enableOk() _oZoomCombo.addActionListener(okEnabler); @@ -208,9 +208,9 @@ public class AddMapSourceDialog _cStyleField.addKeyListener(keyListener); cloudGridPanel.add(_cStyleField); cloudGridPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.maxzoom"))); - _cZoomCombo = new JComboBox(); + _cZoomCombo = new JComboBox(); for (int i=10; i<=20; i++) { - _cZoomCombo.addItem("" + i); + _cZoomCombo.addItem(i); } cloudGridPanel.add(_cZoomCombo); cloudPanel.add(cloudGridPanel, BorderLayout.NORTH); 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 _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(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 { /** ArrayList containing fields */ private ArrayList _fields = new ArrayList(); @@ -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 _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(_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 { /** * @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 _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(altunits); grid.add(_altUnitsDropDown); // Waypoint name JLabel nameLabel = new JLabel(I18nManager.getText("dialog.pointnameedit.name")); 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 _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(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 _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(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 _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(_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..bbe7719 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 @@ -42,11 +68,107 @@ public class ShowThreeDFunction extends GenericFunction I18nManager.getText("error.function.notavailable.title"), JOptionPane.WARNING_MESSAGE); } 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.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 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/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 @@ -61,6 +61,19 @@ public class EstimationParameters _parseFailed = false; } + /** + * @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 +{ + /** + * 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 +{ + 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 _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 buildNodeList(Track inTrack) + { + TreeSet nodes = new TreeSet(); + final int numPoints = inTrack.getNumPoints(); + DataPoint prevTrackPoint = null; + int prevTrackPointIndex = -1; + SegmentEnd segmentStart = null; + for (int i=0; i 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 +{ + 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 _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(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 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..8517cd0 --- /dev/null +++ b/tim/prune/function/srtm/DownloadSrtmFunction.java @@ -0,0 +1,217 @@ +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 tileList = new ArrayList(); + + // 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 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 0) + { + 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 tileList = new ArrayList(); boolean hasZeroAltitudePoints = false; boolean hasNonZeroAltitudePoints = false; // First, loop to see what kind of points we have - for (int i=0; i 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= 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 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); @@ -225,6 +284,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) @@ -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 _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(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 _forecastList = new ArrayList(); + + + /** + * 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 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..83c5ca0 --- /dev/null +++ b/tim/prune/function/weather/WeatherResults.java @@ -0,0 +1,133 @@ +package tim.prune.function.weather; + +import java.util.ArrayList; + + +/** + * Model for results of weather forecast from openweathermap.org + */ +public class WeatherResults +{ + /** List of forecasts */ + private ArrayList _forecastList = new ArrayList(); + /** 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 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) + { + _sunriseTime = inRiseTime.substring(11, 16); + _sunsetTime = 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; + } +} diff --git a/tim/prune/function/weather/WeatherTableModel.java b/tim/prune/function/weather/WeatherTableModel.java new file mode 100644 index 0000000..274490e --- /dev/null +++ b/tim/prune/function/weather/WeatherTableModel.java @@ -0,0 +1,125 @@ +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("Wind", forecast.getWindDescription()); + case ROW_ICON: return forecast.getImageName(); + case ROW_TEMP: return buildDisplayString("Temp", forecast.getTemps() + + (_results.isCelsius() ? UNITS_DEGC : UNITS_DEGF)); + case ROW_HUMID: return buildDisplayString("Humidity", forecast.getHumidity()); + } + // TODO: Use language-specific texts for wind, temp and humidity + } + 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 "" + (inTitle == null ? "" : (inTitle + ": ")) + + "" + inValue.replaceAll(" ", " ") + ""; + } + + /** + * 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 _coordFormatDropdown = null; + private JComboBox _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(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(); final UnitSet currUnits = Config.getUnitSet(); for (int i=0; i 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/GenericProgressDialog.java b/tim/prune/gui/GenericProgressDialog.java new file mode 100644 index 0000000..acef147 --- /dev/null +++ b/tim/prune/gui/GenericProgressDialog.java @@ -0,0 +1,117 @@ +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; +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; + +/** + * Class to show a progress dialog for various time-consuming functions + */ +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; + + /** + * 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 GenericProgressDialog(String inTitleKey, String inLabelKey, + JFrame inParentFrame, Cancellable inFunction) + { + _dialogTitleKey = inTitleKey; + _labelKey = inLabelKey; + if (_labelKey == null) { + _labelKey = "confirm.running"; + } + _parentFrame = inParentFrame; + _function = inFunction; + } + + /** + * Create the dialog to show the progress + */ + private void createProgressDialog() + { + _progressDialog = new JDialog(_parentFrame, I18nManager.getText(_dialogTitleKey)); + _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(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) + { + _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(); + } +} 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 { /** 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 _waypointList = null; private WaypointListModel _waypointListModel = null; // Photos private JPanel _photoListPanel = null; - private JList _photoList = null; + private JList _photoList = null; private MediaListModel _photoListModel = null; // Audio files private JPanel _audioListPanel = null; - private JList _audioList = null; + private JList _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(_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(_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(_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 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 _actionList; /** @@ -52,7 +52,7 @@ public class UndoManager { undoActions[i] = undoStack.elementAt(undoStack.size()-1-i).getDescription(); } - _actionList = new JList(undoActions); + _actionList = new JList(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 { Track _track = null; ArrayList _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 { private ArrayList _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/add_photo_icon.png b/tim/prune/gui/images/add_photo_icon.png old mode 100755 new mode 100644 diff --git a/tim/prune/gui/images/add_textfile_icon.png b/tim/prune/gui/images/add_textfile_icon.png old mode 100755 new mode 100644 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 0000000000000000000000000000000000000000..d6d011d56dba44373d87f400bdcfe4612a5d3c28 GIT binary patch literal 3088 zcmV+r4Da)aP)ZO44F~JcG_;wcXxc*RsDbWo^$W1TcoP^NjCnp1$=dagZjKcme2O?hwK7`NXK8r9S z4ux15He?Qu9iVXJ0<>#v{)7L5H{RRal1dY$&U$SO4WJOPa*bZ|F|O86>CE%!*z*Vs zH_9J04j#_Ei+&R(`<_zA1rh|7Mc`vWpoDm^MpSM9WjHr7z|zP9|;LFD3mZ9s9Q_S z>@^8T>n^8;m)t5!MkqHTD5a&^>&`8oayt7@d(raT0F9eeXI~i+N)nIGn&*Li%4te; zemuJSqf)rk`WeaWI2;Da&kX>8imXFu$;Wif4n=Qe2jZKM0HDLiiSkq=U9`G3g9(ao zR`X8`pmmoNVPTfc%+XTfy*nA4Pp-1SJ6!%?<6E=C```E$|zht3{4^q=|3^80Y%x+0qjoTTd%zp#9TrjB0L?z3uo<{)05pqfQx zGzcmsPiab2PQpbTPw%$gKw3-i>hBWL!5X#a8F`Z-Wq_-I&j8l|kOAR7k5b+F&cX`4 z^*4&nT9#cH|Hr&M_g(8}mpN~B3_8uB`Rv}a&PF=}Oj5OFP%59-xrGU;2r5TJl9H!t zDqJch_uUu6>E>7Q`QP}Ej{nkmZA^uvF@Y-p2LK7SmKhJI1H1)%3yPLfb-(fVzjKq` z{kP-hVDjgkqw+nc%?|rkTWz*7aJDnGF+=Tn1Xd>pzs%9;BcOCaw6kor=|Eo6JMN3& zy!|8m;_od{Tt9%!gaAP~0x0a24G0wC)dFe&H6Wh@a6lYd?an@;|M~0wd_@1EeN^9O z*J?S}Y_o07^j*-Ej|Bh#joYuhLgDBICy|nJ6fLjqkAL|7AK+L2aFu*_6uwVXN&v?I zMZgBcMhz6geZH;E0iS_f7ob_E6i?BAe0`vOvi#L-D8I$7(PC;eDbH3jMjec5eID;U z$*p!xC9YhdAT#eRLZ?3np#+j3zJBAk^rv3CLt$;d`idxr05L%H|Le_edcrkgC=1m0 z3q|`5>ZM<}mEPu6Fa6hO~qkQGHO00)IasW;-(1LF{yxsZ;0;Udy~X>xb= zXMvPODqRHSZ`aS^PlnFw>`oeY1<<~&NAv}XeKwkKSB!aP6@ zAO|H2g?YS?-FDsgKouZBy#-o}fxi6f98NuYO<7v1ohxeE-<^IN;cgmt2KZ+Z?YIg_ zAS4{b5A)O5Hz-Kbnzzaz4~5?*+!s9$1Hcw_FK8b-hhE!F5D$=tw_u8rnpXQczI|1l zSzaXxMIw~<2JkW>l1PX}$VU>)bzjA?3v1{$xR_mv7Z)NNR4o*?IK~r3VEZ-QI)+~d z+#0|F&0373rxt_7>{VhBa)HEgn(hX$^Gdu9Ay&4eED+|*pP}K=J~b@?l?w52y`Jp_ zC8!q;z6jRsrVi>Bq2>XQ%|Puk$Tk3PK;8lxfs+>)gQEj{-JbRTJ#53qtLgc5^@p;U~P0tJL>u|^0|@(9U-h?H_@Z|+t9OaL?QP`x~G z^5>KA#E_zDiFm7x8fZ2NP#YQ!0GY?gDM4PUrsy*=@ zw4qOPfQh&?c9hNC`=+(r*h%~o0S^AOE&=%0#Yw6LeDDOx@F1warot;^TUAI>QuDCr zVF>{VwrVa4SO8{hE)Yy4ZAFV%6q z^-KU8W}0xEt{D)TqFsP=!RIYTke1ZBLLe8bDOk3|%LW8*1+o?bi{3AcbX(6WLp_5y z228tyAXrs(1~BXD^VoX4zIL77?K_We6^Yd|0E{+}FI96Fdgr`&=To`COS2Kx`fBL5 zWFt^PJvSoW1SufoF#sZ0_`52gelr%foAd?c(RwrB)rXa{y0DYZ zo!5cOYS~KLTW8^OXY1jcxO=ysql({}b!ZEMx#v1P2o9kVK_x1VH(g7LwzNI?i)V3= zdU`O^pVh)Qg}wFG+Qu4MJ8Ik&006sAL)ltSx#=DVZeqP@k$2`LuK6Na+khyJmA0Ui z1(1)72KCx}xpJ*(?2n;+o`bzSS(n9)n<>=~dhN7I{@5tkVylbhl4Kj8b< zgu{|04)Ay)PLJ0_&`tNEC$~Xw8YPAVfu(mQy{YjxJUY22XOb{mecDIO?mKgr)Wn)E zd$?TLyZr3uZX?e!JX)9t!jdU+xn5ER-Sls$2YCl_rYPQRo!{0TsvrP?i182CZfKTg z?bFGBi_P)??QC)tPyL{I>egaq8OxolorQE}6cSK&Zv5}*>IWw%>pTpY9aJ~x`R(H2 z?Ir-c{Ro7|#(g0>N5(Gj#DlTy%-G*DI+;f&2W3l_E~)w00JyBSRDS6IBpgsP_b97Q zeG`)ShFFSec@7!=b zgAUYLT&w#2(|aFyhEK}Y^~wm9&K)Lu?NQfrO}LtRuB4@VcsZKxyy4CaU6(`qE*VN9 z8&-H@T_{##+IUItquznMLul5V_?E%L#{Xhs^~L;9^k?~r1Ghk>acAbMi!}9@V`EF} zKNf&(O=7C0@LN5kaNSl*FD& z2?c3Xnsi@^R?P399Ed+uUZc8{DL`4FMJ})Cs-p}`u0d0cGBxvV-Y?&1kIT0hCiAA5 z33uAnzHAz!+Maob>Ic7x75wSWInW*f3Y&R{>Pe|GEF8ofB$0*(`z1a#7R<~Y4j+7Y zj641;d9A^t4gg>*X%w|wseIoLm%iPuFTCj~H)6BgYd6b{ZH)xondjYB+%o_G;2tF_ z?Q~cnl_)+v!tvONuDvr&*<6EI1SGvx3IL$W!<8^Rau|Mb-nD;t&yzDQZ|7luHs5yy zYrAEfyV;(%*Kt(41Hgm1t5l9+jM#j>P1%iUl0aZWCQ{n`KvO7maZRauz`JGu*I4Uj z+1g|StCwGVz8@MNwY`tPHe0@Z=9OBXUR#`;gsUPAkwzS8V7vxVNZuQ=5jwev#zw6P z4-B4}`1gac!2bap6hLXm*G($`0000Y$92Yk)m7Eqv$Na{m%A%5q$p9;g(4lurg^g*NEBr;f&%%NeMP!6nrgB5^hX?C%IPwf)aGC zP4KrO02{&E#&}PjxfcSeQdxmjY$PDYp1B=?rkZ;JbGIS@Jw~9`aqz)}=N%hEs0rLw z&lUU@-nd%=vH@OK*G&=u6FkuG_mz~=$8lVTN|Qh{d}KwUe!i&g-xmTk=;pY=3un)s zoq6w9?>@2G={%L?*{m_9<($)akWv&%>OmMqow+Sre!YL+!H;(C+}Y!%2PTD|;4VT+ z++iJ1KzJJh>SwS2?6I?Fesk11xB1D(pX8B89+fjQGsJO(k{ah7S(dT7*5T5n%Ur*{ zVwTS@3n|sg?%lgz|Ju=GpH)C>&8xAf^tL2!M?hBKYXT!Ajvqh1?Y;jw@lq7U3xEFA zud2nx2g&n{G)c(w9OoR)SwNt}5br!nD{gc;bUGct^T{V?jC20N{)2~JJFxG-Iey&eETAP^!z2r<-6GqZHN zYs7JkKyYs9b9%jX!YIU8>woj{X|wC$$Nudb-+1}d82BcM$v|&GARN;nk?;S@-ybWA z;^~);eO)2x}H?}xyHpafr!073|?bu2DE00P!~Jx>4nG=pS7tJNY5BhH-p$V_jV ze&?0%e(%jO;7uZTMWBxR4IcRZKmWr^VHiIB{E?$-ZFQA?f4#DDa9aXWN}O|8YmiEl zr3qPxw=BH*JYCPy!SX~vBqGE0%Ht$o?}dbwT>)JvBps3IjvR;fHX~5>#p+9wue-= z-+ksMKY4X8H`BFjVA&w!1jZjwfI4~d#Mb2t=Z?MjwHMXuYKJ80*KjKm<>;KFC~}IT zz*vKI4r?rVo@0#xJl(Zbu3fvv`g#v#H(io0A5~zWXEKomx>y1aWR9hDp9uQ0C&XJ}m zX_AnoDOsA5CMkn{LY8GX=Mf@6DTPuJDHYb0A?2JSNfMm3;63=#2yql)?ak2cuC1Y! z64shW-+ue8ZH%t;b>VnR0)o-POn&&`>7)DheOaxnTqVyl@+>DW3i7-t!^IRh=kVU6 zv_?var}O}n*~uD*brvZV;IYnPZK;tC34);VXi0W`y~oa-kE&B2oO*H0`ZbAL5*X3t z<>j_9W`4`uJl9rMDp0Nr5$|x$R{FkTJ^KydQpi6>hEJA>eLgF|k3_}7bK?K7&<-NxmOO|CU%+LF`{_E$DkKwv) z2#u7)SFT;&^WXyysI}Eqy!ULGUm%`pp|q|*mL#NeD7-P^{ljr&T81RW91Y^tqj4`b^j>+?! zC<=+9h`G&kL}7$hx|+Oo*tn7S2MFVcKuV;P!&TWki?gF7f{LJrVHlO)7Z_vOXG3fWni<~4$0gu*-x}K_E-mI^pxJ49~ zB}2ZpR;~k7$-vv8)o(UkJW?z0z6^;CrkzZn20VOo9LF{YWKk3*Y)`lG-jnA!LI~0% zC6EH8LgH3yXwe$HnDo|awJu*Fs(3G7>zp0FScjTGN<j!y5HKEqRvVT)8clQdgkM@6{`DAj@i&+8B7GlKqBp zYivVlPU2>_zymIdqU*h0mne#cR&M6`a?-ky>@zxU$~#F&Cuhbv&mjCC=AwGvNR(ZB;Y+-hpesL$QKtM{BVMC@8|&sxWfkzeY||(JW&`8L!$;Z z3A}TjK$f9ZlMMoi5CK~j7uhsB!`#9mt@aeHsWvmSn@~z)OqtO<^;`a`j^|b?O0b@tz5Z!h1uEJkChkavDVP-uF>oDs>RE3><3@`;+Iz@7<7Y7>LS+!ist8UfBQSX zisSgbcTXNS^YaVL&i+YNyBoYOH|M=xm;QRU1nwO{5a66+FzAzI8H4@+r6k@v(kx?; zBxt3XnrgFY(<~P+UNqC2HvQ)}zxk~X+1Os3*zEmnJ9FVb{?p(6MVco24jnwKy>qOs ztqq+gr5aYALIj9FV2vfsQcPh`S`o)B@Qw?YE>aW)aTF6q5lhSGigtU`+kf-dfBOda zp)&`!>5cy2NB{bzAb3Mm7G2w3ZO$?^>EtzTMRE@U9j9XxR0-}mo7e3kp$oo*r_qFTSzEI@(W^3$}^G*TCKonA{K^R8Yx6IGK z|IFdTA8p_MNT1QgbDYFoozUDZ0l0@VvZ5#gt#z|Jxzib04gT&y-NQ-!AJi%3y%2yu zpmXATCD4@r*OU7DL15(fs`2;szY_PG!0%lq)k1$%@_*l>>r;LQOSb?3002ovPDHLk FV1n$o=*<8C literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..bebf3fb0d696fea47ec0e1ba930154f919a23dfa GIT binary patch literal 3766 zcmV;n4oUHeP)L)qTgmzxTR(rsvq5o!Qxy_Rwl|-w31#iBk{)#)bfeIf}9i;+OC7jpZ9y+uYa$JneoRg@o5|QxcUs>Y#QjG3B&uT_W$ep*7=_B@TzxQ z_9By^Mb8uWEgU&(0|JZ5LpOlU7v%6PqI0>437|)w-F$Z>BEL^WO|5CyKQvgjnNbKe zzNcfQr~mbiWtCH%JKRz8ZKM62nfd;UpSXLR1HeD7>Tvu(zdWzC=JMN?wz#e%kNsEl z6xQz?^nSN&Uj_RIDC}6}13~sfRB#9DQ#Q7i zEG%dBi30rn&$hp~bVla-n-@2^r#nU)#`A9<^n3f>{obEUSiMe@ZC#vRGbrr#CbApn zgS}7%8()P9jZuLjmPN!Wqv>fV096CS+v!FV!M4l{?*2po9{zDh8X~wdopgTs_!sBI z&vv0u8+_;a9VQVK3Lq-`UcNbbaQ!_C|L0W!!=Arohd{x%jMheZA(K*}QsrXc4W(kZ zgES`B&Ev<<$NVV)zWI|)i(K3Ok!6YbbumlbbJgTneNE)77h+}*5yr0rzJt zx;k!~^U|vN=!YFT=q<1a6l)G81tAzrGJUnyxfxpZ{yZ}$eB)yQeB;?|m&79U+Ba^R zQFkt;$ECG~QX#_e5(W@LB2`<5cRRYV_QN5rS9>dq?B9N{plSUt{HDk*NDH9WDT0D} zHaXY#JD#i8g#(zw!FdCG>pwOvaHRFZgp}KQ_m!=Y`ecNN=!_>aGqhGvT32hOpmY^L zDK!QmkWOU~a}($rF5uZW4)O!`W4Ux`d!Slc)&$erw?YSOOfIj>n>GuolyZIk;mOO{ zJ1>C8{&U+)_g~j`b6Z1_g3w2(LI@HdA_x+|F&G9j!x)BP4WkXTsRC%Np|pZh;aFYW z1^^)hni^ZslULZdcaOPe{Xhy+kX_(+h^fGf2;VH zwNAsGi(Ayx$}1g_4}SHaXpR;tlN0Z12e1uzxIr}4few$icqRz|(1xD+-wlUDvRpWB zEwY(LM4SkO5U5S2kWSUZk`_v(0t&@Z6ibD%Qbeg-K&f0rxl)23_#ZJpE~OO?9NLR( z=4J8KKbwV@w`MKTeAyga|Io10M=RVey;M*%DiDo;WKDiMQs*229V`D}?WV!f#?GO? z1cck(;5OWNabr|S8*A1d3Jwi<@+;T2$;O6>$d3-A;*~*Uqf{<|*`O|!L4CRbN`=T5 zMxeDqB9;UIRJ;lpjJO+zHX6C%K}>9HL0`dzDwZru7Ml3ldk;ImdFoLWjX&aN-8}+I z2|LUdw~_4&WFU~t^kdVhhSf<=AtH*_&zfc5{?|Wzu%;$)lQBVH6<+;iYXf54`lc02 zXQrdGn`^E5S{DRDJnCQ|*N2As3HZ^PPBf=1TrwpM#}e4vGm20d%xJEK6oj2eMj#+C zuPucSI&&A<6$%`YQ22F0v;`)o5v2ouJj`r-s zluRBw2kH?jfys3RxHe;NE(xUx)6+QsgzW=qOsyHj743t_))>I}05m8j1AhJKoXRBD zcqma9uDM@?ftL+cT)c5` zCiV?CU{Z1b%V&%tn|7hJK%f|o9f4(A2t$Q1RERhNQZPKP48aPH#UK*^9DubCK?P$d z92BqroPO2z_0=1@ue<|HTI>8@efLYtSD((~>0hT;k>puUf*eai3M+Cnn2DNtX(i%y zvYA>PQk&!Z_0ED_@Qom*(LWG9U@SfCR&h z*#Hrk6>!qrM>xKN$BG7^#zsVSo6IPnJwQZIEGV(o`5T1gSSp=#QSn27^%>`b(*uk` zl3<~Tg@hEsH?yf>Vl9Uy35o$^z~hEM00}T?%(4OK50X9BU_A&94*`BRSmi*f0GJSD z6g*XnCwDy|Ze2Ph{e?+&U}h)-7#7J#p4-y8^1g+K&kJBAfCbkfG7%{w=>*JZ7M7jh zpbRDjK#ujIIA$IMFmcs939zYvbuU=;fK?A*Jdn`nB^+ z8Rn-~-na0inern5-5V$qubX~#)SjAUBo&c3ehi0Te>Xs7uw4fjQH4+n21S58u#f=B z>VC@^JC9)uu{Hg{hNWh`9OxDiVyyoP5!{5HO{d@gQpNZ?fOkAet~-ZtA={oau$ zH|))20ett20L@Ey2*57}cT>_5%~MG>)PquKmSUJxDG3>kad@=a!_o$%b-WhU0wDn* zaN{@29R*%z6# zJ5_r}B`gOlZ5Uat_YBR(x~@OP(UCMJXZxWHVd{i=xN7c1L~RLXoU_?KlraV%V0+hy z`mgmz*7>3N+{*hFdS?azFx(NFL`Y;{Cz9?_>mp}ZK8|He>cYZ4o28B2`33lO=Uq@* zz_Anf+UI5=5w$T|2~hSmCf1#H9pIc)O`0^cQ830J;zY5vYdBoBz8ef)9VmYEi~#xV zRA=nE7Pevu$u)_ZcM8wpnq_QInW%J8Jh}0oaQ_X9k%-z5LO`hiS}RzV4Jjm;4TKON zA*zYdIT#+#GscTH8pdd7t-)Z#qH#D*1ltY_@$>7uSDkTXt~Nk%2&K>>8L|w6up$0a z`@QX77FR4##GV5%l-97UD6|eSS{Q*40z$~KS{%>pgs4t)L?;g=26!yf8!sm&OvfRr zb*WT@Wm%ZlHj(sOM{Yjz(&8cxBikq!LOT#98q8VLX}`U}N6$cqH6c0o#;GsZ|Na|; zykCpIyLATKsEcB`h^S-1mNqJWX>9Bt&+Md-FegQwt9cjmF_0<%LkJ1WvY^5MQV7)5 zW)O))K>#*)_OnsmYiD0tTuau`bYyBx^120ZQWYSQQ%E+JFng4tkmXP^G{mJ#EJMkv4V#=$*19zLhF(UHz8uCeUwbT-94WBN+& z{>oyfFL;bEn=%B)niz;kWJli&ywi0Vx(jWXkT?vGz@)~xxORRslCdZTM)Fv*r3bru z0zCBjDX58B7|svD4?Ng*1ev-lDuKbio>4QeEiIH*c>b+J{_gJlE@|1V0699Stl#wJ z>Thm+^M#(_-h&>1(pdpYI}(i)o7+lidyi*CT8gA=M8b`AVVQHJvO>?!?vO1ti$aPz z6~si@2ZU`Q8nyBIhfA=he=de9X`~_}n4aE*nc3ZVapzy)^NZRrZDJj25-y5m4?7PJ zo0s124(jE5+xvp0lR(0%6!WW|S#kTLmEs7aI%kCd%6x+9bBe&&p_JQ!7P8_gDUz-s zHyZ5~QzBj73PkNF7B@#=o3xfOX{0N)uwclB=V^qV4+{!0tD;?5(S1X;gKxgSEOF3U z5*?x@%SNRiLEi^0-V;yVe$4@QWT-nfYdKy5;4zKJL$N4X zt`pm>uXl#SuOMzo(-O~9M#jlPoWa$JfZ?$wA49r8hYN8k241B`KD)P;f8X2du?{PK zU2TwGxFG0^PUdp=2Ff(Qf?@mvo3lE)kdJ)b8z>WvB^so#BEpI~Y&WN5WX3eByRX$g zw69%M%p5YDhC%i9QecQ3r=(-?gQg+&yK-jWEki*ll^+=P{2)}NM$79y-(|%M1^@tb zQFOTMHZWwuiOg&ik!e#swf_kDeFYLyg5*gl8Hu*E##LbS-Zm+`St*3D<_}2#2lEu;tifOi0Q>B?z5q z6IEz8)z9vD`C~UarVEe22^y8VHl^#?tFH$D6wYvHZ{M~^_(`-mu1_QapQb+h(EVxa gGk{N9fAj$V2e4OD6iB*W761SM07*qoM6N<$f^2#SUoynvPooo%Asf|PF64Itnpu{eYNo~CE)-Fp=di#5KIZOX|i=LijaEwdo zH}lQ8@9Eupf4_6S^KC~&YmNWU!f){NNO`0+pLLC*X1pPCBM49c3?;A>^TAi7mV~O) z4<*-2fS?4rqP5_!MgUj{Ue=7)wKCU3fR~k(!E!7lKr z2IcQRJb^#_!FzieBj#7XxT`-*Nxfy1B<~9R;LRZ~Gd=Xz4TaNL&-Y?N2uCSpKm&}C zfHR|DT2`_-9zQjB`yFR`dwa8(zXG-3gPPCRPvB4Xo@k%-<@x6yTN^g{VpcIHzEF66 z?=e9@l+>W@4dzsTSNQy<711Hj^N>m>F)=ZTnVD%ZGCD#S6Q- zxpo47@OQ_Ww9;Q z(%=TiY%+;tG6_KA!i94}DLpoL$DOZkynW+j4d{ycWxcAqok!ry`%ap!FW%fW*fqFe zWfM0Tg~mdcQZHI-1R{Wx0zw1`f&h$hghLVRJ2)&)4rfJojGVu9iF2;KfoF$?FC&}H z!L&>WDfQ{IC&h}TEC2D~hj$;V17AyGQBxsWYdrU@cVlq)pM#LccWvxqclF23YngGS zR0*^c5TXoT1l7?n474=0L4)AbXj1?1(6s#3&GCOQlv_M`>Ld!q0vyMIX<9gU?u>{; zBL|=S%u}z`0k0*pIDu!sf8^PF*SCIk$NEk~X$jx=01!}0K?wl}nac^ZhSsVKUP3Ba z8mmNtDj6XJlvc1U8|`gfI5&~k|9N8S;`Uy7!1H_zj}Ai$3Cp(O*bYXa|5yz{4V*i30$q_{`wSCL^6q!t;tCgo08ELMR9!C^;VjXblYwtu>TVWvwS6A%uX| zb7t#$F2*NEu&%eCyn89v`=hhj@Tx|6*m0a>E}Mhv7UBCIdb)b}+4E;Mzw*k03 zavUYwuS9#k7y$|ZLXq#keQG+0Mq+4d=|EFsGYBE@0}rNQA`*%MS|gj!B9|*`K8su- zi(Daxd|8Wb5kcV3178t8@G&xW2~TZbN_TJSX+Jt`Zqh=+b&JqS!!5eVWHRVm(a$ej zzVOJgV@Kkci~5zw&lX()D(n~DI5NKGtrN44Q=-Y8H%ICAwe3bzIEGWhv+DHt75(tW zZq{TNm`x^7bPFJaK**AZbLKmA`*&1DT(2+%jjFO z5_vfu2eS=AXyE%L^UmjT;GE;;6@C1t2Y!10x^=g{i1|4et6znx7uvUPA3J#PU_9J@ zb92yo+m{J5cW7vaV&W~sOLgdY|82VlTiJ?^1`-R0005rv;mY(Fy4siE?Nigb!C~m{ zZX}k$F`mf46B^wu5pc#ZlE^@5=;b%X$eFPu_P%+3<`ZqRKgwjY$mR23j6rJ+LI_%0 z+i>CHx!{)dw|;xemaW5O`}fLw!9|Tns&%6rfCa!R6bkmig9jTu&kLP8bK=w6?%vuu zG3!SEd(2xC&}f^dtq3IqJJvVRt!q2U#fdat{EzeL2y@3XI9wPbKzG&gy{+`CE zVzIzH-=~&oYRj^v@B1{BOi|0Wlx3PKnM`vLc-nRxL21d;C1w&3QbB74t$_lK#!_bQ zhNA0{{-y2DJh6S{yLAMrF13=4)~CZ106l#8P~Xy?CCFy85JJE(nC2`bLk^LIk|3^A zujg{3JQNP0xw%;Y8lg}~hC*RU2+Ae_>4a=a2?e0h*cc6zjzFujJJuQ?6k1DYUAkwA zF5CR*Cswv_2K?p6fL^P8e4v#{p>M^LZGC0nRyu6i`Z4UtY4|5_qjk zlN9(tb-QV32pND1S_&yjSA!y@fYKnBjawgDv#d4pxxp?10%VH;qzrJuw%H z)Ef1`=Pi#A!m(`|zVE}bOjwqM=B8#?rUio=P^x0Ldfs&<4w$wD$`}}9AmvnCD+wiK zO(4{u8c3kMck}Ab=%ZVf@`%kr05nA`Y+K(Ie)*lTXE9%@I2Ws50RXP+aw(-@T4u@q zO%t~5Km!P5DREOumsM3wp!V9aZCI88LIBIOz$k;NM)6Vv_k?1jvTa4d>Djnrefxqy zwPSq;^#k=Q-+1-h#`?e25?CzL(-mfhQOc(C`7DyNv+!IG#i9$PBp9O$z*lBh1F&od zk%k61;SeUgQ2UY0O8lZ_0Im|TZCg^xf*=qGN5arr!}opofd{u(1Z4!KWx;lw1>h@$YIP{hI?_hh zJ->ec`X$dixVBA%JMTQ4U-s~}_1z8k-rB)zW5MKCC1P_ty>q!8NVTVa!YiA&HhHPk zt=cen(eqrBbEBN0mdQCb05WIST4ByHkm{x$&6(}v1*^9pd22C%^QBL%U}3xT%HMsk zkLNuRt$h9@Kv|YGlg(ygmSw^7+&P9C?6d z$i-}EATP|D!rY&vfPxiU_2~8<7LS-v07B;6EE7_i*=v!?DsEfbs{ZVi(_j0e$Jc$X z`keQR#QR^~H>jn&e_&u0pBNuQW!5Vxb~-Sl=Q0f&B#2oESkn{Rd(EDB2$LxnS5mIp z)Yo#gKEO3grRLB|!7wfS^Wib?zs}5#1X6udX?$zZT&SXBW5bPmU)uYHhd%ibzkK;p zC9+mY9GYp_ao^z5j%95T!Wl)#Vh|z#XB?C=XlRgfVRWscwT99)wT7JYSO7uT3Bwl( zZ=Ia--#9VzSM??|uYcLqwIs(4K7D2CN=GafLozvwipSL&@D+=$Y>$$Lunj2%W>d3Z zv{VdH%E}cT1)-#-I3f$%cJm6Qxr(nuL{_Umr4(}c3@p>a&RaUn*N-QDqn<#eM^nkW z<-*dU+X*-H(>K%tO>VVE!t0@w3E zKuQEEo1T=e=aKiAM^*f(j$xMSoDUG}vAZ#%tk_jj&U|9|# z;RsB_LN1?0zL+adf0=|vIZZK8DCWQ@N4&WeQfin62cR)BmBhauO$Kft)mS1oGcj`U z*Xjw7THqCZ=V$M99ysv!)7$Re$_x2CGUfceM1;aL4V+Av{o`(AT|>mN(m7WUXmS6B zc6;-xP719dq`;Aj34P#fMnAo?mqPiNO(h_t03{U7jd3_m1XHO3I^w13?X{z$^7Z4% zf@5+U0E(VJJ$m8f9ygbsdiiT#I0hiISZ9vctbkD=v48*H-+pCk>VdnrY~_iWDHQT~ z5JC`%HXxVH!Lm#QLBJ9@)ey5}!L*||JmuV#kxpk@%)~^>ClOklwu70=d3(u*r*|yj zoh?x)rN9}-FInWFgC@kG>PP-IqeGeMg6eM_D7yj_ujAXT~s$ z_wNfW_I>>dRIb1C-M_idb&I#(dB-M0D}{7Ajfycd#;chpDSIC&BwWvh2n4uMA_uK- zd3cCsvsvbmNGxKgd?GPXjK^Ew`0Q`~)=%(@cIK`ktPZ?^l6e^~{c!JXBcsC)tXsX7 zceHnsY&L_UTb$=IOA%~tvzI7pXr(ZlOrlUMz_uL-A@s=TP|&+{#s1y9fB85*OmB1% zsqBX;`0~gP9Xi-?=gS_Oe+q4#rB2vUDZ`&-b9U z)Wahq0i$f_j*T1tWpHrQ6mCR!`YI$A5GX7Aw|{?i`PnmP@AZSAwY#fJx3#tz;cyt1 zX@PSNr4&5RM=qa3GL=L&mkFk(rif`;6RQVSy}Dz^y~Ft5Iv=PJR!5@J>Z>%H=%q{N zBZm(EY#=d{SR43$lN2(fwBi83C%jZQiu$On3LPJU)sOn%duD{tDD~ z%N8^Q03ZkgY8Xasdh$VMWEJo?_Kd7fK31oc*FymK7@ZSeFM(S5KRv0x5d`KvuRMNa m|0{8$34G*o?WNMkRQ?B&RZpH) z>zrTz*i8sD2_~Q!XZX&Xf9~u(=ll8n?(dx6y~~IQKVaC~ORQt7g>8i|RHv7~- zOl*94-k4agO!PK&3sxK)@XrrUG&lI;;@LM3SIjOiSBMBttll?%)9%_Zy-jodfe<+wU#8e#%u^JboBp?bjJ&I2$(5;MaOIpO6CcrVSoY zTHrqU*y|Nzdvfo$Dq;q(g{vxxmdz=xdT!&9x5}>eO~}h|s$W+JmAl6lI5|r|y!TZt z^1Lb(&Qa5%;jXg&UCt?`fDjTw3P>R!rG%ao4GiF%gMooD2HlkSsG=T|OMDZz_N6{s{#G85t-o9GE7A zfTqR4I0q4MuqJ}a#u)OvPD~h)4U#4lMS*Tfn3e?&-r<=|8YcoJA%v8WQY4`S;_*17 z6v$6^r;aQ3PWk<+ia|@~l%DF!pR#!I;%)){d!X*x)`*z=O>LCf6a_xF0$tZ&ngW~l zw4kgw8)a8#VQ`)sHibdq3OHvd%y#3=ooArH!mi_fe0jnT%`lMZ<`9+zOIWZh3qo`p z0+tXE!h#;xFnB;tinOe`t3EwRQ%du{=v4-!DMJ%Wa!`<&iob0+9{=WyO&MS8Q@0&% z#msSq@TAz!MT05r?V+L&?w>Fa)h$undYuod!f>=MjN|no6#5)+I#WO-5ei2@B)|Z8 zJYI0c2Gh`Bvpdlg)Xd4Fax)Iqgzo_Ge6IiuOH?-nb)z6F)$!o;t5S4RqOu_(e|%#B zPwQa*LNUgmC=3I$UCDyAP@JEJwcDC7Vqgy5-c=h3#zbmSu8Rj_64Og_@b;d%)~bfk z$)f!9ft7XsT}_RF)!Eq@Gn)e8C%ateq6bB8u4~cC-5JmBdW$i^3 zA6~C`=vZCAh{Sa|dM2prhDotlj5JLn!!XFQED}PH?558#OhGHRo+8Z@^v3pTI#P3% zLg6qSt7)c9U!Eb+MVld{Akz}G<48+z&xy0)sAka7GXZV!x`RtD_K3GXLaH724e&Yb zkw_4QnQk>6ivSGZjDv9w&JqV!+en`cp_tu~ z0>dz1mVwB*p^&@~N^IzmA|PB%y4uyYK_Ps6m)d6-g~2c--1 z9n}^b&N)B?pWE(v?DdMN-QLqy1B@}-ubzH;LT0va%IT_;AKx}`0ysQ|CT0U_{yEyB&75Urp8VPw$*Q>E(OI_R9)|f>0C=)#1XynkWs; zPi61#Yru6wvQd!fLTzgdSLM3Vn_VkmUY-YGv)OR0As*kdxABB-nw!HRYuU1SV_G@| zIFBp=;{c!lkPkk+?#^pVM?QbU5ML&N*xpkybw*hpKU5Q=CD8aMiAY00?27U+|nKAkZ1e`8!F+=I+97wPUjf25{9YU)*wPNi5)c=EaAH=skL= zXN++G$}gT;Q~dM0ChiH~4B{PViKPs|>9V~;+oBu_`l7(1Nq+xpOsBh9}HMFN|P@^c@UJGrQzjRU3t zY@8z&3niik{D09CffDnb%LV{fY8p7_h{vPQv?v(k$jI+ zXI`L}U*?ENGRD>d*Z_ddc=&}4waqs@>P~gu5DJDqEy&Nh{eh{2`UL}LA*6&W)dkg& z0uUh_4#2dmb_fu`Znq=Nzxp@P!vTSif$n`twS`MFX+TclZPl`t4 zpC7K+{lcK(W2Wl<$Y!AA(!L9|i7{pakeUn#o3-GjYi_%3+WKjuay(wQs#MkojCa3k zsPeiUX|u)^E&HxvU@}x-0-4KzK4cj^Se__{PnCWO}wQ^OnZurqBNK=XoDyq~|HK z7yRWn9#8u8+ze+k2?$ c>jw|;KS37t;K9 zC2gZdsv`BJ%0s2TRH}Hbl)kiisd`hDq@pnx7o~ubRuF7#Y#b*OV}_ZtS05Y#Qz~JH z1EWg*y`02{P)|T6&~1eL{CQTbw@hjL%!1yRO5YL zc+?0=qY=u|d#7In9*x>7@@L@hhovOKSAy+(oYWWLqgjHHofhGd6OYNZsQn@ba817L zvL&0T4C2qejz9N0sqP+<-IY*7UX0o=Qh>=rQUt!%vty5?zKEYaiJw0M%L^1ocRE$R zbo_vfM(G(TfcHEb%LK7Z-!T5%8;A(vV)*xt(pjvM$OR$rqbNNi12{S+1r?qy?)aRQ zMh!nRiLGwL4GiJuPb2jYD309kfW71UWF#uz$N-$@Td{OL-qpVaf9`LHv)Ez@lj}y3 z9rz0qq)UCo)7j8Cei)T+Bmn=|FF6C>C~p6(mF5!u!UVRr55$0gtMudNPC;Xd;^uoD zo-a=xlr2$sM*`5aJYzev9hrfx`13~*Yp})M>n6DXNhZ+QlcdZ2xX#Yd!w;hHzHNZ< zz0xJ{&EmG*#)QlG>6PlAUkLf}uV?tlt24ZI{6k!M06%vcY1Zi(9C5(DiTyI%b^w#) zY1@%Wb#2;)pBhID*kUiaPRlQTe~DLKn`i3%1|}b15^;3?6q(+BY$EB^+6bFZ#z=2~2WvFccm+O4p zCOXsEd+xr*~7yn%?7Rj{@t?yvt`;^p7WQd{;d=WV$kzwj>v2r3l^j7=VrO?M3-5$7rElKK4LefXJ4 zL>stLxh19%D*IkJ%g=r{$1neQsnxVl#5jZ2E|4vi5n~9I=kE%@pASpY7@jX|eGqE} zKlK)-R0Y#oeXmf-I}%RVy?MjU#+82j;#tHRisc>)JT`GyHm)_FHGh$1nmmb%cjt%h z7o9zcgmr9B`KGvp6aMnj5Fagj3hA3|Y@vvsI*TsNku8*_G-S0_4B${p%3se5*Oue3WgHqJ23$u+|zB=#(h2`0DJ7-o5| zd|SqRZt)xLQ`tgFz>m57T>8f{elFt|-k+9$!4Imzx+n1a@_xtrHNH(dNn_r56m9B3 zZhtQ0$1*lSKPB}%gus)t22Ri)%lHHH{)YRdhcbBS=q^{?2FLFUa5DGP`?VxM;CGA( z_>S?1_Jk&Q+Q1A#QUX>V6Tm&vgGt)!XQlttYZ7tSkqAnm;y&H8!MnE?>K6k1O-A8E zEFb~ioP?aIR~mS_RdT)$#u1!}Be=wo&!D%I1`}ZLRn*O(q!_zpD!{o3pbb`7Yeie8 z8tLW-N#lSYxF`6EYd*B{90zF1X%}kpwI*QX=@3g6t4;%(v4nXz7K*XZLwn1lFTtpuCh^bu!<2K*4Wb1B&Xdf z$&hjU)&4lOr$>~#vwk;u+B|*2!YThC{3m*v=uTW z&(5=2W#(F$r39+%FS+5d0eOyUX7E%&sjb&j^qFF0q2mJQWeHT?qRM;RGIyeyR7HJC zC-txz94w;2)A6={j4fL~@RVR6#FeOS)nbz+k9YrzBXZxmx=?%j5aZ7(k;X zVO2qto~q?4Q*Bet@@mBdJb1&9#eCMrsS;F^3hYH~6>>^oCS=`^9*V^6Jkg+cNmvn3 zm6Rkb!FimV&45@%RgWt-PMf4OCM|CC8$kCM8*p{il3cGcSb~`Fo`EWzN7`B|OI>SX zOCampydJ3%KZ56h=4Psd*WIdevI<_jCms2lLGRmylUB79sM#<>}!D4*|V5hwH>8V~-z~sH??*3Do>*U-@0IxnWVXG1@ffH$n zF6xU1Dgrxh7n;H&v0qzY)Q8h|#k-ejAA%wc}V zb7VkDAb#(#PCKa9D~TjAJ2fK#6?o}u0@7m#YtPZ1_ zRg7CUAk{{l#8zr`NIC3<&aOPFmYm{Ts!Qhgxo<__@9aNI+dU@1p8@9qEz!3?v7N<{ zmNd$-QwVO%{;Z`Twhn5!;IlK%JlC$J%KS?^1NN0f{_enep8ps4a&x|){2R|7@5S~D Xi=7Bz#V(?>00000NkvXXu0mjfT6)ff literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/weather-hail.png b/tim/prune/gui/images/weather-hail.png new file mode 100644 index 0000000000000000000000000000000000000000..563e92b2cde8dc07cd38c621dfc562844e124202 GIT binary patch literal 1708 zcmV;d22=ToP)v=d`bK$+jCB!bai3=Y|w_TW})k@v&rE)&7VYrHHCh@xGJ(S;fC6bXV*i zs{jL7!TQQk-emi$`M2M22MGg05OUHzu0Q{A^#yRcTk>4ZVXk0pS+>o|#tD+XSv+N^ z(t3T~mctVUWq7uXyDiPvuaC>+urnBj0T56d(zQ*yPk(+UpiWD1h(63N-ydw@+diI_ z*J8-nWY@j;NaPzZEuk$L3pQ7zRb7QPOms7H#|M zbiu|7cUOH_+6QKBDEAH?dEZTHBvJ*ii`N-ruu1F+Gusyz9Mc}i%^EtubK;H7m1DXz zxszpL1sf{{f`~2px@!CG!*z{S=bFM_{(dIXnRaPUoExNR8b#CyDN2|^p=OFkHCprI zv6`YSl`Cd{T0Xop-a32=&Dv0&GbqEeCok8V`p2=x@Z_;sX+F1X2LmKY0s{kMom+Jx zf*=7Q%)vQ?5a5D?a{++ha3(?!aHhVc<-3FDzkl<|!B1T&W`_Vp#g!{(J$mbFj5Ii8 zCphO2f`dsCB*tP8Oafyj2m_eeRs;h6cb}QYi9jX<7Xm_X6DkHG1V^IVgTrSdfgR-y zd5fnEJ$X5Q9d2a0&QGg47aoUA(gnakh-y(tOahabY1{+=fYx6C6kB!>a1nrrK;pkj z(4`f*1gI$xz=+&_z9vO4oxl0e`)|K6;sZ+nM@tW_US-3W^c44)v-QE!$3|yQxbwzj zHx~w&Bm)p=Q9{5KS}9iLOYSK!xLgS!5>ONc1~VzGoHI1ign@{9qreq(l3H<4= z3vNnF^G)&xL-RkLao=%E0GEJBh;>B*04D$&0Nb2T_AGpU!l0?SnMqEBO!7`w{r zgTr!C67q6=ZY>&trfZOGG8|49S~MXx{BrWtj^g!Ged$?Aw+|mR#UVShqiUqIVqeMX zlF#0)0l)zm0CY2_L6^Yb>2&AH{~S8TF;W2h9-BP z>+YhgzMlRWDK2+ISkq3N57h=k!9ClT&0Tu3s_X&)0l;A{h7LeAjWZ$=o$_J@pbZQ& zO~@vcFpW46Yds9fpX@$TwzCDHkT9VYBI51ED=I3I0HCn2@Y(rMKO3Li(?Vs7#+nnk_^GO?@k+Fo0_Zr6yV>voOkYz#{+^vu?+w!C2CXtn0xAD!*hw%S%w`h#q@_bJ_X z^q#`Pr^8la02W*5sl@EHFF!GU@6E#>{Lsq$najU-dXp1#vod`HZK-m)l^CtE^iE7f z{#t*qUI^j0GJn~OhokSW-1(p^%f9(DCbhdkb;XRezF)Q}xjXKks~z3)7;R4*zWVKv zjI^Yg{)X_%7at$Hy+@Lu2fxhuQW7UWls|N0YKnVuPvro5@XOrUx`xGDckT}d!r?VN zl>_Leoj9Jl?mzDK0>oRr0P$8YK)lrp5N}=g0RI9nM_@%U%)j>l0000Udl>C8VGoZ@=`QZ#)v>;V=Q%E%rc-& z+mxhvX<|FhwXg5J=lu5}H#9_Ui$OLuw=ea(9M8HH`JCY~^TBO5R|IFRb0YfI(#zUMYnC2X0nyL2@#bOv@EI5gBBN%B9v-qo=#7?g`#w8X+t}`zsSjKGKy^c#Z|P;%oUbL5T>$$!pD_kmVrOWXUR-c&xVAj7 zY>~J7{+Bvd%&2jO%fxCM+kXTiw&V4#H$Uhc80q>dHul=bJ;_YMC7n1oN!N8sYDrSn zF^b1WDV5Uc&+i@Tt8eajs^-PE%1pdX`xUBgXe(Ph-`i4E?koP+p^>qht_;ledlUy4 zAW0G!7#PcJ)`=MX}G3l7c&0D{w%4?#fBP$Kd6e@^^$(=V3(@=SeB3s4{I zc&7UL3xCTy#x!01Skbo`S?AFo{{lEdT(RcmkmD zX$Jvc0}v5NoLUK*ydsl;Vvz_gEnn!5sYdITmkvI({nkq#wgvD->%m>m%2-iSbxZQbhx!f>K1FEXR3Z(IsAcEphQ0ObhyP^Kke1);gFDx!_DKa#Ju)no$^h^*A7PGTb{f3n!~mLCV@y8kBU41E&wtBdE@gf+iqFC`1bMz z1ulog&^xS}@3sv^FDWa^t19<<^i&eMp+k`sI9+ZebRio4e&p!>;GWz4C4qt^mCM&V z6<29WOSZOubYRzky?^WjzyX*53~Nl2PGi*8S!m6gdPhr39jt-8w`^{_vh0E%|5{d* z^Ww4S-(G#>ch4`s;qHqnf4Z_hFwcAL{35q!cud#3PsID9@o3ANPu%y|k*?5500My1 znhXPgW*KKhBr-BH)18lqa9?BSj#KdrX03=9K%wQw6!U;(vA-z8^`IbO&yDU`Ofi(zfz+WwkG~uS%C6>W+m~!$|vy%t&@)FJ0Oe3?JOs&{{P$|K2CQ zbZ!63XaAecu_n^2-AZiS6AqXr6*9)0Q_B`EGPah@^YBdjdZ0O6l4)Jr0M(75D<=Pb z^v;Uo8aHZo{;KkyQ|k&2$D#g2dnWyEdFAuVeECX4rao)|Y~I_sy1?nUXKMbA+bZ8m zSHF$I57gbbILX& z^#b&?4By2dEB9B7u$l~8RYmU8?9^AS`OV1UZc9!?-} ziLma<{jIG416Y1x<=E&*!;_PT^%+h=hY*C+c#HbQSv3y8m2Mg5elBzQJIhjJ6PqMR z`e6P13|BDz{Qgr@hV--SpER_uH8ECIjRpo?*8u`{^pcBOjfnJZN8eq5Lj4{X(yGzeIiwlmNY3WHB11uL; zyjwZ`o;r7NnOJ^NMH-0Mx`Wjp9z9imv-(<#^ZgT7JzXi6JBf3h{C+=qydHA7oaAt{ zlF#R-9iN=9E7)K8X5OCi%&vIrbQj9qU7k9mpXFFqdUVX^=WjaaOib!)H7f=%K$0Xd zFfi7&StlY05)dNra1J2^xZvPi03aAm;SdB|ZFIXo`ttfeR{nCtukMz!Q-Ffv%C~bL z9sCp{Ka7eAoO1}l!6XS1V{H&j0%HLX2CzU`5eT^LcNQ>C1PVZKAs_?~K(&Dg!4Yn@ z;8cyrcDTGbYyJF;OW)+L(}PUY_!Y0lg~cdKngAFGKEDqVlfWbvFdhH^fZ!(pYMXWt za0`HlK;r9_p!O@$4yeUu!@?EcrK%ZG~#7h2jD~aar#P0)R0v z85#g@z&Il!(IqqYdh*RKE|&8OOJDiAp1}Z^MogF<10WKB1#RUP4j=|V%*e@ek`NIQ zJ8@=qN}!G0K4<=((g~{zkE-osd4(n8^NT9ty8R?h^9{`oHz_mPO#rQ4eUwknd1V0+ zwGxq&h;$-y1F!+GR~`TJbvPXM3A2|y9caUY{TaZb&FVoHi|F4z78R}@5;~3T%+Eam zJbvJSoZLtK13}!jd$p!n7f2HOamTiSLst-yzIE;#JLSU~}#=rH9aSz?K|wRI29es)JyeDHfzgt95t zVv6pVGc7)9sU%6)B$;jjSR;)uq{T>T-{O%2f1c$SvuSa0K`RPbxn&nTjxm9eJJX7fS{h; zTas*ubp4z-wmRH?J9N`b0La}}l8_Lq&Pk0kUzZ`L0f1RG3_NqSwH{!+&gLNiSXor@ zM2tmw@{Qk(tluJV(Z7$!NnkIg|K0BkTGan)UsaIiqSts%N`o*I+1K1ag z8@b{2X`|wskDYF`NfHeMfEEug{_nZwM|Ghu+Ln{0E!ke8j7W@FqbLS{K>?&yt8i=2 zCxEaZb5~b=gaoi+N9BMBtKsohr#Rm}p1Zp|HOwGC)Z*6mE3!0|K}rUISLbIeZQm}+ zWXzpDCQ&Z_&sBR%T15gdx;7LaK!_zIsbXzp?-P6KI)pyJ;=}b z#*Teg>^d6}XH^zQsD`f;S&|4MIaKpNhyYw*YE`X6aKS6u$2Tp`Y}AS6*4bRbr|H?R zEqd_p9s6nqZ=``q^zYLRtuBwQ8kl^?OIlYI(p@NTcj*j;$;s;$j@};1pey!Pz92C+ z*$-au+7l0+2qi~Q_w{1y`=B5E+v#3_o>nhFPpcQ8r_~G4)A~Q+z=!E1zy1dR0000< KMNUMnLSTa2xBbTe literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/weather-snow.png b/tim/prune/gui/images/weather-snow.png new file mode 100644 index 0000000000000000000000000000000000000000..286fb2862754c62c7b93185131c861f616fca1d4 GIT binary patch literal 2106 zcmV-A2*vk_P)`_rlnJ9|P z;`E7o&fjkMqPnT0#p)K1{G+aA>7FZN!|@R|z_PN+H}AK(0*on%6HpG{S<|z4=ZFg1~$} zAM*zS?1NL+?-m@WQuowHSo1*szN@-X6JGy;*W;-L$b<8Q5Xd<4l$MW|NJV7-I5lz1 zP|fWXhpVUbsBwsCA_WJkQotDb;AHLT3pFhrwfAk#6JOPPLMfNWNfZU<_xqX0>tQaJ zlQ|rA=JWa4U(ercG#;(qzI6Z9aiMtY_9>LV?`rbMcujdmsxJD{%?{`6sfjUqwL%U8 za2y8$0zyI?b;cMdqo7pUL=luyh*X3qQUDA}RU{|_>Ra9JGyiV-`^w*p`t8$Vb_-xM zRqx9G*@!<9=7&N*(?@}PaUGSxdIhy4qw@{ZtnOG!veTbaiw&(3{&E? z>M8eHtrasTCruwcEJ`g>0fbWkpm4h!z*9;onB^|_6bN`e0*q0(TrLQr00JN?N(W9R zN5JoeT*1R^^9EiU9jCu`&oKkQV*$D?Arf@OOR%z~JJ01LCJfHjsa2X=4VK!UW+Y~$ z4~@{+opulaJRbp-iiaQs;BvVj6;d!0j6oq+AW9dF&nlbkkqSa4kBAafFBB(0!kGRx1Z8|U*4%ZYbslyk4qfz z;xTn40{<;eU`R6vG@g>*vRc^FEq1Gv|eI6JIta#%dDcwY<9B>G$7max_~V z*7DO^mv6pPTiFJH0#HhW5dh$oj1$Hf4QXIQ1u$+qwnRo`Vw=s`yyd;D;|~G^OH2ts zAwf~eh!VllL;5ST)z>ONb;IGH610mkCWh2;H|lmKHW-ZjyM=q^#SMy1{`ril<0E** zu*}?|_|EInyyRnyIT>SiX)iFw+yE>9tSJcz9ylDeeZxeU_SY3UK%XFf=qpr?!)GN5{p#+!U=SNz}uh`V0P1TtJly{zYxB?w6r{~^(Zq$!;I$f;gM(gpivL)6> z+ZzlEMy`p3Q}Z;!u16VXR7IW@gH|{(j9^E?;ciJ9EVFg!q{=ro0gwqf47x zup_DS`k}3h?H;eMt;1qFZR@aC+SDG|x1$D2b^H5S=iQBgojvgrGJwHgeOXw0=rT zvU~Jt8S-N^C#O`QO^#eXJatI){U&q$eRKOi_pZ;q(s@01P024bQQ8Wt)1FweHv4iY zG3PGYr9ZrL;ZuRSQ^>0lI-Q*7WGr&>gtTb;mrlpt^|P;Zy}n~z&Kc>jvAbA?KNZER zwF#<$gH^u*u%%~~A6Cd2`1Ly*2WnjrqApeUu1cZ!x~a}wy?Idj+J@)|r1Lz#2fU90>T>t<807*qoM6N<$f}&^r-~a#s literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/weather-storm.png b/tim/prune/gui/images/weather-storm.png new file mode 100644 index 0000000000000000000000000000000000000000..86e4271a0d093ca9f35b3a0b0b477070e4e66f71 GIT binary patch literal 2984 zcmV;Z3s>}sP)t-bcX`;wFr-(+EDI)JY$-vXRz`4-?z%eMe$TE6iB>To+KD=#&P=oZG< zeE=YYSS+P{cKNausiCkx>;Ob`OG!!bqZdrSKnH+LA8vZIuI>N;JUJBhhrNUpMY(VK zh12y!B93@Gj_DU(peu@U-%!~fmJF1Ym(J03tpcDas;Z7L3>`f^0*o;ziUI)UQ282C zfU@$^t2AAEYV5eNBhEiBV)^_&XqpDcabOxI6jen!Z9_`Q%F0X6S-xz=$6wbc4dygj zR$f}5Y1*GT=hrBTvf6Q+UoKy^qW-vXQdRX2!C>H?tFE478HNEVWk!XBkOERl2qB^C z8ag|>@rakPsZZTtQmFv1y%a^VAa7Y1uwBV*!X3f5IZb88qYG`Pb z@2;y^{?ZFCm-mm)uP9$UZrr%LE}40eNkm_2Cj%isL=ZyY?X~Zu;_*0U&7Nfjg8{U+ zACvFZyqD_e=$t)Rh(VQ*l=9kwf-yuyC@2_1jIpxv`DHg~nzkjKPOlGVhfW$i01#3C z8UPXDiYqTS0f2LkR4Ro)Ad4=&bf&p#^&6D{<_;ME5p6nn;NZ;Sl41gYiUsqDi1cJK zIpgr*rfG$R1=`7iKM{K??3`>8TwFU?JA-PYJ3rd_>zbPNd)wRF2b=?d zWmzaGDb_SiJEh?Jg@{0mGEZV4V&I$ufGjI7{r->v?D}|j17png`Fy7p=wL-k2_iz- zyi&8Uu<-Ty73Dk2%1ixUHRptjxU9TXBcfY%L$7chex7a^J6y+|7F}2|q;0pqgt*|~ z{4-Tm4VGo1uI`|_Y2%i4wv)bc*~_n-u-lN75@qG3Du5dR5P+4MrY)H?Y0`C*&zWeL zriFAmjYK?wP&kNFt@SSxAu_ekIUjg(E+9Au7aRZ~91daIww>ucwfi8YumQQ=wo`Y$ zvUGXl0KnXNKa6Oawr;|N@s{hlw159Wr?_~WdeOxfnzrqL2w;qXG1gbBrw`EIMn9)q zfC~X3`T)ddAgZPz7z{wuG}yL-hK9pp^X6?m=~Qa+%P%cy1OQc4)%&lUb44&54iORI zg6Y!?#u$2gdm)54E_epU=yM$sU< zStjs)00{<03M6AdU-yuZ01+4wBxULlAV>)i7=$Fq0SET(I21Fj%&nRyB@2oL}TL=1oeMhwIhD2f8c)XWD^GSI{n5HcA92tuMyC0q`0 zF5z$imrJ;ugF6m5=Ww|T&N-6F6mm!8B0D>zaxSh(BvO-=f`X#P*4EBa?g(mWY3Exv z?@ssjBt^;Dg=#uw!*(3#h6T&(gQDt4rEN&bGUY;>EQ!;Z11g0=(x&g;^G8-Bf;9P=p0SO7e-w&_Hgkw7}3XAUy%rm6flKBRW#_T0$tbO_j(WxWFb2gME+S}n1&8jA%HT6J1He}Lq{MO z00{wKRuGPB!**Nojt<3{`v(KfnexHU9~0)DPr+>^8LKL(RWRrl5e!O9Yfl7pr|S|O@r6# zK~8oqbWMX}r@>tZUcV1s%fg5p3%$KI+Kxr zbwh;^5{aH(v@~|7+FE;RQ;GN%!_aTw!ZmF>`Tnj?w%wbbUpSdD@@&|!Zsov6Mnp;< zj0?aAz=O2Hh}_>`kJr-bmH%i9PuE-|k*hdCwC!=49vGpeXv;TsSh7?%mbd z(f02rp1l7>$FW@i0sudOI$wI6CL#lX0YLA2*#!`I;5UDNAS)|8&l}JUk5B5{QEeyb zrS|sLXO1-2Z$J0k8Nc`V_$bXEH&uh+&oWUkI)z;y19pmWk@nhl!svRZ=9OEPEPZ4@ zfHX3PN^t^B^!e={xBHZ3wiPJ{^?TqrC=);c0NsA;qDcPek{R)M&%w=G*6w)sy;nN` zkf!PE!QU>uIyX1}B9F&+j$vetzvzC$Qv$k!{4-YQ~hU zAleQfRuzpT&j`R^rLsC!k?Xf#y#3No(bl7*xywXTr-9~96D{2acR9UN6^&eRy7q?^ zpt3p^Cg78sXEco5*WyD%yMp))`gBrAllero(evg-oyLMu!4w)h%qcicfFZqFtgMbLsjQC4?!p+) z4=3l9PHn>OrYuY?>I8tBE30G2``g;oX(BI}0x}snEg7;5+*=*HKIC=g-#P0LG)2PU z3@>dQjU$}~|Jh}SlriDtc@4*`D1hhsrfhBQG%=;93jnUItd9K^z`Fp}S4AVo2J3f7 z7V%G?{%^=KeH#~E+)%u`$?tAD7!;Y)o_rA?OeyLtol?~4`)5tzUI|REibmp<)iIB% z$nIZVRi_;7HgKrT!l5<~>f0@0yDSFa@~UX0{bas}q=YQX_v6_U+iYDGU7HUDSEwRB z5|9A^I|{NBV=w+ryB1HYM-@dL08jt`RnbVo<+SE6Zx)N1J=q{Y&YsZ5@11ji7)c+1 zp27J3+5mU_W1GeZIJZ#otlORUEChdO@lR&-0I*ah17*{?Cha+r^Y)UBqaSiP-BuNi z)b`J)W8|dP`q$U9OGYrJH~Uj%kx$5{Nni?Sxv3AS0xgAr=5!JVLuC$AH literal 0 HcmV?d00001 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 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/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 */ @@ -68,6 +76,21 @@ public class MapTileManager implements ImageObserver return (_zoom > maxZoom); } + /** + * 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 */ @@ -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/lang/prune-texts_af.properties b/tim/prune/lang/prune-texts_af.properties index c99a13b..f208cf0 100644 --- a/tim/prune/lang/prune-texts_af.properties +++ b/tim/prune/lang/prune-texts_af.properties @@ -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 8057356..fc6c7ff 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=
\u0160ipkyPosunout mapu vlevo, vpravo, nahoru, dol\u016f
Ctrl + \u0161ipka vlevo, vpravoVybrat p\u0159edchoz\u00ed nebo n\u00e1sleduj\u00edc\u00ed bod
Ctrl + \u0161ipka nahoru, dol\u016fP\u0159ibl\u00ed\u017eit, odd\u00e1lit
Ctrl + PgUp, PgDownVybrat p\u0159edchoz\u00ed, n\u00e1sleduj\u00edc\u00ed \u010d\u00e1st stopy
Ctrl + Home, EndVybrat prvn\u00ed, posledn\u00ed bod
DelSmazat aktu\u00e1ln\u00ed bod
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,22 @@ 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.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 +632,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 +721,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 +755,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 +767,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 +789,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 +813,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 @@ -811,3 +851,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_de.properties b/tim/prune/lang/prune-texts_de.properties index deb44ed..91f75ac 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=
Pfeil TastenKarte verschieben
Strg + Links-, Rechts-PfeilVorherigen oder n\u00e4chsten Punkt markieren
Strg + Auf-, Abw\u00e4rts-PfeilEin- oder Auszoomen
Strg + Bild auf, abVorheriges oder n\u00e4chstes Segment markieren
Strg + Pos1, EndeErsten oder letzten Punkt markieren
EntfAktuellen Punkt entfernen
dialog.keys.normalmodifier=Strg @@ -530,13 +537,31 @@ 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.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 +580,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 +599,22 @@ 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.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 +632,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 +721,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 +761,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 +783,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 +807,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 +845,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 27f3c7e..fd56f48 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.
Kopiere, Wiiterverbreitig und Ver\u00e4nderige sin erlaubt und willkomme
unter die Bedingige im enthaltene license.txt File. dialog.about.summarytext3=Bitte lueget Sie na http://activityworkshop.net/ 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=
Pfiil TasteKarte verschiebe
Strg + links, r\u00e4chts PfiilVorherigi oder n\u00f6chsti Punkt markiere
Strg + uuf, aba PfiilIi- oder Uusezoome
Strg + Bild uuf, abVorherigi oder n\u00f6chsti Segm\u00e4nt markiere
Strg + Pos1, ÄndeErschti oder letschti Punkt markiere
EntfAktuelli Punkt l\u00f6sche
+dialog.keys.keylist=
Pfiil TasteKarte verschiebe
Strg + links, r\u00e4chts PfiilVorherigi oder n\u00f6chsti Punkt markiere
Strg + uuf, aba PfiilIi- oder Uusezoome
Strg + Bild uuf, abVorherigi oder n\u00f6chsti Segm\u00e4nt markiere
Strg + Pos1, \u00c4ndeErschti oder letschti Punkt markiere
EntfAktuelli Punkt l\u00f6sche
dialog.keys.normalmodifier=Strg dialog.keys.macmodifier=Kommando dialog.saveconfig.desc=Die folgendi Iinstellige k\u00f6nne gspeicheret werde : @@ -525,13 +532,31 @@ 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.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 +575,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 +594,30 @@ 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.none=Kei Files abeglade, die sin 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 +628,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 +716,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 +756,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 +778,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 +802,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 @@ -804,3 +840,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 925419c..03d04df 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,31 @@ 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.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 +580,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 +599,22 @@ 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.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 +632,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 +721,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 +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=and @@ -728,6 +767,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 +789,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 +813,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 @@ -811,3 +851,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_es.properties b/tim/prune/lang/prune-texts_es.properties index 4ddc66b..182e670 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 @@ -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 @@ -106,6 +108,7 @@ 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 @@ -134,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 @@ -181,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 @@ -212,6 +225,7 @@ dialog.exportgpx.copysource=Copiar la fuente dialog.exportgpx.encoding=Codificaci\u00f3n dialog.exportgpx.encoding.system=Sistema dialog.exportgpx.encoding.utf8=UTF-8 +dialog.3d.useterrain=Terreno dialog.exportpov.text=Introduca los par\u00e1metros para exportar dialog.exportpov.font=Fuente dialog.exportpov.camerax=C\u00e1mara X @@ -232,6 +246,8 @@ 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 del track dialog.pointtype.waypoint=Waypoints @@ -252,6 +268,7 @@ dialog.clearundo.text=\u00bfEsta seguro que desea despejar la lista de deshacer? 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 @@ -334,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 @@ -377,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 @@ -441,6 +458,7 @@ dialog.saveconfig.prune.mapsource=Proveedor de mapas seleccionado dialog.saveconfig.prune.mapsourcelist=Proveedor de mapas dialog.saveconfig.prune.diskcache=Memoria intermedia de mapas dialog.saveconfig.prune.kmzimagewidth=Ancho de im\u00e1genes en KMZ +dialog.saveconfig.prune.kmzimageheight=Alto de im\u00e1genes en KMZ dialog.saveconfig.prune.colourscheme=Color de esquema dialog.saveconfig.prune.linewidth=Ancho de l\u00ednea dialog.saveconfig.prune.kmltrackcolour=Color de pista de KML @@ -483,13 +501,22 @@ 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 # 3d window dialog.3d.title=GpsPrune vista 3-D @@ -511,8 +538,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 @@ -526,13 +552,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 @@ -638,7 +667,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 @@ -673,6 +701,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 @@ -681,6 +713,7 @@ logic.or=o # External urls url.googlemaps=maps.google.es wikipedia.lang=es +openweathermap.lang=sp # Cardinals for 3d plots cardinal.n=N @@ -724,10 +757,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 diff --git a/tim/prune/lang/prune-texts_fr.properties b/tim/prune/lang/prune-texts_fr.properties index 7d744f8..4e7ceef 100644 --- a/tim/prune/lang/prune-texts_fr.properties +++ b/tim/prune/lang/prune-texts_fr.properties @@ -134,6 +134,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 @@ -338,7 +339,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 @@ -381,8 +381,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 @@ -447,6 +446,7 @@ dialog.saveconfig.prune.mapsource=Carte source s\u00e9lectionn\u00e9e dialog.saveconfig.prune.mapsourcelist=Sources de cartes dialog.saveconfig.prune.diskcache=Cache de carte dialog.saveconfig.prune.kmzimagewidth=Largeur de l'image KMZ +dialog.saveconfig.prune.kmzimageheight=Hauteur de l'image KMZ dialog.saveconfig.prune.colourscheme=Mod\u00e8le de couleurs dialog.saveconfig.prune.linewidth=Largeur de ligne dialog.saveconfig.prune.kmltrackcolour=Couleur de la trace KML @@ -490,13 +490,30 @@ 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 # 3d window dialog.3d.title=Vue 3D de GpsPrune @@ -518,8 +535,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 @@ -533,13 +549,16 @@ 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.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.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 @@ -644,7 +663,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 @@ -687,6 +705,7 @@ logic.or=ou # External urls url.googlemaps=maps.google.fr wikipedia.lang=fr +openweathermap.lang=fr # Cardinals for 3d plots cardinal.n=N @@ -730,10 +749,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 diff --git a/tim/prune/lang/prune-texts_hu.properties b/tim/prune/lang/prune-texts_hu.properties index 8b2b35b..e0e6b1d 100644 --- a/tim/prune/lang/prune-texts_hu.properties +++ b/tim/prune/lang/prune-texts_hu.properties @@ -7,6 +7,7 @@ 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 @@ -57,6 +58,7 @@ 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,6 +86,7 @@ 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 @@ -99,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 @@ -132,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 @@ -139,10 +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.deleteaudio.deletepoint=T\u00f6rli a pontot, amely ehhez a hangf\u00e1jlhoz 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 @@ -157,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 @@ -175,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? @@ -191,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 @@ -208,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 @@ -231,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 @@ -275,6 +331,27 @@ dialog.distances.toofewpoints=Ehhez a funkci\u00f3hoz \u00fatpontok kellenek, am 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 @@ -310,7 +387,6 @@ dialog.correlate.photoselect.intro=V\u00e1lasszon egyet ezek k\u00f6z\u00fcl a m 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 @@ -343,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 @@ -354,19 +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 \u00fajra -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.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.
M\u00e1sol\u00e1sa, terjeszt\u00e9se \u00e9s m\u00f3dos\u00edt\u00e1sa megengedett \u00e9s \u00f6szt\u00f6nz\u00f6tt
a mell\u00e9kelt license.txt 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 http://activityworkshop.net/ 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 @@ -383,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 @@ -398,7 +474,7 @@ dialog.checkversion.newversion1=El\u00e9rhet\u0151 a GpsPrune \u00faj verzi\u00f dialog.checkversion.newversion2=. 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=
Ny\u00edlbillenty\u0171kT\u00e9rk\u00e9p mozgat\u00e1sa balra, jobbra, fel, le
Ctrl + bal, jobb ny\u00edlEl\u0151z\u0151 vagy k\u00f6vetkez\u0151 pont kiv\u00e1laszt\u00e1sa
Ctrl + fel, le ny\u00edlNagy\u00edt\u00e1s vagy kicsiny\u00edt\u00e9s
Ctrl + PgUp, PgDownEl\u0151z\u0151, k\u00f6vetkez\u0151 szakasz kiv\u00e1laszt\u00e1sa
Ctrl + Home, EndEls\u0151, utols\u00f3 pont kiv\u00e1laszt\u00e1sa
DelJelenlegi pont t\u00f6rl\u00e9se
dialog.keys.normalmodifier=Ctrl @@ -425,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 @@ -461,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 @@ -485,11 +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 @@ -503,13 +599,22 @@ 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.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 @@ -527,6 +632,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 @@ -541,6 +647,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 @@ -558,6 +665,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 @@ -605,7 +713,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 @@ -613,7 +721,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 @@ -628,20 +735,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 @@ -667,6 +788,8 @@ 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 @@ -689,10 +812,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 @@ -703,7 +824,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 @@ -728,3 +849,7 @@ error.cache.notthere=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem 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_it.properties b/tim/prune/lang/prune-texts_it.properties index 5ee22f0..63c70ea 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 @@ -487,6 +494,7 @@ dialog.saveconfig.prune.mapsource=Selezionale la fonte delle mappe dialog.saveconfig.prune.mapsourcelist=Fonte delle mappe dialog.saveconfig.prune.diskcache=Cache delle mappe dialog.saveconfig.prune.kmzimagewidth=larghezza immagine KMZ +dialog.saveconfig.prune.kmzimageheight=altezza immagine KMZ dialog.saveconfig.prune.colourscheme=Schema colori dialog.saveconfig.prune.linewidth=Spessore linea dialog.saveconfig.prune.kmltrackcolour=Colore della traccia KML @@ -530,13 +538,30 @@ 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 # 3d window dialog.3d.title=Visione GpsPrune in 3D @@ -549,7 +574,7 @@ 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 @@ -558,8 +583,7 @@ confirm.rearrangephotos=Foto riorganizzate confirm.cutandmove=Selezione spostata confirm.interpolate=Aggiungi punto confirm.convertnamestotimes=Nome del waypoint convertito -confirm.saveexif.ok1=Salvato -confirm.saveexif.ok2=foto +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 +597,16 @@ 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.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 +624,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 @@ -720,6 +748,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 +760,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 +782,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 +806,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 diff --git a/tim/prune/lang/prune-texts_ja.properties b/tim/prune/lang/prune-texts_ja.properties index 78f2dc4..5e09c49 100644 --- a/tim/prune/lang/prune-texts_ja.properties +++ b/tim/prune/lang/prune-texts_ja.properties @@ -296,7 +296,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 +336,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 +371,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=
\u77e2\u5370\u30ad\u30fc\u5730\u56f3\u3092\u4e0a\u4e0b\u5de6\u53f3\u306b\u79fb\u52d5
Ctrl + \u5de6\u30fb\u53f3\u77e2\u5370\u524d\u30fb\u6b21\u306e\u70b9\u3092\u9078\u629e
Ctrl + \u4e0a\u30fb\u4e0b\u77e2\u5370\u62e1\u5927\u30fb\u7e2e\u5c0f
Del\u73fe\u5728\u306e\u70b9\u3092\u524a\u9664
dialog.keys.normalmodifier=Ctrl\u30ad\u30fc @@ -390,7 +390,8 @@ 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.kmzimageheight=KML \u753b\u50cf\u9ad8 dialog.saveconfig.prune.colourscheme=\u8272\u306e\u30b9\u30ad\u30fc\u30e0 dialog.saveconfig.prune.linewidth=\u7dda\u306e\u5e45 dialog.saveconfig.prune.kmltrackcolour=KML \u30c8\u30e9\u30c3\u30af\u306e\u8272 @@ -440,8 +441,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 +455,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 +561,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 +632,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 70f895c..583c2c5 100644 --- a/tim/prune/lang/prune-texts_ko.properties +++ b/tim/prune/lang/prune-texts_ko.properties @@ -295,7 +295,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 +339,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 +378,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=
\ud654\uc0b4\ud45c \ud0a4\uc88c,\uc6b0,\uc544\ub798,\uc704\ub85c \uc9c0\ub3c4 \uc774\ub3d9
Ctrl + \uc67c\ucabd, \uc624\ub978\ucabd \ud654\uc0b4\ud45c\uc9c0\uc810 \uc120\ud0dd \uc774\ub3d9
Ctrl + \uc704, \uc544\ub798 \ud654\uc0b4\ud45c\uc9c0\ub3c4 \ud655\ub300 \ucd95\uc18c
Ctrl + PgUp, PgDown\uc774\uc804 \uc774\ud6c4 \ubd80\ubd84 \uc120\ud0dd
Ctrl + Home, End\ucc98\uc74c \uc9c0\uc810, \ub9c8\uc9c0\ub9c9 \uc9c0\uc810 \uc120\ud0dd
Del\ud604\uc7ac \uc9c0\uc810 \uc0ad\uc81c
dialog.keys.normalmodifier=Ctrl @@ -399,6 +398,7 @@ dialog.saveconfig.prune.mapsource=\uc120\ud0dd\ub41c \uc9c0\ub3c4 \uc704\uce58 dialog.saveconfig.prune.mapsourcelist=\uc9c0\ub3c4 \uc18c\uc2a4 dialog.saveconfig.prune.diskcache=\uc9c0\ub3c4 \uce90\uc2dc dialog.saveconfig.prune.kmzimagewidth=KMZ \uc774\ubbf8\uc9c0 \ub113\uc774 +dialog.saveconfig.prune.kmzimageheight=KMZ \uc774\ubbf8\uc9c0 \ub192\uc774 dialog.saveconfig.prune.colourscheme=\uc0c9 \uad6c\uc131 dialog.saveconfig.prune.linewidth=\ud2b8\ub799\uc120 \ub450\uaed8 dialog.saveconfig.prune.kmltrackcolour=KML \ud2b8\ub799 \uc0c9 @@ -453,8 +453,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 +467,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 +576,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 +646,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. diff --git a/tim/prune/lang/prune-texts_nl.properties b/tim/prune/lang/prune-texts_nl.properties index 1ec6b81..8c0e8a9 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=
Pijltjestoetsenverschuif de kaart links, rechts, omhoog, omlaag
Ctrl + pijltje naar links, rechtsSelecteer volgende, vorige punt
Ctrl + pijltje omhoog, omlaagZoom in of uit
Ctrl + PgUp, PgDownSelecteer vorig, volgend segment
Ctrl + Home, EndSelect eerste, laatste punt
DelVerwijder huidige punt
dialog.keys.normalmodifier=Ctrl @@ -530,13 +537,31 @@ 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.creditnotice=Deze gegevens worden beschikbaar gesteld door openweathermap.org. Hun website heeft meer details. # 3d window dialog.3d.title=GpsPrune in 3D @@ -555,11 +580,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 +599,22 @@ 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.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)\nkan je de afbeeldsnelheid verbeteren en het netwerkverkeer verminderen. +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 +632,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 +721,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 +755,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 +765,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 +787,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 +811,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 @@ -811,3 +849,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 333caa7..212eb69 100644 --- a/tim/prune/lang/prune-texts_pl.properties +++ b/tim/prune/lang/prune-texts_pl.properties @@ -107,6 +107,7 @@ 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 @@ -135,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 @@ -200,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 @@ -225,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=KML ze znacznikami czasu +dialog.exportkml.extendedkml=Standardowy KML ze znacznikami czasu dialog.exportgpx.name=Nazwa dialog.exportgpx.desc=Opis dialog.exportgpx.includetimestamps=Do\u0142\u0105cz znaczniki czasu @@ -257,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 @@ -378,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 @@ -421,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 @@ -467,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=
klawisze strza\u0142ekPrzesuwa map\u0119 w lewo, w prawo, w g\u00f3r\u0119, w d\u00f3\u0142
Ctrl + lewa, prawa strza\u0142kaWybierz punkt poprzedni lub nast\u0119pny
Ctrl + strza\u0142ka w g\u00f3r\u0119, w d\u00f3\u0142Powi\u0119ksz, pomniejsz
DelUsun bie\u017c\u0105cy punkt
dialog.keys.normalmodifier=Ctrl @@ -530,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 @@ -558,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 @@ -573,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 @@ -597,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 @@ -685,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 @@ -720,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 @@ -728,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 @@ -771,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 diff --git a/tim/prune/lang/prune-texts_pt.properties b/tim/prune/lang/prune-texts_pt.properties index dc9a803..f70a01e 100644 --- a/tim/prune/lang/prune-texts_pt.properties +++ b/tim/prune/lang/prune-texts_pt.properties @@ -7,9 +7,11 @@ 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 @@ -20,8 +22,6 @@ menu.range.all=Selectionar tudo menu.range.none=N\u00e3o selecionar nenhuns menu.range.start=Definir in\u00edcio do intervalo menu.range.end=Definir fim do intervalo -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 @@ -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,11 +102,16 @@ 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.downloadsrtm=Baixar arquivos SRTM function.getwikipedia=Obter artigos da Wikipedia das redondezas function.searchwikipedianames=Procurar na Wikipedia por nome function.downloadosm=Baixar dados OSM para a \u00e1rea @@ -129,6 +140,7 @@ 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 @@ -139,6 +151,7 @@ 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 @@ -187,7 +228,10 @@ 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.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 @@ -204,11 +248,25 @@ 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.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 @@ -220,6 +278,7 @@ dialog.confirmreversetrack.text=Esta rota possui informa\u00e7\u00f5es de data-h dialog.confirmcutandmove.title=Confirmar cortar e mover dialog.confirmcutandmove.text=A rota cont\u00e9m informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s o movimento.\n Voc\u00ea tem certeza que deseja mover esta se\u00e7\u00e3o? dialog.interpolate.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 @@ -229,6 +288,7 @@ dialog.clearundo.text=Voc\u00ea tem certeza que deseja limpar a lista de desfaze 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 @@ -299,11 +382,11 @@ 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.nouncorrelatedaudios=Existem \u00e1udios n\u00e3o correlacionados.\nVoc\u00ea tem certeza 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=
CursoresMove o mapa para esquerda, direita, acima e abaixo
Ctrl + cursores esquerdo e direitoSeleciona o pr\u00f3ximo ponto ou o anterior
Ctrl + cursores acima e abaixoAmplia ou reduz
DelRemove o ponto atual
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,31 @@ 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.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 +580,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 +599,22 @@ 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.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 +632,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 +647,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 +658,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 +702,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 +721,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 +735,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 +784,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 +813,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 @@ -705,7 +844,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 3a691c8..ce483bd 100644 --- a/tim/prune/lang/prune-texts_ru.properties +++ b/tim/prune/lang/prune-texts_ru.properties @@ -318,7 +318,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\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438. 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 +360,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,8 +468,7 @@ 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) @@ -498,8 +495,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 +509,16 @@ 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.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 +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\u0447\u0435\u0441\u043a\u0438. + # Buttons button.ok=OK button.back=\u041d\u0430\u0437\u0430\u0434 @@ -623,7 +622,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 +708,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 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..e509978 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=
Ok tu\u015flar\u0131Haritay\u0131 sola/sa\u011fa/a\u015fa\u011f\u0131/yukar\u0131 kayd\u0131r
Ctrl + sol, sa\u011f\u00d6nceki/sonraki noktay\u0131 se\u00e7
Ctrl + yukar/a\u015fa\u011f\u0131Yak\u0131nla\u015ft\u0131r/Uzakla\u015ft\u0131r
DelSe\u00e7ili noltay\u0131 sil
dialog.saveconfig.desc=A\u011fa\u015f\u0131daki ayarlar\u0131 bir dasyada kaydedilir: @@ -269,12 +269,13 @@ dialog.saveconfig.prune.exiftoolpath=exiftool'un yeriyolu dialog.saveconfig.prune.mapserverindex=Harita sunucunun index dialog.saveconfig.prune.mapserverurl=Harita sunucunun adresi dialog.saveconfig.prune.kmzimagewidth=KMZ resim geni\u015fli\u011fi +dialog.saveconfig.prune.kmzimageheight=KMZ resim y\u00fcksekli\u011fi dialog.setpaths.intro=\u0130ste\u011fe ba\u011fl\u0131 a\u015fa\u011f\u0131daki uygulamalar\u0131n veriyolu kaydedebilirsin: dialog.addaltitude.noaltitudes=Se\u00e7ili s\u0131rada y\u00fckseklik bilgisi bulunmad\u0131 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 +301,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 +401,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..d022513 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,22 @@ 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.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 +632,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 +721,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 +755,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 +767,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 +789,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 +812,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 +825,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 +850,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..16030ca 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); } diff --git a/tim/prune/load/BabelLoadFromFile.java b/tim/prune/load/BabelLoadFromFile.java index 1897e0c..187bb49 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 _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(BabelFileFormats.getDescriptions()); grid.add(_formatDropdown); gridPanel.setAlignmentX(Component.CENTER_ALIGNMENT); gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 20)); @@ -226,10 +226,8 @@ 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); } } diff --git a/tim/prune/load/FileCacher.java b/tim/prune/load/FileCacher.java index 22383c6..a151c2e 100644 --- a/tim/prune/load/FileCacher.java +++ b/tim/prune/load/FileCacher.java @@ -49,7 +49,7 @@ public class FileCacher { if (currLine.indexOf('\0') >= 0) { - try {reader.close();} catch (IOException ioe2) {} + 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 _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 _altitudeUnitsDropdown = null; + private JComboBox _hSpeedUnitsDropdown = null; + private JComboBox _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(_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(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(); 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(); 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 fieldTypesBox = new JComboBox(); String[] fieldNames = Field.getFieldNames(); for (int i=0; i _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(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 _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(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 _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(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 _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(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 4bf30f1..93303b2 100644 --- a/tim/prune/readme.txt +++ b/tim/prune/readme.txt @@ -1,11 +1,11 @@ -GpsPrune version 15.2 -===================== +GpsPrune version 16 +=================== 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.2.jar + java -jar gpsprune_16.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,21 @@ 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.2.jar --lang=DE + java -jar gpsprune_16.jar --lang=DE -New with version 15.2 +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 +53,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 _mapSourceDropdown = null; /** Dropdown for zoom levels */ - private JComboBox _zoomDropdown = null; - /** Warning label that image is incomplete */ - private JLabel _imageIncompleteLabel = null; + private JComboBox _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; @@ -83,6 +85,17 @@ public class BaseImageConfigDialog implements Runnable _track = inTrack; } + /** + * @param inDefinition image definition object from previous dialog + */ + public void setImageDefinition(ImageDefinition inDefinition) + { + _imageDef = inDefinition; + if (_imageDef == null) { + _imageDef = new ImageDefinition(); + } + } + /** * Begin the function */ @@ -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= _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(); _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(); // 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..a4ac1a7 100644 --- a/tim/prune/save/GpxExporter.java +++ b/tim/prune/save/GpxExporter.java @@ -694,6 +694,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 = " 0; } - /** - * @return true if all the required tiles were found - */ - public boolean isComplete() { - return _numTilesMissing == 0; - } - /** * @return the pixel dimensions of the result image */ 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/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 135ccbb..1f29d4a 100644 --- a/tim/prune/save/PovExporter.java +++ b/tim/prune/save/PovExporter.java @@ -26,27 +26,30 @@ 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.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 +58,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 +111,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 +129,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 +161,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 +177,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 +240,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 +254,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 +301,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 +339,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 +358,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 +388,39 @@ public class PovExporter extends Export3dFunction implements DataSubscriber } } + boolean useTerrain = _terrainPanel.getUseTerrain(); + if (useTerrain) + { + TerrainHelper terrainHelper = new TerrainHelper(_terrainPanel.getGridSize()); + Track 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); + + 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 +459,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 +482,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 +588,8 @@ public class PovExporter extends Export3dFunction implements DataSubscriber "box {", boxDefinition, "}", "", + // terrain + terrainDefinition, // write cardinals "// Cardinal letters N,S,E,W", "text {", @@ -612,6 +628,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 @@ -876,16 +917,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/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..ba1ed15 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,25 @@ 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.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 +68,10 @@ 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; /** only prompt about big track size once */ private static boolean TRACK_SIZE_WARNING_GIVEN = false; @@ -90,23 +103,44 @@ 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; + } /** * 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 +304,64 @@ 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) + { + // TODO: Is it maybe possible to cache the last terrainTrack? + // (if the dataTrack and the resolution haven't changed) + // Construct the terrain track according to these extents and the grid size + TerrainHelper terrainHelper = new TerrainHelper(_terrainDefinition.getGridSize()); + Track 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); + + // 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 +381,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 +494,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 +506,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 +564,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 2; + } + + /** + * @return grid size + */ + public int getGridSize() { + return _gridSize; + } +} diff --git a/tim/prune/threedee/TerrainHelper.java b/tim/prune/threedee/TerrainHelper.java new file mode 100644 index 0000000..71bc196 --- /dev/null +++ b/tim/prune/threedee/TerrainHelper.java @@ -0,0 +1,423 @@ +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 -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 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; @@ -36,6 +37,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 */ @@ -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,12 +142,47 @@ 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..b7c73d2 100644 --- a/tim/prune/threedee/ThreeDWindow.java +++ b/tim/prune/threedee/ThreeDWindow.java @@ -14,6 +14,20 @@ 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); /** * 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= 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