From 92dad5df664287acb51728e9ea599f150765d34a Mon Sep 17 00:00:00 2001 From: activityworkshop Date: Fri, 18 May 2018 16:17:37 +0200 Subject: [PATCH] Version 19, May 2018 --- README.md | 2 +- tim/prune/App.java | 8 +- tim/prune/FunctionLibrary.java | 59 +- tim/prune/GpsPrune.java | 8 +- tim/prune/config/Config.java | 7 + tim/prune/config/TimezoneHelper.java | 22 + tim/prune/copyright.txt | 8 +- tim/prune/correlate/AudioCorrelator.java | 11 +- tim/prune/correlate/Correlator.java | 57 +- .../correlate/MediaPreviewTableModel.java | 11 +- .../correlate/MediaSelectionTableModel.java | 9 +- tim/prune/data/AltitudeRange.java | 8 +- tim/prune/data/AudioClip.java | 2 +- tim/prune/data/DataPoint.java | 10 +- tim/prune/data/GradientCalculator.java | 2 +- tim/prune/data/IntegerRange.java | 22 +- tim/prune/data/SpeedCalculator.java | 6 +- tim/prune/data/Timestamp.java | 485 ++-------- tim/prune/data/TimestampLocal.java | 104 ++ tim/prune/data/TimestampUtc.java | 399 ++++++++ tim/prune/data/Track.java | 4 +- tim/prune/function/AboutScreen.java | 35 +- tim/prune/function/AddTimeOffset.java | 7 +- tim/prune/function/CheckVersionScreen.java | 2 +- tim/prune/function/ConvertNamesToTimes.java | 7 +- .../CreateMarkerWaypointsFunction.java | 145 +++ .../function/DistanceTimeLimitFunction.java | 275 ++++++ tim/prune/function/DownloadOsmFunction.java | 7 +- tim/prune/function/Export3dFunction.java | 2 +- tim/prune/function/HelpScreen.java | 2 +- tim/prune/function/SearchOsmPoisFunction.java | 179 ++++ .../function/SearchOsmPoisXmlHandler.java | 92 ++ .../function/SelectTimezoneFunction.java | 640 ++++++++++++ tim/prune/function/SetLineWidth.java | 58 -- tim/prune/function/browser/UrlGenerator.java | 15 - tim/prune/function/deletebydate/DateInfo.java | 18 +- .../function/deletebydate/DateInfoList.java | 32 +- .../deletebydate/DeleteByDateFunction.java | 32 +- .../deletebydate/DeletionTableModel.java | 7 +- .../estimate/jama/QRDecomposition.java | 73 -- .../function/gpsies/GpsiesXmlHandler.java | 2 +- tim/prune/function/gpsies/TrackListModel.java | 20 +- .../search/GenericDownloaderFunction.java | 6 +- tim/prune/function/search/SearchResult.java | 20 +- .../function/search/wikimedia_galleries.txt | 586 ++++++++--- .../{ => settings}/AddMapSourceDialog.java | 2 +- .../{ => settings}/MapSourceListModel.java | 2 +- .../function/{ => settings}/SaveConfig.java | 2 +- .../{ => settings}/SetAltitudeTolerance.java | 3 +- .../function/{ => settings}/SetColours.java | 2 +- .../function/settings/SetDisplaySettings.java | 274 ++++++ .../function/{ => settings}/SetLanguage.java | 8 +- .../{ => settings}/SetMapBgFunction.java | 2 +- .../{ => settings}/SetPathsFunction.java | 2 +- .../function/sew/SplitSegmentsFunction.java | 220 +---- tim/prune/gui/CombinedListAndModel.java | 110 +++ tim/prune/gui/DetailsDisplay.java | 28 +- tim/prune/gui/IconManager.java | 5 + tim/prune/gui/ImageUtils.java | 3 + tim/prune/gui/MenuManager.java | 41 +- ...eCheckBox.java => MultiStateCheckBox.java} | 40 +- tim/prune/gui/PhotoThumbnail.java | 6 +- tim/prune/gui/colour/ColourerCaretaker.java | 2 +- tim/prune/gui/colour/DateColourer.java | 35 +- tim/prune/gui/images/points_arrows.png | Bin 0 -> 649 bytes tim/prune/gui/images/points_connected.png | Bin 586 -> 592 bytes tim/prune/gui/images/points_disconnected.png | Bin 513 -> 498 bytes tim/prune/gui/images/points_hidden.png | Bin 403 -> 416 bytes tim/prune/gui/images/wpicon_default_m.png | Bin 0 -> 138 bytes tim/prune/gui/images/wpicon_pin_l.png | Bin 0 -> 554 bytes tim/prune/gui/images/wpicon_pin_m.png | Bin 0 -> 492 bytes tim/prune/gui/images/wpicon_pin_s.png | Bin 0 -> 398 bytes tim/prune/gui/images/wpicon_plectrum_l.png | Bin 0 -> 348 bytes tim/prune/gui/images/wpicon_plectrum_m.png | Bin 0 -> 314 bytes tim/prune/gui/images/wpicon_plectrum_s.png | Bin 0 -> 271 bytes tim/prune/gui/images/wpicon_ring_l.png | Bin 0 -> 277 bytes tim/prune/gui/images/wpicon_ring_m.png | Bin 0 -> 257 bytes tim/prune/gui/images/wpicon_ring_s.png | Bin 0 -> 216 bytes tim/prune/gui/images/wpicon_ringpt_l.png | Bin 0 -> 420 bytes tim/prune/gui/images/wpicon_ringpt_m.png | Bin 0 -> 309 bytes tim/prune/gui/images/wpicon_ringpt_s.png | Bin 0 -> 277 bytes tim/prune/gui/map/MapCanvas.java | 164 ++-- tim/prune/gui/map/MapSourceLibrary.java | 10 +- tim/prune/gui/map/OsmMapSource.java | 20 +- tim/prune/gui/map/WpIconDefinition.java | 48 + tim/prune/gui/map/WpIconLibrary.java | 92 ++ tim/prune/jpeg/ExifGateway.java | 108 --- tim/prune/jpeg/ExifLibrary.java | 15 - tim/prune/jpeg/ExifLibrarySwitch.java | 10 - tim/prune/jpeg/ExternalExifLibrary.java | 150 --- tim/prune/jpeg/InternalExifLibrary.java | 26 +- tim/prune/jpeg/drew/ByteArrayReader.java | 224 +++++ tim/prune/jpeg/drew/ExifException.java | 12 + tim/prune/jpeg/drew/ExifReader.java | 773 ++++----------- tim/prune/jpeg/drew/ExifTiffHandler.java | 171 ++++ tim/prune/jpeg/drew/JpegException.java | 25 - tim/prune/jpeg/drew/JpegSegmentData.java | 106 -- tim/prune/jpeg/drew/JpegSegmentReader.java | 130 --- tim/prune/jpeg/drew/Rational.java | 208 ++-- tim/prune/jpeg/drew/TiffDataFormat.java | 54 ++ tim/prune/jpeg/drew/TiffProcessor.java | 298 ++++++ tim/prune/lang/prune-texts_af.properties | 20 +- tim/prune/lang/prune-texts_cz.properties | 30 +- tim/prune/lang/prune-texts_da.properties | 1 - tim/prune/lang/prune-texts_de.properties | 49 +- tim/prune/lang/prune-texts_de_CH.properties | 71 +- tim/prune/lang/prune-texts_en.properties | 48 +- tim/prune/lang/prune-texts_es.properties | 38 +- tim/prune/lang/prune-texts_fi.properties | 909 ++++++++++++++++++ tim/prune/lang/prune-texts_fr.properties | 25 +- tim/prune/lang/prune-texts_hu.properties | 24 +- tim/prune/lang/prune-texts_it.properties | 47 +- tim/prune/lang/prune-texts_ja.properties | 11 +- tim/prune/lang/prune-texts_ko.properties | 20 +- tim/prune/lang/prune-texts_nl.properties | 51 +- tim/prune/lang/prune-texts_no.properties | 140 +++ tim/prune/lang/prune-texts_pl.properties | 30 +- tim/prune/lang/prune-texts_pt.properties | 17 +- tim/prune/lang/prune-texts_ro.properties | 17 +- tim/prune/lang/prune-texts_ru.properties | 210 ++-- tim/prune/lang/prune-texts_sv.properties | 1 - tim/prune/lang/prune-texts_uk.properties | 2 - tim/prune/lang/prune-texts_zh.properties | 18 +- tim/prune/load/FieldGuesser.java | 4 +- tim/prune/load/FileLoader.java | 2 +- tim/prune/load/JpegLoader.java | 15 +- tim/prune/load/TextFileLoader.java | 5 + tim/prune/readme.txt | 61 +- tim/prune/save/FileSaver.java | 2 +- tim/prune/save/GpxExporter.java | 6 +- tim/prune/save/ImageExporter.java | 21 +- tim/prune/save/KmlExporter.java | 2 +- tim/prune/save/PovExporter.java | 4 +- tim/prune/save/SvgExporter.java | 520 ---------- tim/prune/save/SvgFragment.java | 56 -- tim/prune/threedee/Java3DWindow.java | 19 +- tim/prune/undo/UndoAddTimeOffset.java | 6 +- tim/prune/undo/UndoAppendPoints.java | 51 + 138 files changed, 6005 insertions(+), 3485 deletions(-) create mode 100644 tim/prune/config/TimezoneHelper.java create mode 100644 tim/prune/data/TimestampLocal.java create mode 100644 tim/prune/data/TimestampUtc.java create mode 100644 tim/prune/function/CreateMarkerWaypointsFunction.java create mode 100644 tim/prune/function/DistanceTimeLimitFunction.java create mode 100644 tim/prune/function/SearchOsmPoisFunction.java create mode 100644 tim/prune/function/SearchOsmPoisXmlHandler.java create mode 100644 tim/prune/function/SelectTimezoneFunction.java delete mode 100644 tim/prune/function/SetLineWidth.java rename tim/prune/function/{ => settings}/AddMapSourceDialog.java (99%) rename tim/prune/function/{ => settings}/MapSourceListModel.java (96%) rename tim/prune/function/{ => settings}/SaveConfig.java (99%) rename tim/prune/function/{ => settings}/SetAltitudeTolerance.java (95%) rename tim/prune/function/{ => settings}/SetColours.java (99%) create mode 100644 tim/prune/function/settings/SetDisplaySettings.java rename tim/prune/function/{ => settings}/SetLanguage.java (97%) rename tim/prune/function/{ => settings}/SetMapBgFunction.java (99%) rename tim/prune/function/{ => settings}/SetPathsFunction.java (99%) create mode 100644 tim/prune/gui/CombinedListAndModel.java rename tim/prune/gui/{TripleStateCheckBox.java => MultiStateCheckBox.java} (62%) create mode 100644 tim/prune/gui/images/points_arrows.png create mode 100644 tim/prune/gui/images/wpicon_default_m.png create mode 100644 tim/prune/gui/images/wpicon_pin_l.png create mode 100644 tim/prune/gui/images/wpicon_pin_m.png create mode 100644 tim/prune/gui/images/wpicon_pin_s.png create mode 100644 tim/prune/gui/images/wpicon_plectrum_l.png create mode 100644 tim/prune/gui/images/wpicon_plectrum_m.png create mode 100644 tim/prune/gui/images/wpicon_plectrum_s.png create mode 100644 tim/prune/gui/images/wpicon_ring_l.png create mode 100644 tim/prune/gui/images/wpicon_ring_m.png create mode 100644 tim/prune/gui/images/wpicon_ring_s.png create mode 100644 tim/prune/gui/images/wpicon_ringpt_l.png create mode 100644 tim/prune/gui/images/wpicon_ringpt_m.png create mode 100644 tim/prune/gui/images/wpicon_ringpt_s.png create mode 100644 tim/prune/gui/map/WpIconDefinition.java create mode 100644 tim/prune/gui/map/WpIconLibrary.java delete mode 100644 tim/prune/jpeg/ExifGateway.java delete mode 100644 tim/prune/jpeg/ExifLibrary.java delete mode 100644 tim/prune/jpeg/ExifLibrarySwitch.java delete mode 100644 tim/prune/jpeg/ExternalExifLibrary.java create mode 100644 tim/prune/jpeg/drew/ByteArrayReader.java create mode 100644 tim/prune/jpeg/drew/ExifException.java create mode 100644 tim/prune/jpeg/drew/ExifTiffHandler.java delete mode 100644 tim/prune/jpeg/drew/JpegException.java delete mode 100644 tim/prune/jpeg/drew/JpegSegmentData.java delete mode 100644 tim/prune/jpeg/drew/JpegSegmentReader.java create mode 100644 tim/prune/jpeg/drew/TiffDataFormat.java create mode 100644 tim/prune/jpeg/drew/TiffProcessor.java create mode 100644 tim/prune/lang/prune-texts_fi.properties create mode 100644 tim/prune/lang/prune-texts_no.properties delete mode 100644 tim/prune/save/SvgExporter.java delete mode 100644 tim/prune/save/SvgFragment.java create mode 100644 tim/prune/undo/UndoAppendPoints.java diff --git a/README.md b/README.md index 27af085..5820cd0 100644 --- a/README.md +++ b/README.md @@ -3,5 +3,5 @@ GpsPrune is a map-based application for viewing, editing and converting coordina It's a cross-platform java application, and its home page is at https://gpsprune.activityworkshop.net . -Here on github you'll find all the sources from version 1 to the current version 18.6, and in the wiki at https://github.com/activityworkshop/GpsPrune/wiki there's the beginning of a translation effort for anyone to contribute. +Here on github you'll find all the sources from version 1 to the current version 19, and in the wiki at https://github.com/activityworkshop/GpsPrune/wiki there's the beginning of a translation effort for anyone to contribute. Currently just the Spanish translations are online, to see whether it's a workable idea or not. Please help with this if you can. diff --git a/tim/prune/App.java b/tim/prune/App.java index 81e59cc..3a77858 100644 --- a/tim/prune/App.java +++ b/tim/prune/App.java @@ -24,10 +24,10 @@ import tim.prune.data.TrackInfo; import tim.prune.data.SourceInfo.FILE_TYPE; import tim.prune.data.Unit; import tim.prune.function.AsyncMediaLoader; -import tim.prune.function.SaveConfig; import tim.prune.function.SelectTracksFunction; import tim.prune.function.edit.FieldEditList; import tim.prune.function.edit.PointEditor; +import tim.prune.function.settings.SaveConfig; import tim.prune.gui.MenuManager; import tim.prune.gui.SidebarController; import tim.prune.gui.UndoManager; @@ -402,16 +402,16 @@ public class App } /** - * Complete the add time offset function with the specified offset + * Complete the add time offset function with the specified offset in seconds * @param inTimeOffset time offset to add (+ve for add, -ve for subtract) */ - public void finishAddTimeOffset(long inTimeOffset) + public void finishAddTimeOffsetSeconds(long inTimeOffset) { // Construct undo information int selStart = _trackInfo.getSelection().getStart(); int selEnd = _trackInfo.getSelection().getEnd(); UndoAddTimeOffset undo = new UndoAddTimeOffset(selStart, selEnd, inTimeOffset); - if (_trackInfo.getTrack().addTimeOffset(selStart, selEnd, inTimeOffset, false)) + if (_trackInfo.getTrack().addTimeOffsetSeconds(selStart, selEnd, inTimeOffset, false)) { _undoStack.add(undo); UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_EDITED); diff --git a/tim/prune/FunctionLibrary.java b/tim/prune/FunctionLibrary.java index 81e5e0f..0441826 100644 --- a/tim/prune/FunctionLibrary.java +++ b/tim/prune/FunctionLibrary.java @@ -2,7 +2,43 @@ package tim.prune; import tim.prune.correlate.AudioCorrelator; import tim.prune.correlate.PhotoCorrelator; -import tim.prune.function.*; +import tim.prune.function.AboutScreen; +import tim.prune.function.AddAltitudeOffset; +import tim.prune.function.AddTimeOffset; +import tim.prune.function.CheckVersionScreen; +import tim.prune.function.ConnectToPointFunction; +import tim.prune.function.ConvertNamesToTimes; +import tim.prune.function.CreateMarkerWaypointsFunction; +import tim.prune.function.CropToSelection; +import tim.prune.function.DeleteFieldValues; +import tim.prune.function.DeleteSelectedRangeFunction; +import tim.prune.function.DisconnectAudioFunction; +import tim.prune.function.DisconnectPhotoFunction; +import tim.prune.function.DiskCacheConfig; +import tim.prune.function.DownloadOsmFunction; +import tim.prune.function.DuplicatePoint; +import tim.prune.function.FindWaypoint; +import tim.prune.function.FullRangeDetails; +import tim.prune.function.GetWikipediaFunction; +import tim.prune.function.HelpScreen; +import tim.prune.function.IgnoreExifThumb; +import tim.prune.function.InterpolateFunction; +import tim.prune.function.PasteCoordinates; +import tim.prune.function.PhotoPopupFunction; +import tim.prune.function.PlayAudioFunction; +import tim.prune.function.RearrangePhotosFunction; +import tim.prune.function.RearrangeWaypointsFunction; +import tim.prune.function.RemoveAudioFunction; +import tim.prune.function.RemovePhotoFunction; +import tim.prune.function.RotatePhoto; +import tim.prune.function.SearchOsmPoisFunction; +import tim.prune.function.SearchWikipediaNames; +import tim.prune.function.SelectSegmentFunction; +import tim.prune.function.SelectTimezoneFunction; +import tim.prune.function.ShowKeysScreen; +import tim.prune.function.ShowThreeDFunction; +import tim.prune.function.SingleNumericParameterFunction; +import tim.prune.function.StopAudioFunction; import tim.prune.function.autoplay.AutoplayFunction; import tim.prune.function.charts.Charter; import tim.prune.function.compress.CompressTrackFunction; @@ -16,6 +52,13 @@ 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.settings.SaveConfig; +import tim.prune.function.settings.SetAltitudeTolerance; +import tim.prune.function.settings.SetColours; +import tim.prune.function.settings.SetDisplaySettings; +import tim.prune.function.settings.SetLanguage; +import tim.prune.function.settings.SetMapBgFunction; +import tim.prune.function.settings.SetPathsFunction; import tim.prune.function.sew.SewTrackSegmentsFunction; import tim.prune.function.sew.SplitSegmentsFunction; import tim.prune.function.srtm.DownloadSrtmFunction; @@ -29,7 +72,7 @@ import tim.prune.save.GpxExporter; import tim.prune.save.ImageExporter; import tim.prune.save.KmlExporter; import tim.prune.save.PovExporter; -import tim.prune.save.SvgExporter; + /** * Class to provide access to functions @@ -39,7 +82,6 @@ public abstract class FunctionLibrary public static GenericFunction FUNCTION_GPXEXPORT = null; public static GenericFunction FUNCTION_KMLEXPORT = null; public static PovExporter FUNCTION_POVEXPORT = null; - public static SvgExporter FUNCTION_SVGEXPORT = null; public static GenericFunction FUNCTION_IMAGEEXPORT = null; public static GenericFunction FUNCTION_GPSLOAD = null; public static GenericFunction FUNCTION_GPSSAVE = null; @@ -50,6 +92,7 @@ public abstract class FunctionLibrary public static GenericFunction FUNCTION_SELECT_SEGMENT = null; public static GenericFunction FUNCTION_SPLIT_SEGMENTS = null; public static GenericFunction FUNCTION_SEW_SEGMENTS = null; + public static GenericFunction FUNCTION_CREATE_MARKER_WAYPOINTS = null; public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null; public static GenericFunction FUNCTION_COMPRESS = null; public static GenericFunction FUNCTION_MARK_LIFTS = null; @@ -63,6 +106,7 @@ public abstract class FunctionLibrary public static GenericFunction FUNCTION_DOWNLOAD_SRTM = null; public static GenericFunction FUNCTION_NEARBY_WIKIPEDIA = null; public static GenericFunction FUNCTION_SEARCH_WIKIPEDIA = null; + public static GenericFunction FUNCTION_SEARCH_OSMPOIS = null; public static GenericFunction FUNCTION_DOWNLOAD_OSM = null; public static GenericFunction FUNCTION_ADD_TIME_OFFSET = null; public static GenericFunction FUNCTION_ADD_ALTITUDE_OFFSET = null; @@ -95,13 +139,14 @@ public abstract class FunctionLibrary public static GenericFunction FUNCTION_CORRELATE_AUDIOS = null; public static GenericFunction FUNCTION_PLAY_AUDIO = null; public static GenericFunction FUNCTION_STOP_AUDIO = null; + public static GenericFunction FUNCTION_SET_DISPLAY_SETTINGS = null; public static GenericFunction FUNCTION_SET_MAP_BG = null; public static GenericFunction FUNCTION_SET_DISK_CACHE = null; public static GenericFunction FUNCTION_SET_PATHS = null; public static GenericFunction FUNCTION_SET_COLOURS = null; - public static SingleNumericParameterFunction FUNCTION_SET_LINE_WIDTH = null; public static GenericFunction FUNCTION_SET_LANGUAGE = null; public static SingleNumericParameterFunction FUNCTION_SET_ALTITUDE_TOLERANCE = null; + public static GenericFunction FUNCTION_SET_TIMEZONE = null; public static GenericFunction FUNCTION_HELP = null; public static GenericFunction FUNCTION_SHOW_KEYS = null; public static GenericFunction FUNCTION_ABOUT = null; @@ -117,7 +162,6 @@ public abstract class FunctionLibrary FUNCTION_GPXEXPORT = new GpxExporter(inApp); FUNCTION_KMLEXPORT = new KmlExporter(inApp); FUNCTION_POVEXPORT = new PovExporter(inApp); - FUNCTION_SVGEXPORT = new SvgExporter(inApp); FUNCTION_IMAGEEXPORT = new ImageExporter(inApp); FUNCTION_GPSLOAD = new BabelLoadFromGps(inApp); FUNCTION_GPSSAVE = new GpsSaver(inApp); @@ -128,6 +172,7 @@ public abstract class FunctionLibrary FUNCTION_SELECT_SEGMENT = new SelectSegmentFunction(inApp); FUNCTION_SPLIT_SEGMENTS = new SplitSegmentsFunction(inApp); FUNCTION_SEW_SEGMENTS = new SewTrackSegmentsFunction(inApp); + FUNCTION_CREATE_MARKER_WAYPOINTS = new CreateMarkerWaypointsFunction(inApp); FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp); FUNCTION_COMPRESS = new CompressTrackFunction(inApp); FUNCTION_MARK_LIFTS = new MarkLiftsFunction(inApp); @@ -141,6 +186,7 @@ public abstract class FunctionLibrary FUNCTION_DOWNLOAD_SRTM = new DownloadSrtmFunction(inApp); FUNCTION_NEARBY_WIKIPEDIA = new GetWikipediaFunction(inApp); FUNCTION_SEARCH_WIKIPEDIA = new SearchWikipediaNames(inApp); + FUNCTION_SEARCH_OSMPOIS = new SearchOsmPoisFunction(inApp); FUNCTION_DOWNLOAD_OSM = new DownloadOsmFunction(inApp); FUNCTION_ADD_TIME_OFFSET = new AddTimeOffset(inApp); FUNCTION_ADD_ALTITUDE_OFFSET = new AddAltitudeOffset(inApp); @@ -173,13 +219,14 @@ public abstract class FunctionLibrary FUNCTION_PLAY_AUDIO = new PlayAudioFunction(inApp); FUNCTION_STOP_AUDIO = new StopAudioFunction(inApp); FUNCTION_DISCONNECT_AUDIO = new DisconnectAudioFunction(inApp); + FUNCTION_SET_DISPLAY_SETTINGS = new SetDisplaySettings(inApp); FUNCTION_SET_MAP_BG = new SetMapBgFunction(inApp); FUNCTION_SET_DISK_CACHE = new DiskCacheConfig(inApp); FUNCTION_SET_PATHS = new SetPathsFunction(inApp); FUNCTION_SET_COLOURS = new SetColours(inApp); - FUNCTION_SET_LINE_WIDTH = new SetLineWidth(inApp); FUNCTION_SET_LANGUAGE = new SetLanguage(inApp); FUNCTION_SET_ALTITUDE_TOLERANCE = new SetAltitudeTolerance(inApp); + FUNCTION_SET_TIMEZONE = new SelectTimezoneFunction(inApp); FUNCTION_HELP = new HelpScreen(inApp); FUNCTION_SHOW_KEYS = new ShowKeysScreen(inApp); FUNCTION_ABOUT = new AboutScreen(inApp); diff --git a/tim/prune/GpsPrune.java b/tim/prune/GpsPrune.java index 492a457..67b197c 100644 --- a/tim/prune/GpsPrune.java +++ b/tim/prune/GpsPrune.java @@ -28,17 +28,17 @@ 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-2016 and made available through the Gnu GPL version 2. + * Please see the included readme.txt or https://activityworkshop.net + * This software is copyright activityworkshop.net 2006-2018 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 = "18.6"; + public static final String VERSION_NUMBER = "19"; /** Build number, just used for about screen */ - public static final String BUILD_NUMBER = "343"; + public static final String BUILD_NUMBER = "362"; /** Static reference to App object */ private static App APP = null; diff --git a/tim/prune/config/Config.java b/tim/prune/config/Config.java index d2d49c6..cd23dbd 100644 --- a/tim/prune/config/Config.java +++ b/tim/prune/config/Config.java @@ -99,6 +99,12 @@ public abstract class Config public static final String KEY_TERRAIN_GRID_SIZE = "prune.terraingridsize"; /** Key for altitude tolerance */ public static final String KEY_ALTITUDE_TOLERANCE = "prune.altitudetolerance"; + /** Key for waypoint icons to use */ + public static final String KEY_WAYPOINT_ICONS = "prune.waypointicons"; + /** Size of waypoint icons to use */ + public static final String KEY_WAYPOINT_ICON_SIZE = "prune.waypointiconsize"; + /** Id of selected timezone */ + public static final String KEY_TIMEZONE_ID = "prune.timezoneid"; /** Initialise the default properties */ @@ -194,6 +200,7 @@ public abstract class Config props.put(KEY_HEIGHT_EXAGGERATION, "100"); // 100%, no exaggeration props.put(KEY_TERRAIN_GRID_SIZE, "50"); props.put(KEY_ALTITUDE_TOLERANCE, "0"); // 0, all exact as before + props.put(KEY_WAYPOINT_ICON_SIZE, "1"); // medium size return props; } diff --git a/tim/prune/config/TimezoneHelper.java b/tim/prune/config/TimezoneHelper.java new file mode 100644 index 0000000..f3871d4 --- /dev/null +++ b/tim/prune/config/TimezoneHelper.java @@ -0,0 +1,22 @@ +package tim.prune.config; + +import java.util.TimeZone; + +public abstract class TimezoneHelper +{ + + /** + * @return the timezone selected in the Config + */ + public static TimeZone getSelectedTimezone() + { + final String zoneId = Config.getConfigString(Config.KEY_TIMEZONE_ID); + if (zoneId == null || zoneId.equals("")) { + return TimeZone.getDefault(); + } + else { + return TimeZone.getTimeZone(zoneId); + } + } + +} diff --git a/tim/prune/copyright.txt b/tim/prune/copyright.txt index e623180..3022774 100644 --- a/tim/prune/copyright.txt +++ b/tim/prune/copyright.txt @@ -1,9 +1,9 @@ -The source code of GpsPrune is copyright 2006-2016 activityworkshop.net +The source code of GpsPrune is copyright 2006-2018 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 -from Drew Noakes' "Metadata extractor" v2.3.1 which is -copyright Drew Noakes 2002-2006 and was placed in the public domain. +Portions of the package jpeg.drew were taken +from Drew Noakes' "Metadata extractor" v2.7.2 which is +copyright Drew Noakes 2002-2015 and released under Apache 2.0. Translations are copyright various contributors, some of whom are named in the source code and some preferred to remain anonymous. \ No newline at end of file diff --git a/tim/prune/correlate/AudioCorrelator.java b/tim/prune/correlate/AudioCorrelator.java index 432674e..f60d687 100644 --- a/tim/prune/correlate/AudioCorrelator.java +++ b/tim/prune/correlate/AudioCorrelator.java @@ -11,6 +11,7 @@ import tim.prune.App; import tim.prune.DataSubscriber; import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; +import tim.prune.config.TimezoneHelper; import tim.prune.data.AudioClip; import tim.prune.data.AudioList; import tim.prune.data.DataPoint; @@ -18,6 +19,7 @@ import tim.prune.data.MediaObject; import tim.prune.data.MediaList; import tim.prune.data.TimeDifference; import tim.prune.data.Timestamp; +import tim.prune.data.TimestampUtc; import tim.prune.undo.UndoCorrelateAudios; /** @@ -164,14 +166,19 @@ public class AudioCorrelator extends Correlator protected Timestamp getMediaTimestamp(MediaObject inMedia) { Timestamp tstamp = super.getMediaTimestamp(inMedia); + long mediaMillis = tstamp.getMilliseconds(TimezoneHelper.getSelectedTimezone()); try { AudioClip audio = (AudioClip) inMedia; int audioLength = audio.getLengthInSeconds(); // Each option is worth half the length of the audio clip, so need to divide by 2 int secsToAdd = audioLength * (_correlTimesSelector.getSelectedOption() - _fileTimesSelector.getSelectedOption()) / 2; - if (audioLength > 0 && secsToAdd != 0) { - tstamp = tstamp.createPlusOffset(secsToAdd); + if (audioLength > 0 && secsToAdd != 0) + { + mediaMillis += (secsToAdd * 1000L); + tstamp = new TimestampUtc(mediaMillis); + // Here we create a Utc timestamp but it's only temporary for the correlation + // so it will never have to react to timezone changes } } catch (ClassCastException cce) {} diff --git a/tim/prune/correlate/Correlator.java b/tim/prune/correlate/Correlator.java index 7e887ff..0aaafc3 100644 --- a/tim/prune/correlate/Correlator.java +++ b/tim/prune/correlate/Correlator.java @@ -6,8 +6,8 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Calendar; import java.util.Iterator; +import java.util.TimeZone; import java.util.TreeSet; import javax.swing.BorderFactory; @@ -27,6 +27,7 @@ import javax.swing.JTextField; import tim.prune.App; import tim.prune.GenericFunction; import tim.prune.I18nManager; +import tim.prune.config.TimezoneHelper; import tim.prune.data.DataPoint; import tim.prune.data.Distance; import tim.prune.data.Field; @@ -50,6 +51,7 @@ public abstract class Correlator extends GenericFunction protected JTable _previewTable = null; private boolean _previewEnabled = false; // flag required to enable preview function on final panel private boolean[] _cardEnabled = null; // flag for each card + private TimeZone _timezone = null; private JTextField _offsetHourBox = null, _offsetMinBox = null, _offsetSecBox = null; private JRadioButton _mediaLaterOption = null, _pointLaterOption = null; private JRadioButton _timeLimitRadio = null, _distLimitRadio = null; @@ -59,6 +61,7 @@ public abstract class Correlator extends GenericFunction private JButton _nextButton = null, _backButton = null; protected JButton _okButton = null; + /** * Constructor * @param inApp App object to report actions to @@ -110,13 +113,17 @@ public abstract class Correlator extends GenericFunction _dialog.getContentPane().add(makeDialogContents()); _dialog.pack(); } + _okButton.setEnabled(false); + // Init timezone to the currently selected one + _timezone = TimezoneHelper.getSelectedTimezone(); // Go to first available card int card = 0; _cardEnabled = null; - while (!isCardEnabled(card)) {card++;} + while (!isCardEnabled(card)) { + card++; + } _cards.showCard(card); showCard(0); // does set up and next/prev enabling - _okButton.setEnabled(false); if (!isCardEnabled(1)) { _app.showTip(TipManager.Tip_ManuallyCorrelateOne); } @@ -205,7 +212,7 @@ public abstract class Correlator extends GenericFunction && media.getOriginalStatus() == MediaObject.Status.NOT_CONNECTED) { // Calculate time difference, add to table model - long timeDiff = getMediaTimestamp(media).getSecondsSince(media.getDataPoint().getTimestamp()); + long timeDiff = getMediaTimestamp(media).getSecondsSince(media.getDataPoint().getTimestamp(), _timezone); model.addMedia(media, timeDiff); } } @@ -242,27 +249,6 @@ public abstract class Correlator extends GenericFunction } - /** - * @param inFirstTimestamp timestamp of first photo / audio object, or null if not available - * @return time difference of local time zone from UTC when the first photo was taken - */ - private static TimeDifference getTimezoneOffset(Timestamp inFirstTimestamp) - { - Calendar cal = null; - // Use first timestamp if available - if (inFirstTimestamp != null) { - cal = inFirstTimestamp.getCalendar(); - } - else { - // No photo or no timestamp, just use current time - cal = Calendar.getInstance(); - } - // Both time zone offset and dst offset are based on milliseconds, so convert to seconds - TimeDifference timeDiff = new TimeDifference((cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / 1000); - return timeDiff; - } - - /** * Calculate the median index to select from the table * @param inModel table model @@ -478,7 +464,9 @@ public abstract class Correlator extends GenericFunction */ private boolean isCardEnabled(int inCardNum) { - if (_cardEnabled == null) {_cardEnabled = getCardEnabledFlags();} + if (_cardEnabled == null) { + _cardEnabled = getCardEnabledFlags(); + } return (inCardNum >= 0 && inCardNum < _cardEnabled.length && _cardEnabled[inCardNum]); } @@ -595,7 +583,9 @@ public abstract class Correlator extends GenericFunction public void createPreview(boolean inFromButton) { // Exit if still on first panel - if (!_previewEnabled) {return;} + if (!_previewEnabled) { + return; + } // Create a TimeDifference based on the edit boxes int numHours = getValue(_offsetHourBox.getText()); int numMins = getValue(_offsetMinBox.getText()); @@ -615,12 +605,8 @@ public abstract class Correlator extends GenericFunction TimeDifference timeDiff = inTimeDiff; if (timeDiff == null) { - // No time difference available, so calculate based on computer's time zone - Timestamp tstamp = null; - if (inFirstMedia != null) { - tstamp = inFirstMedia.getTimestamp(); - } - timeDiff = getTimezoneOffset(tstamp); + // No time difference available, so try with zero + timeDiff = new TimeDifference(0L); } // Use time difference to set edit boxes _offsetHourBox.setText("" + timeDiff.getNumHours()); @@ -664,7 +650,7 @@ public abstract class Correlator extends GenericFunction if (inMedia.hasTimestamp()) { // Add/subtract offset to media timestamp - Timestamp mediaStamp = getMediaTimestamp(inMedia).createMinusOffset(inOffset); + Timestamp mediaStamp = getMediaTimestamp(inMedia); int numPoints = inTrack.getNumPoints(); for (int i=0; i _list = new ArrayList(); /** Distance units */ private Unit _distanceUnits = UnitSetLibrary.UNITS_KILOMETRES; + /** Current timezone */ + private TimeZone _timezone = null; /** * Constructor * @param inFirstColumnKey key for first column heading */ - public MediaPreviewTableModel(String inFirstColumnKey) { + public MediaPreviewTableModel(String inFirstColumnKey) + { _firstColumnHeading = I18nManager.getText(inFirstColumnKey); + _timezone = TimezoneHelper.getSelectedTimezone(); } /** @@ -83,7 +90,7 @@ public class MediaPreviewTableModel extends AbstractTableModel if (inColumnIndex == 0) return row.getMedia().getName(); else if (inColumnIndex == 1) { if (row.getMedia().hasTimestamp()) { - return row.getMedia().getTimestamp().getText(); + return row.getMedia().getTimestamp().getText(_timezone); } return ""; // media doesn't have a timestamp } diff --git a/tim/prune/correlate/MediaSelectionTableModel.java b/tim/prune/correlate/MediaSelectionTableModel.java index 93f8759..1b90e86 100644 --- a/tim/prune/correlate/MediaSelectionTableModel.java +++ b/tim/prune/correlate/MediaSelectionTableModel.java @@ -1,8 +1,11 @@ package tim.prune.correlate; import java.util.ArrayList; +import java.util.TimeZone; + import javax.swing.table.AbstractTableModel; import tim.prune.I18nManager; +import tim.prune.config.TimezoneHelper; import tim.prune.data.MediaObject; @@ -18,6 +21,8 @@ public class MediaSelectionTableModel extends AbstractTableModel private String _lastColumnHeading = null; /** List of rows */ private ArrayList _list = new ArrayList(); + /** Current timezone */ + private TimeZone _timezone = null; /** @@ -29,6 +34,7 @@ public class MediaSelectionTableModel extends AbstractTableModel { _firstColumnHeading = I18nManager.getText(inFirstColumnKey); _lastColumnHeading = I18nManager.getText(inLastColumnKey); + _timezone = TimezoneHelper.getSelectedTimezone(); } /** @@ -84,7 +90,8 @@ public class MediaSelectionTableModel extends AbstractTableModel MediaSelectionTableRow row = _list.get(inRowIndex); if (inColumnIndex == 0) return row.getMedia().getName(); else if (inColumnIndex == 1) { - return (row.getMedia().hasTimestamp() ? row.getMedia().getTimestamp().getText() : ""); + return (row.getMedia().hasTimestamp() ? + row.getMedia().getTimestamp().getText(_timezone) : ""); } else if (inColumnIndex == 2) return row.getTimeDiff().getDescription(); return (row.getTimeDiff().getIsPositive() ? I18nManager.getText("dialog.about.yes") : diff --git a/tim/prune/data/AltitudeRange.java b/tim/prune/data/AltitudeRange.java index 1703631..6a9f65f 100644 --- a/tim/prune/data/AltitudeRange.java +++ b/tim/prune/data/AltitudeRange.java @@ -170,27 +170,25 @@ public class AltitudeRange */ public boolean hasRange() { - return _range.getMaximum() > _range.getMinimum(); + return _range.hasValues(); } /** * @param inUnit altitude units to use - * @return minimum value, or -1 if none found + * @return minimum value */ public int getMinimum(Unit inUnit) { - if (_range.getMinimum() <= 0) return _range.getMinimum(); return (int) (_range.getMinimum() * inUnit.getMultFactorFromStd()); } /** * @param inUnit altitude units to use - * @return maximum value, or -1 if none found + * @return maximum value */ public int getMaximum(Unit inUnit) { - if (_range.getMaximum() <= 0) return _range.getMaximum(); return (int) (_range.getMaximum() * inUnit.getMultFactorFromStd()); } diff --git a/tim/prune/data/AudioClip.java b/tim/prune/data/AudioClip.java index b6dbd87..2e5f5ee 100644 --- a/tim/prune/data/AudioClip.java +++ b/tim/prune/data/AudioClip.java @@ -23,7 +23,7 @@ public class AudioClip extends MediaObject public AudioClip(File inFile) { // Timestamp is always just taken from the file modification stamp - super(inFile, new Timestamp(inFile.lastModified())); + super(inFile, new TimestampUtc(inFile.lastModified())); } /** diff --git a/tim/prune/data/DataPoint.java b/tim/prune/data/DataPoint.java index c244622..84f4800 100644 --- a/tim/prune/data/DataPoint.java +++ b/tim/prune/data/DataPoint.java @@ -81,7 +81,7 @@ public class DataPoint } } if (inField == null || inField == Field.TIMESTAMP) { - _timestamp = new Timestamp(getFieldValue(Field.TIMESTAMP)); + _timestamp = new TimestampUtc(getFieldValue(Field.TIMESTAMP)); } if (inField == null || inField == Field.WAYPT_NAME) { _waypointName = getFieldValue(Field.WAYPT_NAME); @@ -118,7 +118,7 @@ public class DataPoint _altitude = inAltitude; _fieldValues[2] = "" + inAltitude.getValue(); } - _timestamp = new Timestamp(null); + _timestamp = new TimestampUtc(null); } @@ -369,12 +369,12 @@ public class DataPoint * Add a time offset to this point * @param inOffset offset to add (-ve to subtract) */ - public void addTimeOffset(long inOffset) + public void addTimeOffsetSeconds(long inOffset) { if (hasTimestamp()) { - _timestamp.addOffset(inOffset); - _fieldValues[_fieldList.getFieldIndex(Field.TIMESTAMP)] = _timestamp.getText(); + _timestamp.addOffsetSeconds(inOffset); + _fieldValues[_fieldList.getFieldIndex(Field.TIMESTAMP)] = _timestamp.getText(null); setModified(false); } } diff --git a/tim/prune/data/GradientCalculator.java b/tim/prune/data/GradientCalculator.java index 97a691f..0f608bd 100644 --- a/tim/prune/data/GradientCalculator.java +++ b/tim/prune/data/GradientCalculator.java @@ -40,7 +40,7 @@ public abstract class GradientCalculator && p.hasAltitude() && q.hasAltitude()) { final double horizRads = DataPoint.calculateRadiansBetween(p, point) + - DataPoint.calculateRadiansBetween(point, q); + DataPoint.calculateRadiansBetween(point, q); final double horizDist = Distance.convertRadiansToDistance(horizRads, UnitSetLibrary.UNITS_METRES); final double heightDiff = q.getAltitude().getMetricValue() - p.getAltitude().getMetricValue(); // Get gradient in radians diff --git a/tim/prune/data/IntegerRange.java b/tim/prune/data/IntegerRange.java index a16f44b..d2424d1 100644 --- a/tim/prune/data/IntegerRange.java +++ b/tim/prune/data/IntegerRange.java @@ -2,11 +2,12 @@ package tim.prune.data; /** * Represents a range of integers, holding the maximum and - * minimum values. Values assumed to be >= 0. + * minimum values. */ public class IntegerRange { private int _min = -1, _max = -1; + private boolean _foundValues = false; /** @@ -16,22 +17,31 @@ public class IntegerRange { _min = -1; _max = -1; + _foundValues = false; } /** * Add a value to the range - * @param inValue value to add, only positive values considered + * @param inValue value to add */ public void addValue(int inValue) { - if (inValue >= 0) - { - if (inValue < _min || _min < 0) _min = inValue; - if (inValue > _max) _max = inValue; + if (inValue < _min || !_foundValues) { + _min = inValue; } + if (inValue > _max || !_foundValues) { + _max = inValue; + } + _foundValues = true; } + /** + * @return true if any values added to the range + */ + public boolean hasValues() { + return _foundValues; + } /** * @return minimum value, or -1 if none found diff --git a/tim/prune/data/SpeedCalculator.java b/tim/prune/data/SpeedCalculator.java index 49fd056..96677ca 100644 --- a/tim/prune/data/SpeedCalculator.java +++ b/tim/prune/data/SpeedCalculator.java @@ -79,7 +79,7 @@ public abstract class SpeedCalculator lateStamp = p.getTimestamp(); } - stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p); + stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p); index++; if (p != null && !p.isWaypoint()) { q = p; @@ -153,7 +153,7 @@ public abstract class SpeedCalculator if (p.hasAltitude()) firstAlt = p.getAltitude(); } - stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(p, point); + stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(p, point); index--; } while (!stop); @@ -173,7 +173,7 @@ public abstract class SpeedCalculator if (p.hasAltitude()) lastAlt = p.getAltitude(); } - stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p); + stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p); index++; } while (!stop); diff --git a/tim/prune/data/Timestamp.java b/tim/prune/data/Timestamp.java index d5e065d..ac144bb 100644 --- a/tim/prune/data/Timestamp.java +++ b/tim/prune/data/Timestamp.java @@ -1,44 +1,29 @@ package tim.prune.data; + import java.text.DateFormat; -import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Calendar; -import java.util.Date; import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; + /** - * Class to hold the timestamp of a track point - * and provide conversion functions + * Superclass of all timestamp implementations */ -public class Timestamp +public abstract class Timestamp { - private boolean _valid = false; - private long _milliseconds = 0L; - private String _text = null; - - private static final DateFormat DEFAULT_DATETIME_FORMAT = DateFormat.getDateTimeInstance(); private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance(); private static final DateFormat DEFAULT_TIME_FORMAT = DateFormat.getTimeInstance(); + + protected static final DateFormat DEFAULT_DATETIME_FORMAT = DateFormat.getDateTimeInstance(); + + protected static final DateFormat ISO_8601_FORMAT + = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + protected static final DateFormat ISO_8601_FORMAT_WITH_MILLIS + = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + private static boolean MillisAddedToTimeFormat = false; - private static final DateFormat ISO_8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - private static final DateFormat ISO_8601_FORMAT_WITH_MILLIS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - private static final DateFormat ISO_8601_FORMAT_NOZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - private static DateFormat[] ALL_DATE_FORMATS = null; - private static Calendar CALENDAR = null; - private static final Pattern ISO8601_FRACTIONAL_PATTERN - = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:[\\.,](\\d{1,3}))?(Z|[\\+-]\\d{2}(?::?\\d{2})?)?"); - // year month day T hour minute sec millisec Z or +/- hours : minutes - private static final Pattern GENERAL_TIMESTAMP_PATTERN - = Pattern.compile("(\\d{4})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})"); - private static long SECS_SINCE_1970 = 0L; - private static long SECS_SINCE_GARTRIP = 0L; - private static long MSECS_SINCE_1970 = 0L; - private static long MSECS_SINCE_1990 = 0L; - private static long TWENTY_YEARS_IN_SECS = 0L; - private static final long GARTRIP_OFFSET = 631065600L; + /** Possible formats for parsing and displaying timestamps */ public enum Format @@ -48,451 +33,110 @@ public class Timestamp ISO8601 } - /** Identifier for the parsing strategy to use */ - private enum ParseType - { - NONE, - ISO8601_FRACTIONAL, - LONG, - FIXED_FORMAT0, - FIXED_FORMAT1, - FIXED_FORMAT2, - FIXED_FORMAT3, - FIXED_FORMAT4, - FIXED_FORMAT5, - FIXED_FORMAT6, - FIXED_FORMAT7, - FIXED_FORMAT8, - GENERAL_STRING - } - - /** Array of parse types to loop through (first one is changed to last successful type) */ - private static ParseType[] ALL_PARSE_TYPES = {ParseType.NONE, ParseType.ISO8601_FRACTIONAL, ParseType.LONG, - ParseType.FIXED_FORMAT0, ParseType.FIXED_FORMAT1, ParseType.FIXED_FORMAT2, ParseType.FIXED_FORMAT3, - ParseType.FIXED_FORMAT4, ParseType.FIXED_FORMAT5, ParseType.FIXED_FORMAT6, ParseType.FIXED_FORMAT7, - ParseType.FIXED_FORMAT8, ParseType.GENERAL_STRING}; - // Static block to initialise offsets + // Static block to initialise date formats static { - CALENDAR = Calendar.getInstance(); - TimeZone gmtZone = TimeZone.getTimeZone("GMT"); - CALENDAR.setTimeZone(gmtZone); - MSECS_SINCE_1970 = CALENDAR.getTimeInMillis(); - SECS_SINCE_1970 = MSECS_SINCE_1970 / 1000L; - SECS_SINCE_GARTRIP = SECS_SINCE_1970 - GARTRIP_OFFSET; - CALENDAR.add(Calendar.YEAR, -20); - MSECS_SINCE_1990 = CALENDAR.getTimeInMillis(); - TWENTY_YEARS_IN_SECS = (MSECS_SINCE_1970 - MSECS_SINCE_1990) / 1000L; // Set timezone for output + TimeZone gmtZone = TimeZone.getTimeZone("GMT"); ISO_8601_FORMAT.setTimeZone(gmtZone); ISO_8601_FORMAT_WITH_MILLIS.setTimeZone(gmtZone); DEFAULT_DATETIME_FORMAT.setTimeZone(gmtZone); - // Date formats - ALL_DATE_FORMATS = new DateFormat[] { - DEFAULT_DATETIME_FORMAT, - new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy"), - new SimpleDateFormat("HH:mm:ss dd MMM yyyy"), - new SimpleDateFormat("dd MMM yyyy HH:mm:ss"), - new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss"), - new SimpleDateFormat("yyyy MMM dd HH:mm:ss"), - new SimpleDateFormat("MMM dd, yyyy hh:mm:ss aa"), - ISO_8601_FORMAT, ISO_8601_FORMAT_NOZ - }; - for (DateFormat df : ALL_DATE_FORMATS) { - df.setLenient(false); - } - } - - - /** - * Constructor - * @param inString String containing timestamp - */ - public Timestamp(String inString) - { - _valid = false; - _text = null; - if (inString != null && !inString.equals("")) - { - // Try each of the parse types in turn - for (ParseType type : ALL_PARSE_TYPES) - { - if (parseString(inString, type)) - { - ALL_PARSE_TYPES[0] = type; - _valid = true; - _text = inString; - return; - } - } - } - } - - /** - * Try to parse the given string in the specified way - * @param inString String to parse - * @param inType parse type to use - * @return true if successful - */ - private boolean parseString(String inString, ParseType inType) - { - if (inString == null || inString.equals("")) { - return false; - } - switch (inType) - { - case NONE: return false; - case LONG: - // Try to parse into a long - try - { - long rawValue = Long.parseLong(inString.trim()); - _milliseconds = getMilliseconds(rawValue); - return true; - } - catch (NumberFormatException nfe) - {} - break; - - case ISO8601_FRACTIONAL: - final Matcher fmatcher = ISO8601_FRACTIONAL_PATTERN.matcher(inString); - if (fmatcher.matches()) - { - try { - _milliseconds = getMilliseconds(Integer.parseInt(fmatcher.group(1)), // year - Integer.parseInt(fmatcher.group(2)), // month - Integer.parseInt(fmatcher.group(3)), // day - Integer.parseInt(fmatcher.group(4)), // hour - Integer.parseInt(fmatcher.group(5)), // minute - Integer.parseInt(fmatcher.group(6)), // second - fmatcher.group(7), // fractional seconds - fmatcher.group(8)); // timezone, if any - return true; - } - catch (NumberFormatException nfe) {} - } - break; - - case FIXED_FORMAT0: return parseString(inString, ALL_DATE_FORMATS[0]); - case FIXED_FORMAT1: return parseString(inString, ALL_DATE_FORMATS[1]); - case FIXED_FORMAT2: return parseString(inString, ALL_DATE_FORMATS[2]); - case FIXED_FORMAT3: return parseString(inString, ALL_DATE_FORMATS[3]); - case FIXED_FORMAT4: return parseString(inString, ALL_DATE_FORMATS[4]); - case FIXED_FORMAT5: return parseString(inString, ALL_DATE_FORMATS[5]); - case FIXED_FORMAT6: return parseString(inString, ALL_DATE_FORMATS[6]); - case FIXED_FORMAT7: return parseString(inString, ALL_DATE_FORMATS[7]); - case FIXED_FORMAT8: return parseString(inString, ALL_DATE_FORMATS[8]); - - case GENERAL_STRING: - if (inString.length() == 19) - { - final Matcher matcher = GENERAL_TIMESTAMP_PATTERN.matcher(inString); - if (matcher.matches()) - { - try { - _milliseconds = getMilliseconds(Integer.parseInt(matcher.group(1)), - Integer.parseInt(matcher.group(2)), - Integer.parseInt(matcher.group(3)), - Integer.parseInt(matcher.group(4)), - Integer.parseInt(matcher.group(5)), - Integer.parseInt(matcher.group(6)), - null, null); // no fractions of a second and no timezone - return true; - } - catch (NumberFormatException nfe2) {} // parse shouldn't fail if matcher matched - } - } - return false; - } - return false; } /** - * Try to parse the given string with the given date format - * @param inString String to parse - * @param inDateFormat Date format to use - * @return true if successful + * @return true if valid */ - private boolean parseString(String inString, DateFormat inDateFormat) - { - ParsePosition pPos = new ParsePosition(0); - Date date = inDateFormat.parse(inString, pPos); - if (date != null && inString.length() == pPos.getIndex()) // require use of _all_ the string, not just the beginning - { - CALENDAR.setTime(date); - _milliseconds = CALENDAR.getTimeInMillis(); - return true; - } - - return false; - } - + public abstract boolean isValid(); /** - * Constructor giving each field value individually - * @param inYear year - * @param inMonth month, beginning with 1 - * @param inDay day of month, beginning with 1 - * @param inHour hour of day, 0-24 - * @param inMinute minute - * @param inSecond seconds + * Get a calendar representing this timestamp */ - public Timestamp(int inYear, int inMonth, int inDay, int inHour, int inMinute, int inSecond) - { - _milliseconds = getMilliseconds(inYear, inMonth, inDay, inHour, inMinute, inSecond, null, null); - _valid = true; - } - + public abstract Calendar getCalendar(TimeZone inZone); /** - * Constructor giving millis - * @param inMillis milliseconds since 1970 + * @return the milliseconds according to the given timezone */ - public Timestamp(long inMillis) - { - _milliseconds = inMillis; - _valid = true; - } - + public abstract long getMilliseconds(TimeZone inZone); /** - * Convert the given timestamp parameters into a number of milliseconds - * @param inYear year - * @param inMonth month, beginning with 1 - * @param inDay day of month, beginning with 1 - * @param inHour hour of day, 0-24 - * @param inMinute minute - * @param inSecond seconds - * @param inFraction fractions of a second - * @param inTimezone timezone, if any - * @return number of milliseconds + * @return true if this timestamp is after the other one */ - private static long getMilliseconds(int inYear, int inMonth, int inDay, - int inHour, int inMinute, int inSecond, String inFraction, String inTimezone) + public boolean isAfter(Timestamp inOther) { - Calendar cal = Calendar.getInstance(); - // Timezone, if any - if (inTimezone == null || inTimezone.equals("") || inTimezone.equals("Z")) { - // No timezone, use zulu - cal.setTimeZone(TimeZone.getTimeZone("GMT")); - } - else { - // Timezone specified, pass to calendar - cal.setTimeZone(TimeZone.getTimeZone("GMT" + inTimezone)); - } - cal.set(Calendar.YEAR, inYear); - cal.set(Calendar.MONTH, inMonth - 1); - cal.set(Calendar.DAY_OF_MONTH, inDay); - cal.set(Calendar.HOUR_OF_DAY, inHour); - cal.set(Calendar.MINUTE, inMinute); - cal.set(Calendar.SECOND, inSecond); - int millis = 0; - if (inFraction != null) - { - try { - int frac = Integer.parseInt(inFraction); - final int fracLen = inFraction.length(); - switch (fracLen) { - case 1: millis = frac * 100; break; - case 2: millis = frac * 10; break; - case 3: millis = frac; break; - } - } - catch (NumberFormatException nfe) {} // ignore errors, millis stay at 0 - } - cal.set(Calendar.MILLISECOND, millis); - return cal.getTimeInMillis(); + return getMillisecondsSince(inOther) > 0; } /** - * Convert the given long parameters into a number of millisseconds - * @param inRawValue long value representing seconds / milliseconds - * @return number of milliseconds + * @return true if this timestamp is before the other one */ - private static long getMilliseconds(long inRawValue) - { - // check for each format possibility and pick nearest - long diff1 = Math.abs(SECS_SINCE_1970 - inRawValue); - long diff2 = Math.abs(MSECS_SINCE_1970 - inRawValue); - long diff3 = Math.abs(MSECS_SINCE_1990 - inRawValue); - long diff4 = Math.abs(SECS_SINCE_GARTRIP - inRawValue); - - // Start off with "seconds since 1970" format - long smallestDiff = diff1; - long millis = inRawValue * 1000; - // Now check millis since 1970 - if (diff2 < smallestDiff) - { - // milliseconds since 1970 - millis = inRawValue; - smallestDiff = diff2; - } - // Now millis since 1990 - if (diff3 < smallestDiff) - { - // milliseconds since 1990 - millis = inRawValue + TWENTY_YEARS_IN_SECS * 1000L; - smallestDiff = diff3; - } - // Lastly, check gartrip offset - if (diff4 < smallestDiff) - { - // seconds since gartrip offset - millis = (inRawValue + GARTRIP_OFFSET) * 1000L; - } - return millis; - } - - /** - * @return true if timestamp is valid - */ - public boolean isValid() + public boolean isBefore(Timestamp inOther) { - return _valid; + return getMillisecondsSince(inOther) < 0; } /** - * @return true if the timestamp has non-zero milliseconds - */ - public boolean hasMilliseconds() - { - return isValid() && (_milliseconds % 1000L) > 0; - } - /** - * @param inOther other Timestamp - * @return true if this one is at least a millisecond after the other + * @return true if this timestamp is equal to the other one */ - public boolean isAfter(Timestamp inOther) + public boolean isEqual(Timestamp inOther) { - return getMillisecondsSince(inOther) > 0L; + return getMillisecondsSince(inOther) == 0; } /** - * Calculate the difference between two Timestamps in seconds - * @param inOther other, earlier Timestamp - * @return number of seconds since other timestamp + * @return the number of seconds since the other timestamp */ public long getSecondsSince(Timestamp inOther) { - return (_milliseconds - inOther._milliseconds) / 1000L; + return getMillisecondsSince(inOther) / 1000L; } /** * Calculate the difference between two Timestamps in milliseconds * @param inOther other, earlier Timestamp - * @return number of millisseconds since other timestamp + * @return number of milliseconds since other timestamp */ public long getMillisecondsSince(Timestamp inOther) { - return _milliseconds - inOther._milliseconds; + return getMilliseconds(null) - inOther.getMilliseconds(null); } /** - * @param inOther other timestamp to compare - * @return true if they're equal to the nearest millisecond + * @return the number of seconds since the other timestamp using the given timezone */ - public boolean isEqual(Timestamp inOther) + public long getSecondsSince(Timestamp inOther, TimeZone inTimezone) { - return inOther != null && _milliseconds == inOther._milliseconds; - } - - /** - * @param inOther other Timestamp - * @return true if this one is before the other - */ - public boolean isBefore(Timestamp inOther) - { - return getMillisecondsSince(inOther) < 0L; + return (getMilliseconds(inTimezone) - inOther.getMilliseconds(inTimezone)) / 1000L; } /** * Add the given number of seconds offset * @param inOffset number of seconds to add/subtract */ - public void addOffset(long inOffset) - { - _milliseconds += (inOffset * 1000L); - _text = null; - } + public abstract void addOffsetSeconds(long inOffset); /** - * Add the given TimeDifference to this Timestamp - * @param inOffset TimeDifference to add - * @return new Timestamp object - */ - public Timestamp createPlusOffset(TimeDifference inOffset) - { - return createPlusOffset(inOffset.getTotalSeconds()); - } - - /** - * Add the given number of seconds to this Timestamp - * @param inSeconds number of seconds to add - * @return new Timestamp object - */ - public Timestamp createPlusOffset(long inSeconds) - { - return new Timestamp(_milliseconds + (inSeconds * 1000L)); - } - - - /** - * Subtract the given TimeDifference from this Timestamp - * @param inOffset TimeDifference to subtract - * @return new Timestamp object - */ - public Timestamp createMinusOffset(TimeDifference inOffset) - { - return new Timestamp(_milliseconds - (inOffset.getTotalSeconds() * 1000L)); - } - - - /** - * @return Description of timestamp in locale-specific format + * @return true if the timestamp has non-zero milliseconds */ - public String getText() - { - return getText(Format.LOCALE); - } + protected abstract boolean hasMilliseconds(); - /** - * @param inFormat format of timestamp - * @return Description of timestamp in required format - */ - public String getText(Format inFormat) - { - if (!_valid) {return "";} - switch (inFormat) - { - case ORIGINAL: - if (_text != null) {return _text;} - // otherwise fallthrough to default - //$FALL-THROUGH$ - case LOCALE: - return format(DEFAULT_DATETIME_FORMAT); - case ISO8601: - return format(hasMilliseconds() ? ISO_8601_FORMAT_WITH_MILLIS : ISO_8601_FORMAT); - } - return _text; - } /** * @return date part of timestamp in locale-specific format */ - public String getDateText() + public String getDateText(TimeZone inTimezone) { - if (!_valid) return ""; - return format(DEFAULT_DATE_FORMAT); + if (!isValid()) return ""; + return format(DEFAULT_DATE_FORMAT, inTimezone); } /** * @return Description of time part of timestamp in locale-specific format */ - public String getTimeText() + public String getTimeText(TimeZone inTimezone) { - if (!_valid) return ""; + if (!isValid()) return ""; // Maybe we should add milliseconds to this format? if (hasMilliseconds() && !MillisAddedToTimeFormat) { @@ -508,29 +152,42 @@ public class Timestamp } catch (ClassCastException cce) {} } - return format(DEFAULT_TIME_FORMAT); + return format(DEFAULT_TIME_FORMAT, inTimezone); } /** * Utility method for formatting dates / times - * @param inFormat formatter object - * @return formatted String */ - private String format(DateFormat inFormat) + protected abstract String format(DateFormat inFormat, TimeZone inTimezone); + + + /** + * @return Description of timestamp in locale-specific format + */ + public String getText(TimeZone inTimezone) { - CALENDAR.setTimeZone(TimeZone.getTimeZone("GMT")); - CALENDAR.setTimeInMillis(_milliseconds); - return inFormat.format(CALENDAR.getTime()); + return getText(Format.LOCALE, inTimezone); } /** - * @return a Calendar object representing this timestamp + * @param inFormat format of timestamp + * @return Description of timestamp in required format */ - public Calendar getCalendar() + public String getText(Format inFormat, TimeZone inTimezone) { - Calendar cal = Calendar.getInstance(); - cal.setTimeZone(TimeZone.getTimeZone("GMT")); - cal.setTimeInMillis(_milliseconds); - return cal; + if (!isValid()) { + return ""; + } + switch (inFormat) + { + case ORIGINAL: + case LOCALE: + default: + return format(DEFAULT_DATETIME_FORMAT, inTimezone); + case ISO8601: + return format(hasMilliseconds() ? ISO_8601_FORMAT_WITH_MILLIS : ISO_8601_FORMAT, + inTimezone); + } } + } diff --git a/tim/prune/data/TimestampLocal.java b/tim/prune/data/TimestampLocal.java new file mode 100644 index 0000000..d4c9093 --- /dev/null +++ b/tim/prune/data/TimestampLocal.java @@ -0,0 +1,104 @@ +package tim.prune.data; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.TimeZone; + + +/** + * Class to hold a timestamp based on a local timezone, for example + * from a camera or audio recorder. + * When the selected timezone changes, this timestamp will keep its + * date and time but the numerical value will change accordingly. + */ +public class TimestampLocal extends Timestamp +{ + private boolean _valid = false; + private int _year=0, _month=0, _day=0; + private int _hour=0, _minute=0, _second=0; + + + /** + * Constructor giving each field value individually + * @param inYear year + * @param inMonth month, beginning with 1 + * @param inDay day of month, beginning with 1 + * @param inHour hour of day, 0-24 + * @param inMinute minute + * @param inSecond seconds + */ + public TimestampLocal(int inYear, int inMonth, int inDay, int inHour, int inMinute, int inSecond) + { + _valid = inYear > 0 && inYear < 3000 + && inMonth > 0 && inMonth < 13 + && inDay > 0 && inDay < 32 + && inHour >= 0 && inHour < 24 + && inMinute >= 0 && inMinute < 60 + && inSecond >= 0 && inSecond < 60; + if (_valid) + { + _year = inYear; + _month = inMonth; + _day = inDay; + _hour = inHour; + _minute = inMinute; + _second = inSecond; + } + } + + + /** @return true if valid */ + public boolean isValid() + { + return _valid; + } + + @Override + public Calendar getCalendar(TimeZone inZone) + { + Calendar cal = Calendar.getInstance(); + if (inZone != null) { + cal.setTimeZone(inZone); + } + cal.set(Calendar.YEAR, _year); + cal.set(Calendar.MONTH, _month - 1); + cal.set(Calendar.DAY_OF_MONTH, _day); + cal.set(Calendar.HOUR_OF_DAY, _hour); + cal.set(Calendar.MINUTE, _minute); + cal.set(Calendar.SECOND, _second); + cal.set(Calendar.MILLISECOND, 0); + return cal; + } + + @Override + public long getMilliseconds(TimeZone inZone) + { + return getCalendar(inZone).getTimeInMillis(); + } + + @Override + public void addOffsetSeconds(long inOffset) + { + System.err.println("Local timestamps don't support offsets."); + } + + @Override + protected boolean hasMilliseconds() + { + return false; + } + + /** + * Utility method for formatting dates / times + * @param inFormat formatter object + * @param inTimezone timezone to use + * @return formatted String + */ + @Override + protected String format(DateFormat inFormat, TimeZone inTimezone) + { + Calendar cal = getCalendar(inTimezone); + inFormat.setTimeZone(inTimezone); + return inFormat.format(cal.getTime()); + } +} diff --git a/tim/prune/data/TimestampUtc.java b/tim/prune/data/TimestampUtc.java new file mode 100644 index 0000000..938123b --- /dev/null +++ b/tim/prune/data/TimestampUtc.java @@ -0,0 +1,399 @@ +package tim.prune.data; + +import java.text.DateFormat; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * Class to hold a UTC-based timestamp, for example of a track point. + * When the selected timezone changes, this timestamp will keep its + * numerical value but the date and time will change accordingly. + */ +public class TimestampUtc extends Timestamp +{ + private boolean _valid = false; + private long _milliseconds = 0L; + private String _text = null; + + private static final DateFormat ISO_8601_FORMAT_NOZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + private static DateFormat[] ALL_DATE_FORMATS = null; + private static Calendar CALENDAR = null; + private static final Pattern ISO8601_FRACTIONAL_PATTERN + = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:[\\.,](\\d{1,3}))?(Z|[\\+-]\\d{2}(?::?\\d{2})?)?"); + // year month day T hour minute sec millisec Z or +/- hours : minutes + private static final Pattern GENERAL_TIMESTAMP_PATTERN + = Pattern.compile("(\\d{4})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})"); + private static long SECS_SINCE_1970 = 0L; + private static long SECS_SINCE_GARTRIP = 0L; + private static long MSECS_SINCE_1970 = 0L; + private static long MSECS_SINCE_1990 = 0L; + private static long TWENTY_YEARS_IN_SECS = 0L; + private static final long GARTRIP_OFFSET = 631065600L; + + /** Identifier for the parsing strategy to use */ + private enum ParseType + { + NONE, + ISO8601_FRACTIONAL, + LONG, + FIXED_FORMAT0, + FIXED_FORMAT1, + FIXED_FORMAT2, + FIXED_FORMAT3, + FIXED_FORMAT4, + FIXED_FORMAT5, + FIXED_FORMAT6, + FIXED_FORMAT7, + FIXED_FORMAT8, + GENERAL_STRING + } + + /** Array of parse types to loop through (first one is changed to last successful type) */ + private static ParseType[] ALL_PARSE_TYPES = {ParseType.NONE, ParseType.ISO8601_FRACTIONAL, ParseType.LONG, + ParseType.FIXED_FORMAT0, ParseType.FIXED_FORMAT1, ParseType.FIXED_FORMAT2, ParseType.FIXED_FORMAT3, + ParseType.FIXED_FORMAT4, ParseType.FIXED_FORMAT5, ParseType.FIXED_FORMAT6, ParseType.FIXED_FORMAT7, + ParseType.FIXED_FORMAT8, ParseType.GENERAL_STRING}; + + // Static block to initialise offsets + static + { + CALENDAR = Calendar.getInstance(); + TimeZone gmtZone = TimeZone.getTimeZone("GMT"); + CALENDAR.setTimeZone(gmtZone); + MSECS_SINCE_1970 = CALENDAR.getTimeInMillis(); + SECS_SINCE_1970 = MSECS_SINCE_1970 / 1000L; + SECS_SINCE_GARTRIP = SECS_SINCE_1970 - GARTRIP_OFFSET; + CALENDAR.add(Calendar.YEAR, -20); + MSECS_SINCE_1990 = CALENDAR.getTimeInMillis(); + TWENTY_YEARS_IN_SECS = (MSECS_SINCE_1970 - MSECS_SINCE_1990) / 1000L; + // Date formats + ALL_DATE_FORMATS = new DateFormat[] + { + DEFAULT_DATETIME_FORMAT, + new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy"), + new SimpleDateFormat("HH:mm:ss dd MMM yyyy"), + new SimpleDateFormat("dd MMM yyyy HH:mm:ss"), + new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss"), + new SimpleDateFormat("yyyy MMM dd HH:mm:ss"), + new SimpleDateFormat("MMM dd, yyyy hh:mm:ss aa"), + ISO_8601_FORMAT, ISO_8601_FORMAT_NOZ + }; + for (DateFormat df : ALL_DATE_FORMATS) + { + df.setLenient(false); + df.setTimeZone(gmtZone); + } + } + + + /** + * Constructor + * @param inString String containing timestamp + */ + public TimestampUtc(String inString) + { + _valid = false; + _text = null; + if (inString != null && !inString.equals("")) + { + // Try each of the parse types in turn + for (ParseType type : ALL_PARSE_TYPES) + { + if (parseString(inString, type)) + { + ALL_PARSE_TYPES[0] = type; + _valid = true; + _text = inString; + return; + } + } + } + } + + /** + * Try to parse the given string in the specified way + * @param inString String to parse + * @param inType parse type to use + * @return true if successful + */ + private boolean parseString(String inString, ParseType inType) + { + if (inString == null || inString.equals("")) { + return false; + } + switch (inType) + { + case NONE: return false; + case LONG: + // Try to parse into a long + try + { + long rawValue = Long.parseLong(inString.trim()); + _milliseconds = getMilliseconds(rawValue); + return true; + } + catch (NumberFormatException nfe) + {} + break; + + case ISO8601_FRACTIONAL: + final Matcher fmatcher = ISO8601_FRACTIONAL_PATTERN.matcher(inString); + if (fmatcher.matches()) + { + try { + _milliseconds = getMilliseconds(Integer.parseInt(fmatcher.group(1)), // year + Integer.parseInt(fmatcher.group(2)), // month + Integer.parseInt(fmatcher.group(3)), // day + Integer.parseInt(fmatcher.group(4)), // hour + Integer.parseInt(fmatcher.group(5)), // minute + Integer.parseInt(fmatcher.group(6)), // second + fmatcher.group(7), // fractional seconds + fmatcher.group(8)); // timezone, if any + return true; + } + catch (NumberFormatException nfe) {} + } + break; + + case FIXED_FORMAT0: return parseString(inString, ALL_DATE_FORMATS[0]); + case FIXED_FORMAT1: return parseString(inString, ALL_DATE_FORMATS[1]); + case FIXED_FORMAT2: return parseString(inString, ALL_DATE_FORMATS[2]); + case FIXED_FORMAT3: return parseString(inString, ALL_DATE_FORMATS[3]); + case FIXED_FORMAT4: return parseString(inString, ALL_DATE_FORMATS[4]); + case FIXED_FORMAT5: return parseString(inString, ALL_DATE_FORMATS[5]); + case FIXED_FORMAT6: return parseString(inString, ALL_DATE_FORMATS[6]); + case FIXED_FORMAT7: return parseString(inString, ALL_DATE_FORMATS[7]); + case FIXED_FORMAT8: return parseString(inString, ALL_DATE_FORMATS[8]); + + case GENERAL_STRING: + if (inString.length() == 19) + { + final Matcher matcher = GENERAL_TIMESTAMP_PATTERN.matcher(inString); + if (matcher.matches()) + { + try { + _milliseconds = getMilliseconds(Integer.parseInt(matcher.group(1)), + Integer.parseInt(matcher.group(2)), + Integer.parseInt(matcher.group(3)), + Integer.parseInt(matcher.group(4)), + Integer.parseInt(matcher.group(5)), + Integer.parseInt(matcher.group(6)), + null, null); // no fractions of a second and no timezone + return true; + } + catch (NumberFormatException nfe2) {} // parse shouldn't fail if matcher matched + } + } + return false; + } + return false; + } + + + /** + * Try to parse the given string with the given date format + * @param inString String to parse + * @param inDateFormat Date format to use + * @return true if successful + */ + private boolean parseString(String inString, DateFormat inDateFormat) + { + ParsePosition pPos = new ParsePosition(0); + Date date = inDateFormat.parse(inString, pPos); + if (date != null && inString.length() == pPos.getIndex()) // require use of _all_ the string, not just the beginning + { + CALENDAR.setTime(date); + _milliseconds = CALENDAR.getTimeInMillis(); + return true; + } + + return false; + } + + + /** + * Constructor giving millis + * @param inMillis milliseconds since 1970 + */ + public TimestampUtc(long inMillis) + { + _milliseconds = inMillis; + _valid = true; + } + + + /** + * Convert the given timestamp parameters into a number of milliseconds + * @param inYear year + * @param inMonth month, beginning with 1 + * @param inDay day of month, beginning with 1 + * @param inHour hour of day, 0-24 + * @param inMinute minute + * @param inSecond seconds + * @param inFraction fractions of a second + * @param inTimezone timezone, if any + * @return number of milliseconds + */ + private static long getMilliseconds(int inYear, int inMonth, int inDay, + int inHour, int inMinute, int inSecond, String inFraction, String inTimezone) + { + Calendar cal = Calendar.getInstance(); + // Timezone, if any + if (inTimezone == null || inTimezone.equals("") || inTimezone.equals("Z")) { + // No timezone, use zulu + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + } + else { + // Timezone specified, pass to calendar + cal.setTimeZone(TimeZone.getTimeZone("GMT" + inTimezone)); + } + cal.set(Calendar.YEAR, inYear); + cal.set(Calendar.MONTH, inMonth - 1); + cal.set(Calendar.DAY_OF_MONTH, inDay); + cal.set(Calendar.HOUR_OF_DAY, inHour); + cal.set(Calendar.MINUTE, inMinute); + cal.set(Calendar.SECOND, inSecond); + int millis = 0; + if (inFraction != null) + { + try { + int frac = Integer.parseInt(inFraction); + final int fracLen = inFraction.length(); + switch (fracLen) { + case 1: millis = frac * 100; break; + case 2: millis = frac * 10; break; + case 3: millis = frac; break; + } + } + catch (NumberFormatException nfe) {} // ignore errors, millis stay at 0 + } + cal.set(Calendar.MILLISECOND, millis); + return cal.getTimeInMillis(); + } + + /** + * Convert the given long parameters into a number of milliseconds + * @param inRawValue long value representing seconds / milliseconds + * @return number of milliseconds + */ + private static long getMilliseconds(long inRawValue) + { + // check for each format possibility and pick nearest + long diff1 = Math.abs(SECS_SINCE_1970 - inRawValue); + long diff2 = Math.abs(MSECS_SINCE_1970 - inRawValue); + long diff3 = Math.abs(MSECS_SINCE_1990 - inRawValue); + long diff4 = Math.abs(SECS_SINCE_GARTRIP - inRawValue); + + // Start off with "seconds since 1970" format + long smallestDiff = diff1; + long millis = inRawValue * 1000; + // Now check millis since 1970 + if (diff2 < smallestDiff) + { + // milliseconds since 1970 + millis = inRawValue; + smallestDiff = diff2; + } + // Now millis since 1990 + if (diff3 < smallestDiff) + { + // milliseconds since 1990 + millis = inRawValue + TWENTY_YEARS_IN_SECS * 1000L; + smallestDiff = diff3; + } + // Lastly, check gartrip offset + if (diff4 < smallestDiff) + { + // seconds since gartrip offset + millis = (inRawValue + GARTRIP_OFFSET) * 1000L; + } + return millis; + } + + /** + * @return true if timestamp is valid + */ + public boolean isValid() + { + return _valid; + } + + /** + * @return true if the timestamp has non-zero milliseconds + */ + protected boolean hasMilliseconds() + { + return isValid() && (_milliseconds % 1000L) > 0; + } + + /** + * @return the milliseconds according to the given timezone + */ + public long getMilliseconds(TimeZone inZone) + { + return _milliseconds; + } + + + /** + * Add the given number of seconds offset + * @param inOffset number of seconds to add/subtract + */ + public void addOffsetSeconds(long inOffset) + { + _milliseconds += (inOffset * 1000L); + _text = null; + } + + + /** + * @param inFormat format of timestamp + * @param inTimezone timezone to use + * @return Description of timestamp in required format + */ + public String getText(Format inFormat, TimeZone inTimezone) + { + // Use the cached text if possible + if (isValid() + && _text != null + && inFormat == Format.ORIGINAL) + { + return _text; + } + + // Nothing cached, so use the regular one + return super.getText(inFormat, inTimezone); + } + + /** + * Utility method for formatting dates / times + * @param inFormat formatter object + * @param inTimezone timezone to use + * @return formatted String + */ + protected String format(DateFormat inFormat, TimeZone inTimezone) + { + CALENDAR.setTimeZone(TimeZone.getTimeZone("GMT")); + inFormat.setTimeZone(inTimezone == null ? TimeZone.getTimeZone("GMT") : inTimezone); + + CALENDAR.setTimeInMillis(_milliseconds); + return inFormat.format(CALENDAR.getTime()); + } + + /** + * @return a Calendar object representing this timestamp + */ + public Calendar getCalendar(TimeZone inZone) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + cal.setTimeInMillis(_milliseconds); + return cal; + } +} diff --git a/tim/prune/data/Track.java b/tim/prune/data/Track.java index 4ae47ff..f45854a 100644 --- a/tim/prune/data/Track.java +++ b/tim/prune/data/Track.java @@ -320,7 +320,7 @@ public class Track * @param inUndo true for undo operation * @return true on success */ - public boolean addTimeOffset(int inStart, int inEnd, long inOffset, boolean inUndo) + public boolean addTimeOffsetSeconds(int inStart, int inEnd, long inOffset, boolean inUndo) { // sanity check if (inStart < 0 || inEnd < 0 || inStart >= inEnd || inEnd >= _numPoints) { @@ -335,7 +335,7 @@ public class Track { // This point has a timestamp so add the offset to it foundTimestamp = true; - p.addTimeOffset(inOffset); + p.addTimeOffsetSeconds(inOffset); p.setModified(inUndo); } } diff --git a/tim/prune/function/AboutScreen.java b/tim/prune/function/AboutScreen.java index d2f5712..80a7983 100644 --- a/tim/prune/function/AboutScreen.java +++ b/tim/prune/function/AboutScreen.java @@ -34,7 +34,6 @@ import tim.prune.ExternalTools; import tim.prune.GenericFunction; import tim.prune.GpsPrune; import tim.prune.I18nManager; -import tim.prune.jpeg.ExifGateway; import tim.prune.threedee.WindowFactory; /** @@ -98,8 +97,8 @@ public class AboutScreen extends GenericFunction descBuffer.append("

").append(I18nManager.getText("dialog.about.summarytext3")).append("

"); descBuffer.append("

").append(I18nManager.getText("dialog.about.languages")).append(" : ") .append("afrikaans, \u010de\u0161tina, deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano,
" + - " magyar, nederlands, polski, portugu\u00EAs, rom\u00E2n\u0103, \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, ukrainian

"); + " magyar, nederlands, polski, portugu\u00EAs, rom\u00E2n\u0103, suomi, \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, ukrainian

"); descBuffer.append("

").append(I18nManager.getText("dialog.about.translatedby")).append("

"); JEditorPane descPane = new JEditorPane("text/html", descBuffer.toString()); descPane.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); @@ -153,13 +152,6 @@ public class AboutScreen extends GenericFunction addToGridBagPanel(sysInfoPanel, gridBag, constraints, _installedLabels[3], 1, 5); addToGridBagPanel(sysInfoPanel, gridBag, constraints, new JLabel("Xerces : "), 0, 6); addToGridBagPanel(sysInfoPanel, gridBag, constraints, _installedLabels[4], 1, 6); - // Exif library - addToGridBagPanel(sysInfoPanel, gridBag, constraints, - new JLabel(I18nManager.getText("dialog.about.systeminfo.exiflib") + " : "), - 0, 7); - final String exiflibkey = "dialog.about.systeminfo.exiflib." + ExifGateway.getDescriptionKey(); - addToGridBagPanel(sysInfoPanel, gridBag, constraints, - new JLabel(I18nManager.getText(exiflibkey)), 1, 7); _tabs.add(I18nManager.getText("dialog.about.systeminfo"), sysInfoPanel); // Third pane for credits @@ -198,35 +190,38 @@ public class AboutScreen extends GenericFunction new JLabel(" theYinYeti, Rothermographer, Sam, Rudolph, nazotoko,"), 1, 4); addToGridBagPanel(creditsPanel, gridBag, constraints, - new JLabel(" katpatuka, R\u00E9mi, Marcus, Ali, Javier, Jeroen, prot_d, Gy\u00F6rgy,"), + new JLabel(" katpatuka, R\u00E9mi, Marcus, Ali, Javier, Jeroen, prot_d,"), 1, 5); addToGridBagPanel(creditsPanel, gridBag, constraints, - new JLabel(" HooAU, Sergey, P\u00E9ter, serhijdubyk, Peter, Cristian, Roman"), + new JLabel(" Gy\u00F6rgy, HooAU, Sergey, P\u00E9ter, serhijdubyk, Peter, Cristian,"), 1, 6); + addToGridBagPanel(creditsPanel, gridBag, constraints, + new JLabel(" Roman, Erkki"), + 1, 7); addToGridBagPanel(creditsPanel, gridBag, constraints, new JLabel(I18nManager.getText("dialog.about.credits.translations") + " : "), - 0, 7); + 0, 8); addToGridBagPanel(creditsPanel, gridBag, constraints, new JLabel("Open Office, Gpsdrive, Babelfish, Leo, Launchpad"), - 1, 7); + 1, 8); addToGridBagPanel(creditsPanel, gridBag, constraints, new JLabel(I18nManager.getText("dialog.about.credits.devtools") + " : "), - 0, 8); + 0, 9); addToGridBagPanel(creditsPanel, gridBag, constraints, new JLabel("Debian Linux, Sun Java, OpenJDK, Eclipse, Svn, Gimp, Inkscape"), - 1, 8); + 1, 9); addToGridBagPanel(creditsPanel, gridBag, constraints, new JLabel(I18nManager.getText("dialog.about.credits.othertools") + " : "), - 0, 9); + 0, 10); addToGridBagPanel(creditsPanel, gridBag, constraints, new JLabel("Openstreetmap, Povray, Exiftool, Gpsbabel, Gnuplot"), - 1, 9); + 1, 10); addToGridBagPanel(creditsPanel, gridBag, constraints, new JLabel(I18nManager.getText("dialog.about.credits.thanks") + " : "), - 0, 10); + 0, 11); addToGridBagPanel(creditsPanel, gridBag, constraints, new JLabel("Friends and loved ones, for encouragement and support"), - 1, 10); + 1, 11); _tabs.add(I18nManager.getText("dialog.about.credits"), creditsPanel); // Read me diff --git a/tim/prune/function/AddTimeOffset.java b/tim/prune/function/AddTimeOffset.java index bbfda5e..5ccdc39 100644 --- a/tim/prune/function/AddTimeOffset.java +++ b/tim/prune/function/AddTimeOffset.java @@ -118,8 +118,9 @@ public class AddTimeOffset extends GenericFunction // Listeners to enable/disable ok button KeyAdapter keyListener = new KeyAdapter() { /** Key typed */ - public void keyTyped(KeyEvent arg0) { - _okButton.setEnabled(getOffsetSecs() != 0L); + public void keyTyped(KeyEvent event) { + final boolean isNumber = "1234567890".indexOf(event.getKeyChar()) >= 0; + _okButton.setEnabled(isNumber || getOffsetSecs() != 0L); } }; MouseAdapter mouseListener = new MouseAdapter() { @@ -194,7 +195,7 @@ public class AddTimeOffset extends GenericFunction if (offsetSecs != 0L) { // Pass offset back to app and close dialog - _app.finishAddTimeOffset(offsetSecs); + _app.finishAddTimeOffsetSeconds(offsetSecs); _dialog.dispose(); } } diff --git a/tim/prune/function/CheckVersionScreen.java b/tim/prune/function/CheckVersionScreen.java index 8c2394e..701937d 100644 --- a/tim/prune/function/CheckVersionScreen.java +++ b/tim/prune/function/CheckVersionScreen.java @@ -106,7 +106,7 @@ public class CheckVersionScreen extends GenericFunction == JOptionPane.YES_OPTION) { // User selected to launch home page - BrowserLauncher.launchBrowser("http://activityworkshop.net/software/gpsprune/download.html"); + BrowserLauncher.launchBrowser("https://activityworkshop.net/software/gpsprune/download.html"); } } } diff --git a/tim/prune/function/ConvertNamesToTimes.java b/tim/prune/function/ConvertNamesToTimes.java index c0286ab..2368dfd 100644 --- a/tim/prune/function/ConvertNamesToTimes.java +++ b/tim/prune/function/ConvertNamesToTimes.java @@ -7,7 +7,7 @@ import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.data.DataPoint; import tim.prune.data.Field; -import tim.prune.data.Timestamp; +import tim.prune.data.TimestampUtc; import tim.prune.data.Track; import tim.prune.undo.UndoConvertNamesToTimes; @@ -51,8 +51,9 @@ public class ConvertNamesToTimes extends GenericFunction DataPoint point = track.getPoint(i); if (point.isWaypoint()) { - Timestamp tstamp = new Timestamp(point.getWaypointName()); - if (tstamp.isValid()) { + TimestampUtc tstamp = new TimestampUtc(point.getWaypointName()); + if (tstamp.isValid()) + { // timestamp could be parsed! point.setFieldValue(Field.TIMESTAMP, point.getWaypointName(), false); // set waypoint name to nothing (track point) diff --git a/tim/prune/function/CreateMarkerWaypointsFunction.java b/tim/prune/function/CreateMarkerWaypointsFunction.java new file mode 100644 index 0000000..f3dd1f1 --- /dev/null +++ b/tim/prune/function/CreateMarkerWaypointsFunction.java @@ -0,0 +1,145 @@ +package tim.prune.function; + +import java.util.ArrayList; + +import tim.prune.App; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.data.DataPoint; +import tim.prune.data.Field; +import tim.prune.data.FieldList; +import tim.prune.data.Track; +import tim.prune.undo.UndoAppendPoints; + +/** + * Function to create waypoints marking either + * at regular distance intervals or time intervals + */ +public class CreateMarkerWaypointsFunction extends DistanceTimeLimitFunction +{ + /** ArrayList of points to append to the track */ + private ArrayList _pointsToAdd = new ArrayList(); + /** Counter of previously used multiple */ + private int _previousMultiple = 0; + + + /** + * Constructor + */ + public CreateMarkerWaypointsFunction(App inApp) { + super(inApp); + } + + /** + * @return name key + */ + public String getNameKey() { + return "function.createmarkerwaypoints"; + } + + /** + * Init the state to start collecting a new set of points + */ + private void initMemory() + { + _pointsToAdd.clear(); + _previousMultiple = 0; + } + + /** + * The dialog has been completed and OK pressed, so do the point creation + */ + protected void performFunction() + { + // Distribute either by distance or time + final int timeLimitSeconds = getTimeLimitInSeconds(); + final boolean createByTime = (timeLimitSeconds > 0); + final double distLimitRadians = getDistanceLimitRadians(); + final boolean createByDistance = (distLimitRadians > 0.0); + if (!createByTime && !createByDistance) { + return; // neither option selected + } + + // Make undo object + final int numPoints = _app.getTrackInfo().getTrack().getNumPoints(); + UndoAppendPoints undo = new UndoAppendPoints(numPoints); + + // set up the memory from scratch to collect the created points + initMemory(); + + // Make new waypoints, looping through the points in the track + DataPoint currPoint = null, prevPoint = null; + double currValue = 0.0, prevValue = 0.0; + for (int i=0; i _distUnitsDropdown = null; + /** Text field for entering distance */ + private WholeNumberField _distanceField = null; + /** Text fields for entering distance */ + private WholeNumberField _limitHourField = null, _limitMinField = null; + /** Ok and cancel buttons */ + private JButton _okButton = null; + private JButton _cancelButton = 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 DistanceTimeLimitFunction(App inApp) { + super(inApp); + } + + /** + * 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); + if (!hasTimestamps) + { + _distLimitRadio.setSelected(true); + } + // set focus to Cancel button so that pressing "Esc" works + _cancelButton.requestFocus(); + _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) { + performFunction(); + } + }); + buttonPanel.add(_okButton); + // Cancel button + _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()); + } + + /** + * @return selected time limit in seconds, or 0 + */ + protected int getTimeLimitInSeconds() + { + if (_timeLimitRadio.isSelected() + && (_limitHourField.getValue() > 0 || _limitMinField.getValue() > 0)) + { + int timeLimitSeconds = _limitHourField.getValue() * 60 * 60 + + _limitMinField.getValue() * 60; + if (timeLimitSeconds > 0) + return timeLimitSeconds; + } + return 0; + } + + /** + * @return selected distance limit in radians, or 0.0 + */ + protected double getDistanceLimitRadians() + { + if (_distLimitRadio.isSelected() + && _distanceField.getValue() > 0) + { + final Unit[] distUnits = {UnitSetLibrary.UNITS_KILOMETRES, + UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_MILES}; + Unit distUnit = distUnits[_distUnitsDropdown.getSelectedIndex()]; + double distLimitRadians = Distance.convertDistanceToRadians(_distanceField.getValue(), distUnit); + return distLimitRadians; + } + return 0.0; + } + + /** + * The dialog has been completed and OK pressed, so do the corresponding function + */ + protected abstract void performFunction(); + + /** + * Create a description for the given multiple of the configured limit + * @param inMultiple multiple of selected limit + * @return language-specific String description for waypoint names + */ + protected String createLimitDescription(int inMultiple) + { + if (_distLimitRadio.isSelected() + && _distanceField.getValue() > 0) + { + final int distLimit = inMultiple * _distanceField.getValue(); + final String[] distUnits = {"kilometres", "metres", "miles"}; + final String unitKey = "units." + distUnits[_distUnitsDropdown.getSelectedIndex()] + ".short"; + return "" + distLimit + " " + I18nManager.getText(unitKey); + } + else if (_timeLimitRadio.isSelected()) + { + final long timeLimitSecs = (long) getTimeLimitInSeconds() * inMultiple; + return new TimeDifference(timeLimitSecs).getDescription(); + } + return null; + } +} diff --git a/tim/prune/function/DownloadOsmFunction.java b/tim/prune/function/DownloadOsmFunction.java index 76c69da..b3ee1b2 100644 --- a/tim/prune/function/DownloadOsmFunction.java +++ b/tim/prune/function/DownloadOsmFunction.java @@ -243,9 +243,10 @@ public class DownloadOsmFunction extends GenericFunction implements Runnable */ public void run() { - final String url = "http://www.overpass-api.de/api/xapi?map?bbox=" + - _latLonLabels[1].getText() + "," + _latLonLabels[3].getText() + "," + - _latLonLabels[2].getText() + "," + _latLonLabels[0].getText(); + String url = "http://overpass-api.de/api/interpreter?data=(node(" + + _latLonLabels[3].getText() + "," + _latLonLabels[1].getText() + "," + + _latLonLabels[0].getText() + "," + _latLonLabels[2].getText() + ");<;);out%20qt;"; + // System.out.println(url); byte[] buffer = new byte[1024]; InputStream inStream = null; diff --git a/tim/prune/function/Export3dFunction.java b/tim/prune/function/Export3dFunction.java index d32828a..f3b7aa5 100644 --- a/tim/prune/function/Export3dFunction.java +++ b/tim/prune/function/Export3dFunction.java @@ -6,7 +6,7 @@ import tim.prune.threedee.ImageDefinition; import tim.prune.threedee.TerrainDefinition; /** - * Abstract superclass for pov and svg export functions + * Abstract superclass of any 3d export function, currently only the PovExporter */ public abstract class Export3dFunction extends GenericFunction { diff --git a/tim/prune/function/HelpScreen.java b/tim/prune/function/HelpScreen.java index 25fb8ec..dc1e059 100644 --- a/tim/prune/function/HelpScreen.java +++ b/tim/prune/function/HelpScreen.java @@ -41,7 +41,7 @@ public class HelpScreen extends GenericFunction == JOptionPane.YES_OPTION) { // User selected to launch home page - BrowserLauncher.launchBrowser("http://activityworkshop.net/software/gpsprune/index.html"); + BrowserLauncher.launchBrowser("https://activityworkshop.net/software/gpsprune/index.html"); } } } diff --git a/tim/prune/function/SearchOsmPoisFunction.java b/tim/prune/function/SearchOsmPoisFunction.java new file mode 100644 index 0000000..a35c7b3 --- /dev/null +++ b/tim/prune/function/SearchOsmPoisFunction.java @@ -0,0 +1,179 @@ +package tim.prune.function; + +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import tim.prune.App; +import tim.prune.I18nManager; +import tim.prune.data.Coordinate; +import tim.prune.data.DataPoint; +import tim.prune.data.Distance; +import tim.prune.data.Field; +import tim.prune.data.Latitude; +import tim.prune.data.Longitude; +import tim.prune.function.search.GenericDownloaderFunction; +import tim.prune.function.search.SearchResult; + +/** + * Function to load nearby point information from OSM + */ +public class SearchOsmPoisFunction extends GenericDownloaderFunction +{ + /** Maximum distance from point in m */ + private static final int MAX_DISTANCE = 250; + /** Coordinates to search for */ + private double _searchLatitude = 0.0, _searchLongitude = 0.0; + + + /** + * Constructor + * @param inApp App object + */ + public SearchOsmPoisFunction(App inApp) { + super(inApp); + } + + /** + * @return name key + */ + public String getNameKey() { + return "function.searchosmpois"; + } + + /** + * @param inColNum index of column, 0 or 1 + * @return key for this column + */ + protected String getColumnKey(int inColNum) + { + if (inColNum == 0) return "dialog.osmpois.column.name"; + return "dialog.osmpois.column.type"; + } + + + /** + * Run method to get the nearby points in a separate thread + */ + public void run() + { + _statusLabel.setText(I18nManager.getText("confirm.running")); + // Get coordinates from current point (if any) or from centre of screen + DataPoint point = _app.getTrackInfo().getCurrentPoint(); + if (point == null) + { + double[] coords = _app.getViewport().getBounds(); + _searchLatitude = (coords[0] + coords[2]) / 2.0; + _searchLongitude = (coords[1] + coords[3]) / 2.0; + } + else + { + _searchLatitude = point.getLatitude().getDouble(); + _searchLongitude = point.getLongitude().getDouble(); + } + + // Submit search (language not an issue here) + submitSearch(_searchLatitude, _searchLongitude); + + // Set status label according to error or "none found", leave blank if ok + if (_errorMessage == null && _trackListModel.isEmpty()) { + _errorMessage = I18nManager.getText("dialog.osmpois.nonefound"); + } + _statusLabel.setText(_errorMessage == null ? "" : _errorMessage); + } + + /** + * Submit the search for the given parameters + * @param inLat latitude + * @param inLon longitude + */ + private void submitSearch(double inLat, double inLon) + { + String coords = "around:" + MAX_DISTANCE + "," + inLat + "," + inLon; + String urlString = "http://overpass-api.de/api/interpreter?data=(" + + "node(" + coords + ")[\"amenity\"][\"name\"];" + + "node(" + coords + ")[\"railway\"][\"name\"];" + + "node(" + coords + ")[\"highway\"][\"name\"];" + + ");out%20qt;"; + //System.out.println(urlString); + // Parse the returned XML with a special handler + SearchOsmPoisXmlHandler xmlHandler = new SearchOsmPoisXmlHandler(); + InputStream inStream = null; + + try + { + URL url = new URL(urlString); + SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); + inStream = url.openStream(); + saxParser.parse(inStream, xmlHandler); + } + catch (Exception e) { + _errorMessage = e.getClass().getName() + " - " + e.getMessage(); + } + // Close stream and ignore errors + try { + inStream.close(); + } catch (Exception e) {} + + // Calculate distances for each returned point + DataPoint searchPoint = new DataPoint(new Latitude(_searchLatitude, Coordinate.FORMAT_DECIMAL_FORCE_POINT), + new Longitude(_searchLongitude, Coordinate.FORMAT_DECIMAL_FORCE_POINT), null); + for (SearchResult result : xmlHandler.getPointList()) + { + Latitude pointLat = new Latitude(result.getLatitude()); + Longitude pointLon = new Longitude(result.getLongitude()); + DataPoint foundPoint = new DataPoint(pointLat, pointLon, null); + double dist = DataPoint.calculateRadiansBetween(searchPoint, foundPoint); + result.setLength(Distance.convertRadiansToDistance(dist)); + } + + // TODO: maybe limit number of results using MAX_RESULTS + // Add track list to model + ArrayList pointList = xmlHandler.getPointList(); + _trackListModel.addTracks(pointList, true); + _trackListModel.setShowPointTypes(true); + + // Show error message if any + if (_trackListModel.isEmpty()) + { + String error = xmlHandler.getErrorMessage(); + if (error != null && !error.equals("")) + { + _app.showErrorMessageNoLookup(getNameKey(), error); + _errorMessage = error; + } + } + } + + /** + * Load the selected point(s) + */ + protected void loadSelected() + { + // Find the rows selected in the table and get the corresponding coords + int numSelected = _trackTable.getSelectedRowCount(); + if (numSelected < 1) return; + int[] rowNums = _trackTable.getSelectedRows(); + for (int i=0; i= 0 && rowNum < _trackListModel.getRowCount()) + { + String lat = _trackListModel.getTrack(rowNum).getLatitude(); + String lon = _trackListModel.getTrack(rowNum).getLongitude(); + if (lat != null && lon != null) + { + DataPoint point = new DataPoint(new Latitude(lat), new Longitude(lon), null); + point.setFieldValue(Field.WAYPT_NAME, _trackListModel.getTrack(rowNum).getTrackName(), false); + _app.createPoint(point); + } + } + } + // Close the dialog + _cancelled = true; + _dialog.dispose(); + } +} diff --git a/tim/prune/function/SearchOsmPoisXmlHandler.java b/tim/prune/function/SearchOsmPoisXmlHandler.java new file mode 100644 index 0000000..5683ece --- /dev/null +++ b/tim/prune/function/SearchOsmPoisXmlHandler.java @@ -0,0 +1,92 @@ +package tim.prune.function; + +import java.util.ArrayList; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import tim.prune.function.search.SearchResult; + +/** + * XML handler for dealing with XML returned from the OSM Overpass api, + * specially for the OSM Poi service + */ +public class SearchOsmPoisXmlHandler extends DefaultHandler +{ + private ArrayList _pointList = null; + private SearchResult _currPoint = null; + private String _errorMessage = null; + + + /** + * React to the start of an XML tag + */ + public void startElement(String inUri, String inLocalName, String inTagName, + Attributes inAttributes) throws SAXException + { + if (inTagName.equals("osm")) { + _pointList = new ArrayList(); + } + else if (inTagName.equals("node")) + { + _currPoint = new SearchResult(); + _currPoint.setLatitude(inAttributes.getValue("lat")); + _currPoint.setLongitude(inAttributes.getValue("lon")); + } + else if (inTagName.equals("tag") && _currPoint != null) { + processTag(inAttributes); + } + super.startElement(inUri, inLocalName, inTagName, inAttributes); + } + + /** + * @param inAttributes attributes to process + */ + private void processTag(Attributes inAttributes) + { + String key = inAttributes.getValue("k"); + if (key != null) + { + String value = inAttributes.getValue("v"); + if (key.equals("name")) + { + _currPoint.setTrackName(value); + } + else if (key.equals("amenity") || key.equals("highway") || key.equals("railway")) + { + _currPoint.setPointType(value); + } + } + } + + /** + * React to the end of an XML tag + */ + public void endElement(String inUri, String inLocalName, String inTagName) + throws SAXException + { + if (inTagName.equals("node")) + { + // end of the entry + if (_currPoint.getTrackName() != null && !_currPoint.getTrackName().equals("")) + _pointList.add(_currPoint); + } + super.endElement(inUri, inLocalName, inTagName); + } + + /** + * @return the list of points + */ + public ArrayList getPointList() + { + return _pointList; + } + + /** + * @return error message, if any + */ + public String getErrorMessage() { + return _errorMessage; + } +} diff --git a/tim/prune/function/SelectTimezoneFunction.java b/tim/prune/function/SelectTimezoneFunction.java new file mode 100644 index 0000000..8f5f778 --- /dev/null +++ b/tim/prune/function/SelectTimezoneFunction.java @@ -0,0 +1,640 @@ +package tim.prune.function; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.TimeZone; +import java.util.TreeSet; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import tim.prune.App; +import tim.prune.DataSubscriber; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.config.Config; +import tim.prune.gui.CombinedListAndModel; +import tim.prune.gui.GuiGridLayout; + +/** + * Class to provide the gui for selecting an alternative timezone + */ +public class SelectTimezoneFunction extends GenericFunction +{ + /** Arraylist of timezone infos */ + private ArrayList _zoneInfo; + /** Dialog */ + private JDialog _dialog = null; + /** Radio button to select system timezone instead of using listboxes */ + private JRadioButton _systemRadio = null; + /** Radio button to select timezone using listboxes */ + private JRadioButton _customRadio = null; + /** Array of list boxes */ + private CombinedListAndModel[] _listBoxes = null; + /** Label for selected zone */ + private JLabel _selectedZoneLabel = null; + /** Label for offset of selected zone */ + private JLabel _selectedOffsetLabel = null; + /** OK button for finishing */ + private JButton _okButton = null; + + private static final int LIST_REGIONS = 0; + private static final int LIST_OFFSETS = 1; + private static final int LIST_GROUPS = 2; + private static final int LIST_NAMES = 3; + + /** + * Inner class for listening to list clicks + */ + class ListListener implements ListSelectionListener + { + private int _key = 0; + /** Constructor */ + ListListener(int inKey) {_key = inKey;} + /** Listen for selection changes */ + public void valueChanged(ListSelectionEvent inEvent) { + if (!inEvent.getValueIsAdjusting()) { + processListClick(_key); + } + } + } + + /** Inner class to hold categorisation info for a timezone */ + class TimezoneDetails + { + public String _id; + public String _region; + public int _offset; + public String _group; + public String _name; + } + + /** + * Constructor + * @param inApp App object + */ + public SelectTimezoneFunction(App inApp) + { + super(inApp); + } + + /** Get the name key */ + public String getNameKey() { + return "function.selecttimezone"; + } + + /** + * 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(); + } + collectTimezoneInfo(); + _systemRadio.setText(I18nManager.getText("dialog.settimezone.system") + " (" + + TimeZone.getDefault().getID() + ")"); + // Set up dialog according to current config + String selectedTimezone = Config.getConfigString(Config.KEY_TIMEZONE_ID); + if (selectedTimezone == null || selectedTimezone.equals("")) + { + _systemRadio.setSelected(true); + } + else + { + _customRadio.setSelected(true); + } + _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)); + // Listener for radio buttons + ActionListener radioListener = new ActionListener() { + public void actionPerformed(ActionEvent inEvent) { + radioSelected(_systemRadio.isSelected()); + } + }; + FocusListener radioFocusListener = new FocusAdapter() { + public void focusGained(FocusEvent inEvent) { + radioSelected(_systemRadio.isSelected()); + } + }; + + // Panel at top + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS)); + JLabel topLabel = new JLabel(I18nManager.getText("dialog.settimezone.intro")); + topLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + topPanel.add(topLabel); + _systemRadio = new JRadioButton(I18nManager.getText("dialog.settimezone.system")); + _systemRadio.addActionListener(radioListener); + _systemRadio.addFocusListener(radioFocusListener); + topPanel.add(_systemRadio); + _customRadio = new JRadioButton(I18nManager.getText("dialog.settimezone.custom")); + _customRadio.addActionListener(radioListener); + _customRadio.addFocusListener(radioFocusListener); + topPanel.add(_customRadio); + ButtonGroup radioGroup = new ButtonGroup(); + radioGroup.add(_systemRadio); radioGroup.add(_customRadio); + dialogPanel.add(topPanel, BorderLayout.NORTH); + + // Main panel with box layout, list Panel with four lists in a grid + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + + JPanel listsPanel = new JPanel(); + listsPanel.setLayout(new GridLayout(1, 4)); + _listBoxes = new CombinedListAndModel[4]; + // First list for regions + _listBoxes[LIST_REGIONS] = new CombinedListAndModel(0); + // Add listener for list selection changes + _listBoxes[LIST_REGIONS].addListSelectionListener(new ListListener(LIST_REGIONS)); + JScrollPane scrollPane = new JScrollPane(_listBoxes[LIST_REGIONS]); + scrollPane.setPreferredSize(new Dimension(100, 200)); + scrollPane.setMinimumSize(new Dimension(100, 200)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + listsPanel.add(scrollPane); + + // second list for offsets + _listBoxes[LIST_OFFSETS] = new CombinedListAndModel(1); + _listBoxes[LIST_OFFSETS].setMaxNumEntries(24); + _listBoxes[LIST_OFFSETS].addListSelectionListener(new ListListener(LIST_OFFSETS)); + scrollPane = new JScrollPane(_listBoxes[LIST_OFFSETS]); + scrollPane.setPreferredSize(new Dimension(100, 200)); + scrollPane.setMinimumSize(new Dimension(100, 200)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + listsPanel.add(scrollPane); + + // third list for groups + _listBoxes[LIST_GROUPS] = new CombinedListAndModel(2); + _listBoxes[LIST_GROUPS].setMaxNumEntries(20); + _listBoxes[LIST_GROUPS].addListSelectionListener(new ListListener(LIST_GROUPS)); + scrollPane = new JScrollPane(_listBoxes[LIST_GROUPS]); + scrollPane.setPreferredSize(new Dimension(100, 200)); + scrollPane.setMinimumSize(new Dimension(100, 200)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + listsPanel.add(scrollPane); + + // fourth list for names + _listBoxes[LIST_NAMES] = new CombinedListAndModel(3); + _listBoxes[LIST_NAMES].setMaxNumEntries(20); + _listBoxes[LIST_NAMES].addListSelectionListener(new ListListener(LIST_NAMES)); + scrollPane = new JScrollPane(_listBoxes[LIST_NAMES]); + scrollPane.setPreferredSize(new Dimension(100, 200)); + scrollPane.setMinimumSize(new Dimension(100, 200)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + listsPanel.add(scrollPane); + mainPanel.add(listsPanel); + + // Details labels underneath lists - description and offset + JPanel detailsPanel = new JPanel(); + GuiGridLayout grid = new GuiGridLayout(detailsPanel); + grid.add(new JLabel(I18nManager.getText("dialog.settimezone.selectedzone") + " :")); + _selectedZoneLabel = new JLabel(""); + grid.add(_selectedZoneLabel); + grid.add(new JLabel(I18nManager.getText("dialog.settimezone.offsetfromutc") + " :")); + _selectedOffsetLabel = new JLabel(""); + grid.add(_selectedOffsetLabel); + mainPanel.add(detailsPanel); + dialogPanel.add(mainPanel, BorderLayout.CENTER); + + // close window if escape pressed + KeyAdapter escListener = new KeyAdapter() { + public void keyReleased(KeyEvent inE) { + if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) { + _dialog.dispose(); + } + } + }; + _listBoxes[LIST_REGIONS].addKeyListener(escListener); + _listBoxes[LIST_OFFSETS].addKeyListener(escListener); + + // 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) { + finishSelectTimezone(); + } + }); + buttonPanel.add(_okButton); + _okButton.addKeyListener(escListener); + // Cancel button + JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + _dialog.dispose(); + } + }); + cancelButton.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent inE) { + if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();} + } + }); + buttonPanel.add(cancelButton); + dialogPanel.add(buttonPanel, BorderLayout.SOUTH); + return dialogPanel; + } + + /** + * React to changes in the radio buttons + * @param inUseSystem true for system, false for custom + */ + private void radioSelected(boolean inUseSystem) + { + for (int i=0; i<_listBoxes.length; i++) + { + if (inUseSystem) + { + _listBoxes[i].clear(); + } + _listBoxes[i].setEnabled(!inUseSystem); + } + if (!inUseSystem) + { + populateTimezoneRegions(); + populateTimezoneOffsets(null); + preselectTimezone(Config.getConfigString(Config.KEY_TIMEZONE_ID)); + } + showTimezoneDetails(); + } + + /** + * React to a selection change on one of our lists + * @param inKey key of list which was clicked + */ + private void processListClick(int inKey) + { + final boolean offsetSelected = _listBoxes[LIST_OFFSETS].getSelectedItem() != null; + final boolean groupSelected = _listBoxes[LIST_GROUPS].getSelectedItem() != null; + // Update offsets? + if (inKey == LIST_REGIONS) + { + populateTimezoneOffsets(_listBoxes[LIST_REGIONS].getSelectedItem()); + } + // Update groups? + if (inKey == LIST_OFFSETS + || (inKey == LIST_REGIONS && !offsetSelected)) + { + populateTimezoneGroups(_listBoxes[LIST_REGIONS].getSelectedItem(), _listBoxes[LIST_OFFSETS].getSelectedItem()); + } + // Update names? + if (inKey == LIST_GROUPS + || (inKey <= LIST_OFFSETS && !groupSelected)) + { + populateTimezoneNames(_listBoxes[LIST_REGIONS].getSelectedItem(), _listBoxes[LIST_OFFSETS].getSelectedItem(), + _listBoxes[LIST_GROUPS].getSelectedItem()); + } + // Show the details of the selected timezone + showTimezoneDetails(); + } + + /** + * Use the system information to populate the list of available timezones + */ + private void collectTimezoneInfo() + { + _zoneInfo = new ArrayList(); + for (String id : TimeZone.getAvailableIDs()) + { + String region = getRegion(id); + if (region != null) + { + TimeZone tz = TimeZone.getTimeZone(id); + TimezoneDetails details = new TimezoneDetails(); + details._id = id; + details._region = region; + details._offset = tz.getOffset(System.currentTimeMillis()) / 1000 / 60; + details._group = tz.getDisplayName(); + details._name = getNameWithoutRegion(id); + _zoneInfo.add(details); + } + } + } + + /** + * Populate the timezone regions into the region list + */ + private void populateTimezoneRegions() + { + _listBoxes[LIST_REGIONS].clear(); + TreeSet regions = new TreeSet(); + for (TimezoneDetails currZone : _zoneInfo) + { + regions.add(currZone._region); + } + for (String region : regions) + { + _listBoxes[LIST_REGIONS].addItem(region); + } + } + + /** + * Extract the timezone region from the id + */ + private static String getRegion(String inId) + { + final int slashPos = (inId == null ? -1 : inId.indexOf('/')); + if (slashPos > 0) + { + return inId.substring(0, slashPos); + } + return null; + } + + /** + * Populate the second listbox with the offsets for the given region + * @param inRegion selected region, or null if none selected + */ + private void populateTimezoneOffsets(String inRegion) + { + _listBoxes[LIST_OFFSETS].clear(); + TreeSet offsetsinMinutes = new TreeSet(); + for (TimezoneDetails currZone : _zoneInfo) + { + String region = currZone._region; + if (inRegion == null || region.equals(inRegion)) + { + offsetsinMinutes.add(currZone._offset); + } + } + for (Integer offset : offsetsinMinutes) + { + _listBoxes[LIST_OFFSETS].addItem(makeOffsetString(offset)); + } + } + + /** + * @return String containing offset for display + */ + private static String makeOffsetString(int inOffsetInMinutes) + { + if (inOffsetInMinutes == 0) return "0"; + final boolean isWholeHours = (inOffsetInMinutes % 60) == 0; + if (isWholeHours) + { + return (inOffsetInMinutes > 0 ? "+" : "") + (inOffsetInMinutes / 60); + } + final double numHours = inOffsetInMinutes / 60.0; + return (inOffsetInMinutes > 0 ? "+" : "") + numHours; + } + + /** + * Populate the group list using the specified region and offset + * @param inRegion selected region (if any) from the first list + * @param inOffset selected offset (if any) from the second list + */ + private void populateTimezoneGroups(String inRegion, String inOffset) + { + _listBoxes[LIST_GROUPS].clear(); + // Convert given offset string (in hours) into numeric offset (in minutes) + final int offsetMins = convertToMinutes(inOffset); + + TreeSet zoneGroups = new TreeSet(); + for (TimezoneDetails currZone : _zoneInfo) + { + if (inRegion == null || currZone._region.equals(inRegion)) + { + if (offsetMins == -1 || offsetMins == currZone._offset) + { + zoneGroups.add(currZone._group); + } + } + } + // If the region and offset were given, then list is unlimited + _listBoxes[LIST_GROUPS].setUnlimited(inRegion != null && inOffset != null); + // Add all the found names to the listbox + for (String group : zoneGroups) + { + _listBoxes[LIST_GROUPS].addItem(group); + } + } + + /** + * Populate the group list using the specified region, offset and group + * @param inRegion selected region (if any) from the first list + * @param inOffset selected offset (if any) from the second list + * @param inGroup selected group (if any) from the third list + */ + private void populateTimezoneNames(String inRegion, String inOffset, String inGroup) + { + CombinedListAndModel nameList = _listBoxes[LIST_NAMES]; + nameList.clear(); + // Convert given offset string (in hours) into numeric offset (in minutes) + final int offsetMins = convertToMinutes(inOffset); + + TreeSet zoneNames = new TreeSet(); + for (TimezoneDetails currZone : _zoneInfo) + { + if ((inRegion == null || currZone._region.equals(inRegion)) + && (offsetMins == -1 || currZone._offset == offsetMins) + && (inGroup == null || currZone._group.equals(inGroup))) + { + zoneNames.add(currZone._name); + } + } + // If the region and offset were given, then list is unlimited + nameList.setUnlimited(inRegion != null && inOffset != null && inRegion != null); + // Add all the found names to the listbox + for (String name : zoneNames) + { + nameList.addItem(name); + } + } + + /** + * Convert the given String from hours to minutes + * @param inOffsetInHours String from listbox in +/- hours + * @return offset in minutes, or -1 + */ + private static int convertToMinutes(String inOffsetInHours) + { + int offsetMins = -1; + try { + offsetMins = (int) (60 * Double.parseDouble(inOffsetInHours)); + } + catch (NumberFormatException nfe) {} // offset stays -1 + catch (NullPointerException npe) {} // offset stays -1 + return offsetMins; + } + + /** + * Remove the timezone region from the id to just leave the name after the slash + */ + private static String getNameWithoutRegion(String inId) + { + final int slashPos = (inId == null ? -1 : inId.indexOf('/')); + if (slashPos > 0) + { + return inId.substring(slashPos + 1); + } + return null; + } + + /** + * Get the selected timezone, or null if none selected + */ + private TimeZone getSelectedTimezone() + { + if (_systemRadio.isSelected()) + { + return TimeZone.getDefault(); + } + + String chosenRegion = _listBoxes[LIST_REGIONS].getSelectedItem(); + // Convert given offset string (in hours) into numeric offset (in minutes) + final int offsetMins = convertToMinutes(_listBoxes[LIST_OFFSETS].getSelectedItem()); + String chosenGroup = _listBoxes[LIST_GROUPS].getSelectedItem(); + String chosenName = _listBoxes[LIST_NAMES].getSelectedItem(); + + TreeSet zoneIds = new TreeSet(); + for (TimezoneDetails currZone : _zoneInfo) + { + if ((chosenRegion == null || currZone._region.equals(chosenRegion)) + && (offsetMins == -1 || currZone._offset == offsetMins) + && (chosenGroup == null || currZone._group.equals(chosenGroup)) + && (chosenName == null || currZone._name.equals(chosenName))) + { + zoneIds.add(currZone._id); + if (zoneIds.size() > 1) { + break; // exit loop now, we've got too many + } + } + } + // Should have exactly one result now + if (zoneIds.size() == 1) + { + return TimeZone.getTimeZone(zoneIds.first()); + } + + // none selected (yet) + return null; + } + + /** + * Show the details of the selected timezone + */ + private void showTimezoneDetails() + { + TimeZone selectedTimezone = getSelectedTimezone(); + if (selectedTimezone == null) + { + // Clear details labels + _selectedZoneLabel.setText(""); + _selectedOffsetLabel.setText(""); + } + else + { + // Fill results in labels + String desc = selectedTimezone.getID() + " - " + selectedTimezone.getDisplayName(); + _selectedZoneLabel.setText(desc); + String offsets = getOffsetDescription(selectedTimezone); + _selectedOffsetLabel.setText(offsets); + } + _okButton.setEnabled(selectedTimezone != null); + } + + /** + * @param inTimezone selected timezone + * @return String describing the time offset(s) of this zone including winter/summer time + */ + private static String getOffsetDescription(TimeZone inTimezone) + { + if (inTimezone == null) + { + return ""; + } + TreeSet offsetsinMinutes = new TreeSet(); + long testTimeMillis = System.currentTimeMillis(); + final long testPeriodInMillis = 1000L * 60 * 60 * 24 * 30 * 2; + for (int i=0; i<5; i++) + { + offsetsinMinutes.add(inTimezone.getOffset(testTimeMillis) / 1000 / 60); + testTimeMillis += testPeriodInMillis; + } + // Make String describing the sorted set + StringBuffer buff = new StringBuffer(); + for (Integer offset : offsetsinMinutes) + { + if (buff.length() > 0) + { + buff.append(" / "); + } + buff.append(makeOffsetString(offset)); + } + return buff.toString(); + } + + /** + * On entry to the dialog, select the items in each listbox + * according to the given preselected timezone id + * @param zoneId id of zone to select + */ + private void preselectTimezone(String zoneId) + { + TimeZone tz = (zoneId == null ? TimeZone.getDefault() : TimeZone.getTimeZone(zoneId)); + if (tz != null) + { + _listBoxes[LIST_REGIONS].selectItem(getRegion(zoneId)); + _listBoxes[LIST_OFFSETS].selectItem(makeOffsetString(tz.getOffset(System.currentTimeMillis()) / 1000 / 60)); + _listBoxes[LIST_GROUPS].selectItem(tz.getDisplayName()); + _listBoxes[LIST_NAMES].selectItem(getNameWithoutRegion(zoneId)); + } + } + + /** + * Finish the dialog by setting the config according to the selected zone + */ + private void finishSelectTimezone() + { + TimeZone selectedTimezone = getSelectedTimezone(); + if (_systemRadio.isSelected() || selectedTimezone == null) + { + // Clear config, use default system timezone instead + Config.setConfigString(Config.KEY_TIMEZONE_ID, null); + } + else + { + // Get selected timezone, set in config + Config.setConfigString(Config.KEY_TIMEZONE_ID, selectedTimezone.getID()); + } + _dialog.dispose(); + // Make sure listeners know to update themselves + UpdateMessageBroker.informSubscribers(DataSubscriber.UNITS_CHANGED); + } +} diff --git a/tim/prune/function/SetLineWidth.java b/tim/prune/function/SetLineWidth.java deleted file mode 100644 index feb49c2..0000000 --- a/tim/prune/function/SetLineWidth.java +++ /dev/null @@ -1,58 +0,0 @@ -package tim.prune.function; - -import tim.prune.App; -import tim.prune.DataSubscriber; -import tim.prune.UpdateMessageBroker; -import tim.prune.config.Config; - -/** - * Function to set the width with which lines are drawn - */ -public class SetLineWidth extends SingleNumericParameterFunction -{ - - /** - * Constructor - * @param inApp App object - */ - public SetLineWidth(App inApp) { - super(inApp, 1, 4); - } - - /** @return name key */ - public String getNameKey() { - return "function.setlinewidth"; - } - - /** @return description key */ - public String getDescriptionKey() { - return "dialog.setlinewidth.text"; - } - - /** @return the current value to display */ - public int getCurrentParamValue() { - return Config.getConfigInt(Config.KEY_LINE_WIDTH); - } - - /** - * Run function - */ - public void begin() - { - // Not required, because this function is started from a ChooseSingleParameter function - // and goes directly to the completeFunction method. - } - - /** - * Complete the function using the given line width parameter - */ - public void completeFunction(int inLineWidth) - { - final int currLineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH); - if (inLineWidth >= 1 && inLineWidth <= 4 && inLineWidth != currLineWidth) - { - Config.setConfigInt(Config.KEY_LINE_WIDTH, inLineWidth); - UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); - } - } -} diff --git a/tim/prune/function/browser/UrlGenerator.java b/tim/prune/function/browser/UrlGenerator.java index faca5a2..7c923d3 100644 --- a/tim/prune/function/browser/UrlGenerator.java +++ b/tim/prune/function/browser/UrlGenerator.java @@ -31,7 +31,6 @@ public abstract class UrlGenerator MAP_SOURCE_BING, /* Bing */ MAP_SOURCE_PEAKFINDER, /* PeakFinder */ MAP_SOURCE_GEOHACK, /* Geohack */ - MAP_SOURCE_PANORAMIO, /* Panoramio */ } /** @@ -54,7 +53,6 @@ public abstract class UrlGenerator return generateBingUrl(inTrackInfo); case MAP_SOURCE_PEAKFINDER: case MAP_SOURCE_GEOHACK: - case MAP_SOURCE_PANORAMIO: return generateUrlForPoint(inSource, inTrackInfo); case MAP_SOURCE_OSM: default: @@ -209,8 +207,6 @@ public abstract class UrlGenerator return generatePeakfinderUrl(currPoint); case MAP_SOURCE_GEOHACK: return generateGeohackUrl(currPoint); - case MAP_SOURCE_PANORAMIO: - return generatePanoramioUrl(currPoint); default: return null; } @@ -240,17 +236,6 @@ public abstract class UrlGenerator // TODO: Could use absolute values and S, W but this seems to work } - /** - * Generate a url for Panoramio.com - * @param inPoint current point, not null - * @return URL - */ - private static String generatePanoramioUrl(DataPoint inPoint) - { - return "http://panoramio.com/map/#lt=" + FIVE_DP.format(inPoint.getLatitude().getDouble()) - + "&ln=" + FIVE_DP.format(inPoint.getLongitude().getDouble()) + "&z=1&k=0"; - } - /** * Get the median value from the given lat/long range diff --git a/tim/prune/function/deletebydate/DateInfo.java b/tim/prune/function/deletebydate/DateInfo.java index 0c2cedb..3e36d75 100644 --- a/tim/prune/function/deletebydate/DateInfo.java +++ b/tim/prune/function/deletebydate/DateInfo.java @@ -2,6 +2,7 @@ package tim.prune.function.deletebydate; import java.text.DateFormat; import java.util.Date; +import java.util.TimeZone; /** * Class to hold the information about a date, @@ -12,7 +13,7 @@ public class DateInfo implements Comparable { /** Date, or null for no date - used for earlier/later comparison */ private Date _date = null; - /** String representation of date, for equality comparison */ + /** String representation of date */ private String _dateString = null; /** Number of points with this date */ private int _numPoints = 0; @@ -22,6 +23,15 @@ public class DateInfo implements Comparable // Doesn't really matter what format is used here, as long as dates are different private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance(); + + /** + * @param inZone Timezone to use for the date identification + */ + public static void setTimezone(TimeZone inZone) + { + DEFAULT_DATE_FORMAT.setTimeZone(inZone); + } + /** * Constructor * @param inDate date object from timestamp @@ -47,10 +57,10 @@ public class DateInfo implements Comparable } /** - * @return date object, or null + * @return string representation of date */ - public Date getDate() { - return _date; + public String getString() { + return _dateString; } /** diff --git a/tim/prune/function/deletebydate/DateInfoList.java b/tim/prune/function/deletebydate/DateInfoList.java index 7628667..979325b 100644 --- a/tim/prune/function/deletebydate/DateInfoList.java +++ b/tim/prune/function/deletebydate/DateInfoList.java @@ -24,34 +24,34 @@ public class DateInfoList */ public void addPoint(Date inDate) { + DateInfo currentInfo = null; if (_previousInfo != null && _previousInfo.isSameDate(inDate)) { // found it - _previousInfo.incrementCount(); + currentInfo = _previousInfo; } else { - // loop through list, seeing if date already present - boolean foundDate = false; + // loop through list, to see if date already present for (DateInfo info : _infoList) { if (info.isSameDate(inDate)) { - info.incrementCount(); - _previousInfo = info; - foundDate = true; + currentInfo = info; break; } } // create new info if necessary - if (!foundDate) + if (currentInfo == null) { - _previousInfo = new DateInfo(inDate); - _previousInfo.incrementCount(); - _infoList.add(_previousInfo); + currentInfo = new DateInfo(inDate); + _infoList.add(currentInfo); _hasBeenSorted = false; } + _previousInfo = currentInfo; } + // Now we've identified the current info or created a new one + currentInfo.incrementCount(); } /** @@ -64,18 +64,6 @@ public class DateInfoList _hasBeenSorted = true; } - /** - * not used, can be removed - * @return true if any points without dates were found - */ - public boolean hasDatelessPoints() - { - if (_infoList.isEmpty()) {return false;} - sort(); - DateInfo firstInfo = _infoList.get(0); - return (firstInfo != null && firstInfo.isDateless() && firstInfo.getPointCount() > 0); - } - /** * @return number of entries in the list, including dateless points */ diff --git a/tim/prune/function/deletebydate/DeleteByDateFunction.java b/tim/prune/function/deletebydate/DeleteByDateFunction.java index 3085aa3..42d52f5 100644 --- a/tim/prune/function/deletebydate/DeleteByDateFunction.java +++ b/tim/prune/function/deletebydate/DeleteByDateFunction.java @@ -22,12 +22,13 @@ import tim.prune.App; import tim.prune.DataSubscriber; import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; +import tim.prune.config.TimezoneHelper; import tim.prune.data.DataPoint; import tim.prune.function.compress.MarkAndDeleteFunction; /** * Function to select a date or dates, - * and delete the corresponding points + * and mark the corresponding points for deletion */ public class DeleteByDateFunction extends MarkAndDeleteFunction { @@ -56,6 +57,9 @@ public class DeleteByDateFunction extends MarkAndDeleteFunction @Override public void begin() { + // Select the current timezone + DateInfo.setTimezone(TimezoneHelper.getSelectedTimezone()); + // Make a list of which dates are present in the track _infoList.clearAll(); final int numPoints = _app.getTrackInfo().getTrack().getNumPoints(); @@ -65,29 +69,14 @@ public class DeleteByDateFunction extends MarkAndDeleteFunction if (point != null) { if (point.hasTimestamp()) { - _infoList.addPoint(point.getTimestamp().getCalendar().getTime()); + _infoList.addPoint(point.getTimestamp() + .getCalendar(TimezoneHelper.getSelectedTimezone()).getTime()); } else { _infoList.addPoint(null); // no timestamp available } } } -// System.out.println("Debug: info list has dateless points? " + (_infoList.hasDatelessPoints() ? "yes":"no")); -// System.out.println("Debug: info list has " + _infoList.getNumEntries() + " different entries"); -// System.out.println("Debug: info list has " + _infoList.getTotalNumPoints() + " total points"); -// final boolean checkOk = (_infoList.getTotalNumPoints() == numPoints); -// System.out.println("Debug: which " + (checkOk?"IS":"ISN'T!") + " the same as track: " + numPoints); - - // Loop over entries for debug -// if (!checkOk) -// { -// for (int i=0; i<_infoList.getNumEntries(); i++) -// { -// DateInfo info = _infoList.getDateInfo(i); -// System.out.println(" " + i + " (" + info.getPointCount() + " points) - " + -// (info.isDateless() ? "no date" : "date")); -// } -// } // Complain if there is only one entry in the list - this means all points are on the same day if (_infoList.getNumEntries() < 2) @@ -182,7 +171,9 @@ public class DeleteByDateFunction extends MarkAndDeleteFunction DataPoint point = _app.getTrackInfo().getTrack().getPoint(p); if (point != null) { - Date date = (point.hasTimestamp() ? point.getTimestamp().getCalendar().getTime() : null); + final Date date = (point.hasTimestamp() ? + point.getTimestamp().getCalendar(TimezoneHelper.getSelectedTimezone()).getTime() + : null); boolean pointMarked = false; // Try to match each of the date info objects in the list for (int d=0; d 0) { optionallyDeleteMarkedPoints(numMarked); } - else { + else + { // Do nothing //System.out.println("Nothing selected to delete!"); // delete flags might have been reset, so refresh display UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); diff --git a/tim/prune/function/deletebydate/DeletionTableModel.java b/tim/prune/function/deletebydate/DeletionTableModel.java index 202e01e..7e5d52f 100644 --- a/tim/prune/function/deletebydate/DeletionTableModel.java +++ b/tim/prune/function/deletebydate/DeletionTableModel.java @@ -1,6 +1,5 @@ package tim.prune.function.deletebydate; -import java.text.DateFormat; import javax.swing.table.AbstractTableModel; import tim.prune.I18nManager; @@ -12,8 +11,6 @@ public class DeletionTableModel extends AbstractTableModel /** info list, one for each row of table */ private DateInfoList _infoList = null; - /** Formatter, determining how dates appear in the table */ - private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance(); /** Column heading for date */ private static final String COLUMN_HEADING_DATE = I18nManager.getText("fieldname.date"); /** Column heading for number of points */ @@ -110,7 +107,7 @@ public class DeletionTableModel extends AbstractTableModel } /** - * @return cell contents at the given row, column inded + * @return cell contents at the given row, column index */ public Object getValueAt(int inRowIndex, int inColIndex) { @@ -124,7 +121,7 @@ public class DeletionTableModel extends AbstractTableModel if (info.isDateless()) { return I18nManager.getText("dialog.deletebydate.nodate"); } - return DEFAULT_DATE_FORMAT.format(info.getDate()); + return info.getString(); case 1: // number of points return info.getPointCount(); case 2: // keep diff --git a/tim/prune/function/estimate/jama/QRDecomposition.java b/tim/prune/function/estimate/jama/QRDecomposition.java index e8aa2b7..199c67e 100644 --- a/tim/prune/function/estimate/jama/QRDecomposition.java +++ b/tim/prune/function/estimate/jama/QRDecomposition.java @@ -96,79 +96,6 @@ public class QRDecomposition return true; } - /** - * Return the Householder vectors - * @deprecated - * @return Lower trapezoidal matrix whose columns define the reflections - */ - private Matrix getH() - { - Matrix X = new Matrix(_m, _n); - double[][] H = X.getArray(); - for (int i = 0; i < _m; i++) { - for (int j = 0; j < _n; j++) { - if (i >= j) { - H[i][j] = _QR[i][j]; - } else { - H[i][j] = 0.0; - } - } - } - return X; - } - - /** - * Return the upper triangular factor - * @deprecated - * @return R - */ - private Matrix getR() - { - Matrix X = new Matrix(_n, _n); - double[][] R = X.getArray(); - for (int i = 0; i < _n; i++) { - for (int j = 0; j < _n; j++) { - if (i < j) { - R[i][j] = _QR[i][j]; - } else if (i == j) { - R[i][j] = _Rdiag[i]; - } else { - R[i][j] = 0.0; - } - } - } - return X; - } - - /** - * Generate and return the (economy-sized) orthogonal factor - * @deprecated - * @return Q - */ - private Matrix getQ() - { - Matrix X = new Matrix(_m, _n); - double[][] Q = X.getArray(); - for (int k = _n - 1; k >= 0; k--) { - for (int i = 0; i < _m; i++) { - Q[i][k] = 0.0; - } - Q[k][k] = 1.0; - for (int j = k; j < _n; j++) { - if (_QR[k][k] != 0) { - double s = 0.0; - for (int i = k; i < _m; i++) { - s += _QR[i][k] * Q[i][j]; - } - s = -s / _QR[k][k]; - for (int i = k; i < _m; i++) { - Q[i][j] += s * _QR[i][k]; - } - } - } - } - return X; - } /** * Least squares solution of A*X = B diff --git a/tim/prune/function/gpsies/GpsiesXmlHandler.java b/tim/prune/function/gpsies/GpsiesXmlHandler.java index f90bb03..c549c59 100644 --- a/tim/prune/function/gpsies/GpsiesXmlHandler.java +++ b/tim/prune/function/gpsies/GpsiesXmlHandler.java @@ -50,7 +50,7 @@ public class GpsiesXmlHandler extends DefaultHandler _track.setDescription(_value); } else if (inTagName.equals("fileId")) { - _track.setWebUrl("http://gpsies.com/map.do?fileId=" + _value); + _track.setWebUrl("https://gpsies.com/map.do?fileId=" + _value); } else if (inTagName.equals("trackLengthM")) { try { diff --git a/tim/prune/function/gpsies/TrackListModel.java b/tim/prune/function/gpsies/TrackListModel.java index 94ce503..8509213 100644 --- a/tim/prune/function/gpsies/TrackListModel.java +++ b/tim/prune/function/gpsies/TrackListModel.java @@ -12,7 +12,7 @@ import tim.prune.data.Unit; import tim.prune.function.search.SearchResult; /** - * Model for list of tracks from gpsies.com + * Model for list of tracks from a search result (eg gpsies.com, geonames, overpass) */ public class TrackListModel extends AbstractTableModel { @@ -24,6 +24,8 @@ public class TrackListModel extends AbstractTableModel private String _lengthColLabel = null; /** Number of columns */ private int _numColumns = 2; + /** Normally this model shows distances / lengths, except when this flag is true */ + private boolean _showPointTypes = false; /** Formatter for distances */ private NumberFormat _distanceFormatter = NumberFormat.getInstance(); @@ -75,6 +77,14 @@ public class TrackListModel extends AbstractTableModel return _lengthColLabel; } + /** + * @param inShowTypes true to show point types, false for distances + */ + public void setShowPointTypes(boolean inShowTypes) + { + _showPointTypes = inShowTypes; + } + /** * @param inRowNum row number * @param inColNum column number @@ -83,7 +93,13 @@ public class TrackListModel extends AbstractTableModel public Object getValueAt(int inRowNum, int inColNum) { SearchResult track = _trackList.get(inRowNum); - if (inColNum == 0) {return track.getTrackName();} + if (inColNum == 0) { + return track.getTrackName(); + } + if (_showPointTypes) + { + return track.getPointType(); + } double lengthM = track.getLength(); // convert to current distance units Unit distUnit = Config.getUnitSet().getDistanceUnit(); diff --git a/tim/prune/function/search/GenericDownloaderFunction.java b/tim/prune/function/search/GenericDownloaderFunction.java index 403a0ef..fa8d4c3 100644 --- a/tim/prune/function/search/GenericDownloaderFunction.java +++ b/tim/prune/function/search/GenericDownloaderFunction.java @@ -29,7 +29,7 @@ import tim.prune.function.gpsies.TrackListModel; /** * Function to load track information from any source, - * subclassed for special cases gpsies or wikipedia + * subclassed for special cases like gpsies, wikipedia or OSM */ public abstract class GenericDownloaderFunction extends GenericFunction implements Runnable { @@ -116,16 +116,18 @@ public abstract class GenericDownloaderFunction extends GenericFunction implemen if (!e.getValueIsAdjusting()) { final int numSelected = _trackTable.getSelectedRowCount(); + boolean foundUrl = false; if (numSelected > 0) { setDescription(_trackListModel.getTrack(_trackTable.getSelectedRow()).getDescription()); _descriptionBox.setCaretPosition(0); + foundUrl = _trackListModel.getTrack(_trackTable.getSelectedRow()).getWebUrl() != null; } else { _descriptionBox.setText(""); } _loadButton.setEnabled(numSelected > 0); - _showButton.setEnabled(numSelected == 1); + _showButton.setEnabled(numSelected == 1 && foundUrl); } } }); diff --git a/tim/prune/function/search/SearchResult.java b/tim/prune/function/search/SearchResult.java index 562f5a4..d757b01 100644 --- a/tim/prune/function/search/SearchResult.java +++ b/tim/prune/function/search/SearchResult.java @@ -1,12 +1,14 @@ package tim.prune.function.search; /** - * Class to hold a search result from wikipedia / gpsies / panoramio etc + * Class to hold a search result from wikipedia / gpsies etc */ public class SearchResult implements Comparable { /** Track name or title */ private String _trackName = null; + /** Point type (for POIs) */ + private String _pointType = null; /** Description */ private String _description = null; /** Web page for more details */ @@ -35,6 +37,22 @@ public class SearchResult implements Comparable return _trackName; } + /** + * @param inType type of point (for POIs) + */ + public void setPointType(String inType) + { + _pointType = inType; + } + + /** + * @return type of point (for POIs) + */ + public String getPointType() + { + return _pointType; + } + /** * @param inDesc description */ diff --git a/tim/prune/function/search/wikimedia_galleries.txt b/tim/prune/function/search/wikimedia_galleries.txt index 9bb446f..3647237 100644 --- a/tim/prune/function/search/wikimedia_galleries.txt +++ b/tim/prune/function/search/wikimedia_galleries.txt @@ -1,26 +1,47 @@ A Arnoia 42 14 54.95 N 8 08 04.88 W A Barciela, Oroso 42 57 57.49 N 8 26 31.07 W A Cañiza A Cañiza is a municipality in Galicia, in the province of Pontevedra. 42 12 45.46 N 8 16 29.77 W -A Coruña 43 21 58.95 N 8 24 28.71 W -A Guarda 41 54 09.12 N 8 52 27.14 W +A Coruña A Coruña is a Galician city, in north-western Spain. 43 21 58.95 N 8 24 28.71 W +A Estrada 41 32 35.1 N 2 12 59.63 E +A Guarda 41 54 09.12 N 8 52 27.15 W A Gudiña A Gudiña is a municipality in Galicia, in the province of Ourense. 42 03 40.13 N 7 08 34.19 W A Illa de Arousa A Illa de Arousa is a municipality in Galicia, in the province of Pontevedra. 42 33 08.36 N 8 52 06.13 W A Merca A Merca is a municipality in Galicia, in the province of Ourense. 42 13 22.53 N 7 54 15.55 W A Mezquita 42 00 42.5 N 7 02 43.17 W -A Pobra de Trives 42 20 19.97 N 7 15 15.88 W +A Pobra de Trives 42 20 19.96 N 7 15 15.88 W A Pobra do Brollón 42 33 29.3 N 7 23 40.15 W A Pobra do Caramiñal A Pobra do Caramiñal is a municipality in the province of A Coruña, Galicia, Spain. 42 36 12.2 N 8 56 14.25 W A Rúa A Rúa is a municipality in Galicia, in the province of Ourense. 42 23 34.93 N 7 06 59.02 W A Veiga A Veiga is a municipality in Galicia, in the province of Ourense. 42 15 01.36 N 7 01 30.25 W -A Walk on Main Street, Ferndale, California Ferndale is a small town in Eureka County, California. Its Main Street is listed by the National Register of Historic Places as a historic district. 40 34 32.16 N 124 15 54 W +A Walk on Main Street, Ferndale, California Ferndale is a small town in Humboldt County, California. Its Main Street is listed by the National Register of Historic Places as a historic district. 40 34 32.16 N 124 15 54 W A Walk up Main Street, Adamstown, Pennsylvania Adamstown is a small, historic town in Lancaster County, Pennsylvania. 40 14 36.96 N 76 03 16.2 W +Abbaye d'Acey 47 15 42 N 5 39 25 E +Abbaye de Bourgueil 47 16 46.2 N 0 10 18.12 E +Abbaye de Cluny The Abbey of Cluny (or Cluni, or Clugny) was founded on 2 September 909 and is located in the modern-day department of Saône-et-Loire in the region of Bourgogne, in east-central France, near Mâcon. 46 26 03 N 4 39 33 E +Abbaye de Fontenay 47 38 27 N 4 23 23 E +Abbaye de Fontevraud Fontevraud Abbey is located near Saumur in Anjou, France 47 10 53 N 0 03 06 E +Abbaye de La Sauve-Majeure 44 46 07.24 N 0 18 42.41 W +Abbaye de Montmajour 43 42 20 N 4 39 50 E +Abbaye de Saint-Germain-des-Prés 48 51 14 N 2 20 04 E +Abbaye de Saint-Savin-sur-Gartempe 46 33 51 N 0 51 58 E +Abbaye de Sénanque 43 55 42 N 5 11 13 E +Abbaye de Valmagne 43 29 12.97 N 3 33 44.19 E +Abbaye du Relec 48 26 56 N 3 43 00 W +Abbaye Saint Wandrille Monastery Saint Wandrille in France 49 31 46.42 N 0 45 59.58 E +Abbaye Saint-Corneille 49 25 02.6 N 2 49 28.57 E +Abbaye Saint-Florentin, Bonneval 48 10 40.41 N 1 23 04.87 E Abeilhan 43 27 02 N 3 17 43 E +Abri-caverne de l'ouvrage du Haut-Bois 47 35 24.07 N 6 48 34.23 E +Abtei Saint-André (Lavaudieu) 45 15 49 N 3 27 17 E Aceldama Aceldama or Akeldama is the Aramaic name for a place in Jerusalem associated with Judas Iscariot, one of the followers of Jesus. 31 46 05.1 N 35 13 59.51 E Adissan 43 32 09.96 N 3 25 45.12 E +Agadir 30 25 00 N 9 35 00 W Agel 43 20 18.96 N 2 51 13.68 E Agrón, Ames 42 54 20.32 N 8 41 43.21 W Aguiño, Ribeira 42 31 24.99 N 9 00 57.6 W -Alagna Valsesia Alagna Valsesia is a village in Piemonte in Italy. 45 51 05.15 N 7 56 16.98 E +Aichi prefecture 35 04 59 N 136 58 59 E +Aiguafreda Montseny 41 46 05.2 N 2 15 05.39 E +Alagna Valsesia Alagna Valsesia is a village in Piemonte in Italy. 45 51 05.16 N 7 56 16.98 E Albrechtsburg The Albrechtsburg is the castle that dominates the city centre of Meißen, Germany. 51 09 59.65 N 13 28 17.83 E Aletsch Aletsch region, UNESCO World Heritage Site since 2001, in Bernese Alps, Switzerland 46 27 50.9 N 8 04 20.89 E Allariz Allariz is a municipality in Galicia, in the province of Ourense. 42 11 20.76 N 7 48 09.71 W @@ -32,37 +53,39 @@ Anta de Casaínhos 38 52 50.16 N 9 10 09.91 W Antwerp Central Station 51 13 02 N 4 25 15.6 E Aquis Querquennis Aquis Querquennis was a Roman castra at Os Baños, Bande (Ourense, Galiza). 41 58 27.2 N 7 58 52.1 W Arbo 42 06 39.45 N 8 18 57.47 W +Arc de Triomf de Barcelona The Arc de Triomf (Triumphal Arch) is an archway structure in Barcelona, Spain. 41 23 27 N 2 10 50 E +Arc de Triomphe du Carrousel The Arc de Triomphe du Carrousel is a triumphal arch in Paris, located in the Place du Carrousel on the site of the former Tuileries Palace. 48 51 43 N 2 19 58 E Arch of Constantine 41 53 23 N 12 29 27 E Arch of Septimius Severus (Rome) The Arch of Septimius Severus is situated in the Forum Romanum in Rome. 41 53 34 N 12 29 05 E Arch of Titus The Arch of Titus is a triumphal arch with a single arched opening, located on the Summa Sacra Via to the west of the Roman Forum in Rome. 41 53 26 N 12 29 19 E +Arnhem 51 59 00 N 5 55 00 E Arenys de Munt Arenys de Munt is a village in the comarca of Maresme, Catalonia. 41 36 46 N 2 32 25 E -Artwork:La Nouvelle Liberté 4 03 51.2 N 9 42 24.97 E -Artwork:Lucas Grandin, Le jardin sonore de Bonamouti 4 04 19.3 N 9 42 37.91 E -Artwork:Pascale Marthine Tayou, La Colonne Pascale 4 01 34.12 N 9 42 22.84 E -Artwork:Tracey Rose, Oasis 4 01 28.09 N 9 42 32.75 E As Neves As Neves is a municipality in Galicia, in the province of Pontevedra. 42 05 10.49 N 8 24 53.43 W As Pontes de García Rodríguez 43 26 47.63 N 7 51 09.54 W Askersund 58 53 00 N 14 54 00 E Assumption Cathedral in Odessa Assumption Cathedral in Odessa, Ukraine 46 28 49.43 N 30 44 21.24 E -Australian Synchrotron 37 54 51 S 145 08 33 E -Avión Avión is a municipality in Galicia, in the province of Ourense. 42 22 23.72 N 8 15 02.64 W +Australian Synchrotron The Australian Synchrotron, a synchrotron facility in the suburb of Clayton, in Melbourne, Victoria, Australia. 37 54 51 S 145 08 33 E +Avión Avión is a municipality in Galicia, in the province of Ourense. 42 22 23.73 N 8 15 02.64 W Ayasofya The Church of the Holy Wisdom, commonly known as Hagia Sophia in English, is the former Greek Orthodox patriarchal cathedral, converted in 1453 to a mosque, now a museum, in Istanbul. 41 00 30.5 N 28 58 47.7 E +Aérodrome de Besançon-Thise 47 16 25.68 N 6 04 59.09 E +Aşgabat 37 57 00 N 58 23 00 E Bahnhof Dresden-Neustadt Dresden-Neustadt railway station and the four inner city bridges 51 03 56 N 13 44 27 E Baiona Baiona is a municipality in Galicia, Spain in the province of Pontevedra. 42 07 03.29 N 8 51 01.86 W Baltar Baltar is a municipality in Galicia, in the province of Ourense. 41 57 04.75 N 7 42 56.7 W -Barbadás 42 17 56.03 N 7 53 13.13 W +Barbadás 42 17 56.03 N 7 53 13.14 W +Basilique Notre-Dame de la Daurade 43 36 03 N 1 26 23 E +Basilique Notre-Dame de Thierenbach 47 52 54.12 N 7 11 21.16 E +Basilique Saint-Sauveur de Rennes 48 06 42 N 1 40 55 W Beade Beade is a municipality in Galicia, in the province of Ourense. 42 20 06.72 N 8 08 45.05 W -Beamish Museum: Entrance area The entrance to Beamish Museum is on the south side of the site. 54 52 54.04 N 1 39 30.93 W Bergfriedhof (Stuttgart) The Bergfriedhof is a cemetery in the Stadtbezirk Stuttgart-Ost in Stuttgart. 48 47 19.16 N 9 12 23.35 E -Bergondo 43 19 15.35 N 8 13 54.04 W +Bergondo 43 19 15.36 N 8 13 54.04 W Berlin Anhalter Bahnhof The Anhalter Bahnhof was a large main-line railway station in Berlin. Today it is just a stop on the Berlin S-Bahn. 52 30 11 N 13 22 55 E Betanzos 43 16 44.24 N 8 12 55.95 W +Bibliothèque Sainte-Geneviève 48 50 49.5 N 2 20 45 E +Bibracte 46 55 23 N 4 02 15 E Biella 45 34 05.07 N 8 03 02.61 E Bienertparks in Dresden Als Bienertpark werden verschiedene Parkanlagen in Dresden bezeichnet, deren Entstehung auf die Familie Gottlieb Traugott Bienert zurückgeht. 51 01 48.29 N 13 42 54.9 E -Bildstock Austraße, Burggrumbach 49 52 19.59 N 10 01 43.48 E -Bildstock Herrngasse 1, Grafenrheinfeld 50 00 11.52 N 10 11 54.6 E -Bildstock Mainbacher Weg, Holzhausen 50 07 06.2 N 10 11 07.12 E -Bildstock Schulstraße 2, Burggrumbach 49 52 13.11 N 10 02 14.06 E +Bishkek 42 52 00 N 74 34 00 E Boccadasse Boccadasse è un antico borgo marinaro situato nel levante di Genova. 44 23 23.85 N 8 58 22.71 E Boettcherstrasse 53 04 30 N 8 48 21 E Boiro 42 38 13.16 N 8 52 12.31 W @@ -72,6 +95,7 @@ Borne des Trois Puissances Dreiländerstein 47 30 10.58 N 7 07 48.76 E Borovsk Эта страница на русском: Боровск 55 12 27.19 N 36 29 05.05 E Bosco delle Querce The Regional Natural Park Bosco delle Querce (Oaks' Wood), built after the Seveso Disaster on the "A" zone. 45 38 55 N 9 09 10 E Botanischer Garten Heidelberg 49 25 00 N 8 40 10 E +Brancion 46 32 51 N 4 47 48.12 E Brudermühlbrücke The Brudermühlbrücke is a bridge in Munich across the Isar. 48 06 45.3 N 11 33 36.07 E Brudermühlstraße The Brudermühlstraße is a street in Munich, part of the Mittlerer Ring around the city centre. 48 06 44.2 N 11 33 01.37 E Buenos Aires 34 36 13 S 58 22 54 W @@ -81,16 +105,17 @@ Burg Stahleck Stahleck Castle in Bacharach is a 12th century castle occupying a Burj Khalifa The Burj Khalifa ("Khalifa Tower") is a skyscraper in Dubai, United Arab Emirates. 25 11 49.7 N 55 16 26.8 E Bürgerhospital (Stuttgart) 48 47 34.6 N 9 10 45.95 E Cabanas 43 24 50.49 N 8 10 04.15 W -Cabo Fisterra 42 52 50.96 N 9 16 18.27 W +Cabo Fisterra Cape Finisterre is a rock-bound peninsula on the west coast of Galicia, in Fisterra (Spain). 42 52 50.96 N 9 16 18.27 W Cabrils 41 31 42 N 2 22 09 E -Cadaqués Cadaqués is a touristic village in Costa Brava, Alt Empordà, Catalonia, Spain 42 17 19.46 N 3 16 40.96 E -Cal Becs (Puig-reig) 41 57 41.01 N 1 54 21.89 E +Cadaqués Cadaqués is a touristic village in Costa Brava, Alt Empordà, Catalonia, Spain. 42 17 19.46 N 3 16 40.96 E Calgary 51 02 42 N 114 03 26 W Cambados 42 30 52.2 N 8 48 24.89 W Cambre Cambre is a municipality in the province A Coruña, Galicia, Spain. 43 17 37.32 N 8 20 34.49 W Campo de' Fiori 41 53 43.94 N 12 28 20.06 E +Canal du Midi The Canal du Midi is a 240km long canal in southern France, linking the Garonne River to the Mediterranean Sea, between Toulouse and the Mediterranean port of Sète. 43 36 40 N 1 25 06 E Candelabro de Paracas The Paracas Candelabra is a well-known prehistoric geoglyph found on the northern face of the Paracas Peninsula at Pisco Bay in Peru. 13 47 39.67 S 76 18 31.31 W -Cangas 42 15 47.09 N 8 47 09.5 W +Cangas 42 15 47.1 N 8 47 09.5 W +Cape Arkona There are two bunkers at Cape Arkona. 54 40 47 N 13 25 57 E Cape Horn Cape Horn is often said to be the southernmost point of South America. 55 58 47 S 67 16 18 W Capela de São Nicolau Capela de São Nicolau ou Passos da Via Sacra. Em Portugal, Porto. 41 08 27.14 N 8 36 55.49 W Capriata d'Orba Capriata d'Orba è un comune in provincia di Alessandria, Piemonte, Italia. 44 43 43.93 N 8 41 25.29 E @@ -101,12 +126,13 @@ Cartelle Cartelle is a municipality in Galicia, in the province of Ourense. 42 1 Casa Buonarroti (Florence) Casa Buonarroti è un museo di Firenze 43 46 11.64 N 11 15 49 E Casa de las Carnicerías, León The Casa de las Carnicerías (butcher's shops), a monument declared as a Bien de Interés Cultural in 1992, is located at San Martín square, León (Spain). 42 35 48.84 N 5 34 04.4 W Casa del esquileo, Cabanillas del Monte The casa del esquileo (shearing shed) is a monument placed at Cabanillas del Monte, Torrecaballeros, Segovia (Spain). 40 58 36 N 4 01 54 W -Casa Milà Also known as 'La Pedrera', due to its look. 41 23 42.65 N 2 09 42.37 E +Casa Milà Casa Milà, also known as La Pedrera (the open quarry), is a modernist building in Barcelona, Catalonia, Spain 41 23 42.66 N 2 09 42.37 E +Cassine 44 45 03 N 8 31 44 E Castel del Monte Castel del Monte is a castle in Apulia, in Italy. 41 05 05.28 N 16 16 15.57 E Castellazzo Bormida Castellazzo Bormida is a comune (municipality) in the Province of Alessandria in the Italian region Piedmont 44 50 46 N 8 34 42 E Castello di Duino Il castello di Duino è un castello situato nel comune di Duino-Aurisina, in provincia di Trieste, Italia. 45 46 18.39 N 13 36 14.3 E Castello di Miramare Miramare is a castle near Trieste. 45 42 09 N 13 42 45 E -Castello di San Giusto The statues Micheze and Jacheze in the entrance hall of Castello di San Giusto 45 38 49.99 N 13 46 23.62 E +Castello di San Giusto The statues Micheze and Jacheze in the entrance hall of Castello di San Giusto 45 38 50 N 13 46 23.62 E Castello Sforzesco (Milan) The "Castello Sforzesco" is a castle and a museum in Milan, Italy. 45 28 11.4 N 9 10 46.2 E Castelo da Rocha Forte 42 51 42.29 N 8 34 29.38 W Castelo de Castro Marim 37 13 07.43 N 7 26 29.69 W @@ -115,37 +141,59 @@ Castelo de Monforte de Lemos 42 31 26.71 N 7 30 38.86 W Castelo de Monterreal 42 07 29.82 N 8 50 59.18 W Castelo de Pambre 42 51 34.87 N 7 56 53.7 W Castelo de Ribadavia 42 17 12.3 N 8 08 37.49 W -Castelo de San Felipe 43 27 52.83 N 8 16 52.45 W +Castelo de San Felipe Castillo de San Felipe is a castle in the county of Ferrol, Galicia 43 27 53 N 8 16 54 W Castelo de Santa Cruz 43 20 54.08 N 8 21 00.64 W Castelo de Santo Antón 43 21 56.6 N 8 23 16.12 W -Castelo de Sobroso The Sobroso Castle is a castle located in Vilasobroso- Mondariz, Provincia of Pontevedra, Galicia (Spain). 42 12 17.3 N 8 27 20.51 W +Castelo de Sobroso The Sobroso Castle is a castle located in Vilasobroso- Mondariz, Provincia of Pontevedra, Galicia (Spain). 42 12 17.31 N 8 27 20.51 W Castelo de Soutomaior 42 19 46.77 N 8 34 05.54 W Castelo de Vilalba 43 17 52.79 N 7 40 56.44 W Castelo de Vilamarín 42 27 02.87 N 7 54 02.26 W Castiñeiras, Ribeira 42 31 57.33 N 8 59 44.67 W Castrelo de Miño Castrelo de Miño is a municipality in Galicia, in the province of Ourense. 42 17 49.63 N 8 04 02.63 W -Castrelo do Val Castrelo do Val is a municipality in Galicia, in the province of Ourense. 41 59 26.72 N 7 25 25.2 W -Castro Caldelas Castro Caldelas is a municipality in Galicia, in the province of Ourense. 42 22 32.63 N 7 24 54 W -Castro de Baroña 42 41 40.91 N 9 01 54.91 W -Castro de Rei 43 12 32.15 N 7 23 58.38 W +Castrelo do Val Castrelo do Val is a municipality in Galicia, in the province of Ourense. 41 59 26.73 N 7 25 25.2 W +Castro Caldelas Castro Caldelas is a municipality in Galicia, in the province of Ourense. 42 22 32.62 N 7 24 54 W +Castro de Baroña O Castro de Baroña está situado na parroquia de Baroña no concello de Porto do Son. 42 41 40.91 N 9 01 54.91 W +Castro de Rei 43 12 32.14 N 7 23 58.38 W Catacaos 5 16 00 S 80 41 00 W -Catedral de Barcelona 41 23 02.07 N 2 10 35.19 E -Catedral de Santiago de Compostela Santiago de Compostela Cathedral is a Roman Catholic church in the city of the same name. It is the reputed burial-place of Saint James the Greater and the destination of the Way of St. James. 42 52 51.27 N 8 32 42.16 W +Catedral de Santiago de Compostela 42 52 51.27 N 8 32 42.17 W Cathedral of Justo The Cathedral of Justo is being built by Justo Gallego in Mejorada del Campo (Community of Madrid, Spain). 40 23 38.88 N 3 29 17.99 W +Cathédrale Notre-Dame d'Amiens The cathedral of Our Lady of Amiens (fr: Cathédrale Notre-Dame d'Amiens), or just Amiens Cathedral, is the tallest complete cathedral in France. 49 53 42 N 2 18 08 E +Cathédrale Notre-Dame de Chartres 48 26 50 N 1 29 16 E +Cathédrale Notre-Dame de Paris 48 51 10.8 N 2 20 59.28 E +Cathédrale Notre-Dame de Reims Cathedral "Notre-Dame de Reims", located in Reims, France 49 15 13.9 N 4 02 02.5 E Cathédrale Notre-Dame de Strasbourg 48 34 55 N 7 45 03 E +Cathédrale Notre-Dame du Havre 49 29 13 N 0 06 30 E +Cathédrale Saint-André de Bordeaux 44 50 16 N 0 34 39 W +Cathédrale Saint-Bénigne de Dijon 47 19 17 N 5 02 04 E +Cathédrale Saint-Julien du Mans Interior 48 00 33 N 0 11 56 E +Cathédrale Sainte-Cécile d'Albi 43 55 42.57 N 2 08 34.6 E Catoira Catoira is a municipality in Galicia, Spain in the province of Pontevedra. 42 39 54.98 N 8 43 57.93 W Cedeira Cedeira is a municipality in the province A Coruña, Galicia, Spain. 43 39 39.53 N 8 03 10.41 W Celanova Celanova is a municipality in Galicia, in the province of Ourense. 42 09 07.02 N 7 57 24.65 W Cenlle Cenlle is a municipality in Galicia, in the province of Ourense. 42 20 36.87 N 8 05 16.17 W Central Park 40 46 55.2 N 73 57 57.6 W Cesantes, Redondela 42 18 33.05 N 8 36 44.72 W +Champ-de-Mars (Paris) 48 51 22 N 2 17 54 E Chantada 42 36 27.15 N 7 46 09.2 W -Chapela, Redondela 42 15 53.95 N 8 40 22.72 W +Chapela, Redondela 42 15 53.95 N 8 40 22.71 W Chapelle de Languidou Chapel in Plovan / Bretagne 47 54 49.13 N 4 21 09.72 W +Chapelle Notre-Dame de Molsheim 48 32 26.16 N 7 29 40.13 E +Chapelle Notre-Dame du Schaefertal (Soultzmatt) 47 57 06.84 N 7 12 59.4 E Chapelle Notre-Dame du Verger Chapelle Notre-Dame du Verger dans l'anse du Verger à Cancale (Ille-et-Vilaine) 48 41 37 N 1 52 51 W +Chapelle Notre-Dame-du-Chêne de Plobsheim 48 27 35.28 N 7 42 49.32 E +Chapelle Notre-Dame-du-Grasweg de Huttenheim 48 21 31.32 N 7 34 33.85 E +Chapelle royale Saint-Louis, Dreux 48 44 18 N 1 21 48 E +Chapelle Saint-Gonéry 48 50 29.4 N 3 13 42.38 W +Chapelle Saint-Sébastien de Dambach-la-Ville 48 19 37.56 N 7 25 11.75 E +Chapelle Saint-Théodore de Vienne 45 31 28.56 N 4 52 24.13 E +Chapelle Saint-Ulrich d'Avolsheim 48 33 43.56 N 7 30 01.51 E +Chapelle Sainte-Marguerite d'Epfig 48 21 15.84 N 7 28 40.12 E +Chapelle Sainte-Marie de l'Assomption d'Obersteigen 48 38 32.28 N 7 18 22.32 E +Chapelles du Kochersberg 48 38 42 N 7 31 14.88 E Château d'Angers 47 28 11.67 N 0 33 33.61 W Château de Bugarach 42 52 36.16 N 2 21 05.15 E Château de Couiza 42 56 42.68 N 2 15 14.11 E +Château de Condé 49 00 20 N 3 33 34 E Chiesa di Sant'Antonio in Caggiano Church of Saint Anthony in Caggiano. 40 34 04.13 N 15 29 39.47 E Chioggia Chioggia is a town in Veneto in Italy. 45 13 04.23 N 12 16 36.76 E Chroniques de Jérusalem Jerusalem Mount of Olives Santa Marta Passionists church. 31 46 15.83 N 35 15 08.48 E @@ -153,61 +201,125 @@ Church of Adelboden The gothic village church of Adelboden was built in the 15th Church of Saints Apostles Peter and Paul in Vilnius 54 41 38.82 N 25 18 22.69 E Church of St. Johns in Vilnius Organ 54 40 57.5 N 25 17 19.22 E Church of São Vítor (Braga) Igreja de São Victor em Braga 41 33 09.95 N 8 24 47.49 W +Château d'Anet 48 51 29 N 1 26 19 E +Château d'Arlay 46 45 32.4 N 5 32 16.44 E +Château d'Azay-le-Rideau The Château de Azay-le-Rideau is one of the most famous castles in t +he French Loire Valley. 47 15 33 N 0 27 58 E +Château d'Effiat The château d'Effiat in Puy de Dôme, Auvergne, France. 46 02 37 N 3 15 06 E +Château d'If 43 16 47.5 N 5 19 30.5 E +Château d'Ussé The Château d'Ussé is a château of the Loire Valley in Rigny-Ussé. 47 14 59 N 0 17 28 E +Château de Blois The Château de Blois is one of the most renowned châteaux of the Loire Valley. 47 35 07.8 N 1 19 51.42 E +Château de Bressieux 45 19 21 N 5 16 45.01 E +Château de Brest 48 22 52.52 N 4 29 40.95 W +Château de Chambord 47 36 58.22 N 1 31 03.25 E +Château de Chamerolles 48 03 37.08 N 2 09 51.01 E +Château de Chantilly 49 11 38 N 2 29 09 E +Château de Chenonceau 47 19 30 N 1 04 14.16 E +Château de Cheverny 47 30 01 N 1 27 29 E +Château de Chinon 47 10 05 N 0 14 10 E +Château de Châteaudun 48 04 14 N 1 19 25 E +Château de Cléron 47 05 16 N 6 03 27 E +Château de Domfront 48 35 39 N 0 39 09 W +Château de Fontainebleau The Château of Fontainebleau is the largest of the French royal châteaux. 48 24 08 N 2 42 02 E +Château de Frontenay 46 47 08.88 N 5 37 05.88 E +Château de Gevrey-Chambertin 47 13 46 N 4 57 56 E +Château de l'Arthaudière The Château de l'Arthaudière is a castle in the Isère départment of the Rhône-Alpes région of France. 45 07 09 N 5 13 59 E +Château de Lagarde 43 03 02 N 1 56 08.5 E +Château de Langeais The Château de Langeais in the Loire Valley 47 19 29.28 N 0 24 21.96 E +Château de Loches The Château de Loches in Loches is a château of the Loire Valley. The castle area, consisting of three buildings, among them one the oldest keeps in France, is one of the best preserved European architecture ensembles of the Middle Ages. 47 07 37 N 0 59 54 E +Château de Maintenon 48 35 08 N 1 34 41 E Château de Malmaison Château de Malmaison was the place of residence of Joséphine de Beauharnais and Napoleon Bonaparte 48 52 15 N 2 10 01 E +Château de Montfaucon 47 14 46.32 N 6 04 42.24 E +Château de Murol 45 34 42 N 2 56 43 E +Château de Pagax 44 36 29.88 N 2 15 06.98 E Château de Puivert 42 55 16.21 N 2 03 17.82 E +Château de Saint-Germain-en-Laye The Château de Saint-Germain-en-Laye is a former royal residence in Saint-Germain-en-Laye, located ca. 19 km to the west of Paris. Nowadays it serves as Musée des Antiquités Nationales. 48 53 53 N 2 05 47 E +Château de Saint-Izaire The Château de Saint-Izaire is a castle in the Saint-Izaire commune of the Aveyron département of France 43 58 31 N 2 43 10 E +Château de Suscinio 47 30 46 N 2 43 46 W +Château de Suze-la-Rousse 44 17 14.64 N 4 50 11.62 E +Château de Thorens The Château de Thorens is a castle in the commune of Thorens-Glières in the Haute-Savoie département of France. 45 59 37 N 6 15 20 E +Château de Valençay The Château de Valençay is one of the châteaux of the Loire Valley in the french region Centre. 47 09 27 N 1 33 48 E +Château de Vaux-le-Vicomte 48 33 53.46 N 2 42 50.4 E +Château de Wangenbourg The castle of Wangenbourg is a mediaeval castle in Wangenbourg-Engental, Bas-Rhin, France. 48 37 15 N 7 18 51 E +Château des Adhémar 44 33 33.02 N 4 45 15.35 E +Château du Haut-Kœnigsbourg The castle of Haut-Kœnigsbourg is a mediaeval castle located in Orschwiller, France. 48 14 58 N 7 20 39 E +Château-Gaillard 49 14 17.26 N 1 24 09.91 E +Cimetière du Montparnasse The Montparnasse cemetery (Fr: Cimetière du Montparnasse) is a famous cemetery in the Montparnasse quarter of Paris, France. 48 50 17 N 2 19 37 E +Cimetière du Père-Lachaise Père Lachaise Cemetery (French: Cimetière du Père-Lachaise) (officially, cimetière de l'Est “eastern cemetery”) is the largest cemetery in the city of Paris at 118 acres (48 ha), though there are larger cemeteries in Paris suburbs. 48 51 43 N 2 23 39 E Cinque Terre The Cinque Terre are five coastal villages in the province of La Spezia, Italy. 44 06 37.74 N 9 44 31.35 E +Ciudad Real 38 59 00 N 3 55 00 W +Clermont-Ferrand 45 46 47 N 3 05 13 E +Clos Vougeot 47 10 29.72 N 4 57 19.74 E Collingwood Monument A monument to Admiral Collingwood (1748-1810) was erected in Tynemouth, North East England, in 1845. 55 00 52.96 N 1 25 12.14 W +Collégiale Notre-Dame de Melun 48 32 08 N 2 39 37 E Collégiale Saint-Florent de Niederhaslach 48 32 35 N 7 20 29 E Collégiale Saint-Martin de Colmar 48 04 38.5 N 7 21 29 E Collégiale Saint-Thiébaut de Thann 47 48 40 N 7 06 06 E +Collégiale Saints-Michel-et-Gangolphe de Lautenbach 47 56 27.96 N 7 09 32.94 E Colonia-Haus The Colonia-Haus is a 45-storey, 147 m skyscraper completed in 1973 in the Riehl district of Cologne, Germany. 50 57 38 N 6 58 55 E +Colonne Vendôme 48 52 02.89 N 2 19 45.89 E Comacchio Comacchio is a town in Emilia Romagna in Italy. 44 41 31.74 N 12 10 55.95 E Concatedral de Santa María de Guadalajara 40 38 04.72 N 3 09 45.15 W -Corrubedo, Ribeira 42 34 21.64 N 9 04 22.77 W +Conciergerie The Conciergerie in the Palais de Justice, Paris, France 48 51 23 N 2 20 44 E +Corrubedo, Ribeira 42 34 21.64 N 9 04 22.76 W Cortegada Cortegada is a municipality in Galicia, in the province of Ourense. 42 12 24.86 N 8 10 01.06 W Coulée verte René-Dumont The Promenade plantée is an elevated park in the 12th arrondissement of Paris, France. 48 50 32 N 2 23 15 E +Couvent de Bischenberg (Bischoffsheim) 48 28 59.88 N 7 28 46.34 E Couvent de la Divine Providence de Saint-Jean-de-Bassel 48 48 12.12 N 6 59 32.06 E +Couvent de Reinacker 48 40 51.96 N 7 24 27.5 E Crecente Crecente is a municipality in Galicia, in the province of Pontevedra. 42 09 07.02 N 8 13 22.52 W +Credit Lyonnais Head Office 48 52 14.95 N 2 20 11.45 E +Château de Crussol The Château de Crussol is a castle in the Ardèche départment of France. 44 56 18 N 4 51 09 E Cualedro 41 59 18.46 N 7 35 40.71 W Culleredo Culleredo is a municipality in the province A Coruña, Galicia, Spain. 43 17 31.02 N 8 23 08.99 W Curtis Hall Arboretum 40 04 42 N 75 07 44 W +Cáceres Cáceres is the capital of Cáceres Province, in Extremadura, Spain. 39 28 23 N 6 22 16 W Córdoba (Argentina) Intendancy square 31 24 00 S 64 11 00 W +Dakar 14 43 55 N 17 27 26 W Dalhems kyrka Die Kirche von Dalhem zählt zu den berühmtesten auf Gotland. Ihr Turm, der im 14. Jahrhundert angefügt wurde, gehört zu den höchsten der Landkirchen Gotlands. 57 33 08.7 N 18 32 03.2 E Dazaifu Tenman-gū 33 31 17.49 N 130 32 05.45 E Dent du Géant 45 51 43 N 6 57 06 E Detroit Institute of Arts The Detroit Institute of Arts is a large art museum in Detroit, Michigan in the United States. 42 21 33.5 N 83 03 53.3 W -Deutsches Museum Verkehrszentrum 48 07 57.85 N 11 32 40.69 E +Deutsches Museum Verkehrszentrum München 48 07 57.85 N 11 32 40.69 E Deyrulzaferan 37 17 57.6 N 40 47 33.9 E -Dorfkirche Zernikow 53 05 24.62 N 13 05 23.78 E +Dorfkirche Klein Haßlow Church in Klein Haßlow, Wittstock municipality, Ostprignitz-Ruppin district, Brandenburg state, Germany 53 10 29.84 N 12 31 28.6 E +Dorfkirche Priort Church in Priort, Wustermark municipality, Havelland district, Brandenburg state, Germany. 52 30 44.61 N 12 57 49.36 E Dozón 42 34 09.35 N 8 03 08.25 W Drexel University 39 57 14.68 N 75 11 12.76 W Duchesse Anne (voilier) 51 02 15.37 N 2 22 20.21 E -Duino-Aurisina English: 45 45 02.29 N 13 40 29.6 E +Duino-Aurisina 45 45 02.29 N 13 40 29.6 E Durban 29 53 00 S 31 03 00 E Dürrenstein (Südtirol) The Dürrenstein is a mountain in the Dolomites in South Tyrol. 46 40 24 N 12 11 04 E -Edinburgh Park & Ride network The Park & Ride network serving Edinburgh, Scotland. Services run by Lothian Buses call at all the sites except Ferrytoll. 55 56 30.52 N 3 00 42.77 W Effnerplatz The Effnerplatz is a square in the north of Munich, in the Borough Bogenhausen. 48 09 11.79 N 11 36 54.73 E Église Saint-Jean-Baptiste de Laure-Minervois 43 16 20.78 N 2 31 12.11 E +Église Saint-Louis-en-l'Île The Saint-Louis-en-l'Île Church (lit. "St. Louis on the Island"), is a Catholic church located on Île Saint-Louis in the IVe arrondissement of Paris 48 51 4.38 N 2 21 27.47 E Église Saint-Martin de Chambonas 44 25 02.45 N 4 07 43.97 E Eglise Saint-Pierre des Cuisines 43 36 15.49 N 1 26 08.26 E Église Saint-Vincent-de-Paul (Paris) Saint-Vincent-de-Paul is a church in Paris near the Gare du Nord 48 52 43.7 N 2 21 06.6 E -Empúries Empúries is a town in the Mediterranean coast of the Catalan comarca of Empordà. It was founded by the ancient Greeks with the name of Ἐμπόριον (Emporion — "market"). 42 08 20.29 N 3 07 11.19 E -Erica, Victoria Erica, Victoria 37 59 00 S 146 22 00 E +El Padul 37 01 27 N 3 37 36 W +El Vendrell 41 13 11.72 N 1 32 04.08 E +Empúries Empúries is a town on the Mediterranean coast of the Catalan comarca of Alt Empordà in Catalonia, Spain. 42 08 20.29 N 3 07 11.19 E +Enclos paroissial de Saint-Thégonnec 48 31 13.4 N 3 56 47.44 W +Erica, Victoria 37 59 00 S 146 22 00 E Erlöserkirche (Bad Homburg) The Church of the Redeemer is a protestant church in Bad Homburg, Germany. 50 13 35.5 N 8 36 42 E Esgos 42 19 29.71 N 7 41 45.94 W -Eurockéennes 2015 47 41 08.79 N 6 48 30.36 E -European Parliament 48 35 51.82 N 7 46 09.82 E +European Parliament The European Parliament is the parliament of the European Union. 48 35 51.82 N 7 46 09.82 E +Familistère 49 54 15 N 3 37 31 E Fangelsbachfriedhof The Fangelsbachfriedhof is one of the most important historical cemeteries in Stuttgart. 48 45 56 N 9 10 28 E Feldkommandostelle Hegewald (East Prussia) 54 08 05.54 N 21 58 36.02 E Fene Fene is a municipality in the province A Coruña, Galicia, Spain. 43 27 40.11 N 8 11 28.2 W -Ferrol 43 29 21.46 N 8 13 29.94 W +Ferrol 43 29 21.47 N 8 13 29.94 W Ferrytoll Park & Ride Ferrytoll Park & Ride is a bus/car interchange in Fife, Scotland, at the northern end of the Forth road crossing. 56 01 21.47 N 3 24 22.97 W Fieschergletscher 46 30 07.88 N 8 08 30.59 E -Fiesole (area archeologica) 43 48 29.05 N 11 17 39.19 E -Fish Creek, Victoria Fish Creek, Victoria 38 41 00 S 146 05 00 E +Fiesole (area archeologica) 43 48 29.04 N 11 17 39.19 E +Fish Creek, Victoria 38 41 00 S 146 05 00 E Flaucher Flaucher is an area in the south of Munich on the left and right hand side of the Isar (district: Sendling and Thalkirchen). 48 06 27 N 11 33 27 E +Fontaine des Neuf-Canons 43 31 36.12 N 5 26 55.25 E +Fontaine du Palmier 48 51 26.99 N 2 20 50.17 E +Fort Boyard Fort Boyard is a fort located between the île d'Aix and the île d'Oléron in the Pertuis d'Antioche straits, on the west coast of France. 45 59 58.71 N 1 12 50.16 W Fort de Bessoncourt 47 38 58.41 N 6 55 37.74 E Fort de la Miotte 47 38 53.29 N 6 52 30.33 E +Fort de Vézelois 47 36 01.67 N 6 54 29.41 E Fort des Basses Perches 47 37 34.05 N 6 52 06.81 E Fort du Bois d'Oye 47 34 27.7 N 6 50 36.55 E Fort Ross Fort Ross, a former Russian fur trade outpost, located on the coast of Northern California (United States). 38 30 51.35 N 123 14 36.88 W @@ -218,40 +330,49 @@ Fortezza del Priamar La fortezza del Priamar è un antico insediamento storico p Forêt domaniale de Sète The National Forest of Sète in the commune of Sète, Hérault, France. 43 24 18.74 N 3 40 13.74 E Fosse De Sessevalle 50 22 11.03 N 3 15 41.09 E Four solaire d'Odeillo The "Centre du Four Solaire Félix Trombe" is located in Odeillo, France. 42 29 37 N 2 01 45 E -Francelos, Ribadavia 42 16 35.51 N 8 09 34.87 W +Francelos, Ribadavia 42 16 35.51 N 8 09 34.88 W Franciscan Monastery in Katowice Panewniki Neo-Romanesque monastery of the Franciscans in Katowice Panewniki in Poland from the early XX century. 50 13 37 N 18 57 45 E Pfarrkirche St. Bartholomäus in Friesach 46 57 04.28 N 14 24 18.19 E Fubine Fubine è un comune in provincia di Alessandria, Piemonte, Italia. 44 57 55.35 N 8 25 32.75 E +Funkturm Leipzig 51 18 49.02 N 12 23 34.93 E Fuzhou Fuzhou, also known as Foochow, is a city in China. 26 04 16 N 119 18 13 E Gandino 45 48 42 N 9 54 11 E Gardens of Nymphenburg Palace Der Nymphenburger Schlosspark ist eines der größten und bedeutendsten Gartenkunstwerke Deutschlands. 48 09 29 N 11 30 13 E +Gare de Metz-Ville 49 06 35.28 N 6 10 39 E +Gare de Paris-Est Literally East Station, Paris, but usually called Gare de l'Est in Paris, France. 48 52 37 N 2 21 33 E +Gare de Paris-Nord Literally North Station, Paris, but usually called Gare du Nord in Paris, France. 48 52 58 N 2 21 24 E +Gare de Paris-Saint-Lazare Gare Paris Saint-Lazare is one of the six large terminus railway stations of Paris. 48 52 37 N 2 19 28 E Garmischer Straße The Garmischer Straße is a street in Munich, part of the Mittlerer Ring around the city centre. 48 07 28.03 N 11 31 14.51 E Garten des Himmlischen Friedens The Garten des Himmlischen Friedens (lit. Garden of Heavenly Peace) is a small walled Chinese garden in the Bethmannpark in Frankfurt-Nordend 50 07 07.57 N 8 41 26.52 E Gavi Gavi is a town in Piemonte in Italy. 44 41 19.35 N 8 48 10.64 E Gedankengang Offenbach This is part of a series of tunnels in Offenbach that have been redesigned. 50 05 58.64 N 8 45 49.92 E -Gent-Wevelgem 2014 50 48 29.25 N 3 10 49.47 E Georg-Brauchle-Ring The Georg-Brauchle-Ring is a street in Munich. 48 10 34.46 N 11 32 35.48 E -Gernikako Arbola The Gernika oak where the lords of Biscay (including several kings of Castile and Spain) came to take the oath of respect to the basques Fueros (Rules and Rights). 43 18 53.44 N 2 40 47.92 W +Gernikako Arbola The Gernika oak where the lords of Biscay (including several kings of Castile and Spain) came to take the oath of respect to the basques Fueros (Rules and Rights). 43 18 53.43 N 2 40 47.92 W Giardino dei Semplici di Firenze The Orto Botanico di Firenze (2.3 hectares), also known as the Giardino dei Semplici, is a botanical garden maintained by the University of Florence. 43 46 45 N 11 15 39 E -Giurtelecu Şimleului Giurtelecu Şimleului is a settlement in Romania. 47 18 N 22 48 E +Giurtelecu Șimleului Giurtelecu Șimleului is a settlement in Romania. 47 18 N 22 48 E +Glanum 43 46 26 N 4 49 57 E Goethedenkmal (Wien) The Goethe monument at the Opernring in Vienna by Edmund Hellmer 48 12 12.07 N 16 21 57.73 E Gogar Tram Depot 55 56 22.27 N 3 19 36.44 W Gorle 45 42 14 N 9 43 08 E Government House, Jersey 49 11 41.44 N 2 05 39.78 W -Gradara Gradara is a town in Marche, Italy. 43 56 15.75 N 12 45 59.24 E +Gradara Gradara is a town in Marche, Italy. 43 56 15.75 N 12 45 59.25 E +Grand Hôtel de Cabourg 49 17 37.32 N 0 06 59.08 W +Grand Palais The Grand Palais in Paris, France 48 51 58 N 2 18 45 E Granollers Granollers is a city near Barcelona, in Catalonia, Spain. 41 32 35.1 N 2 12 59.63 E +Gromo Gromo is a town in Lombardia in Italy. 45 58 09.91 N 9 55 25.72 E Gråmanstorps kyrka 56 08 59.33 N 13 06 00.4 E -Guaricano Barrio of Santo Domingo (Dominican Republic), Genoese mission 18 32 01.81 N 69 55 58.57 W Gut Böckel Böckel Castle in Rödinghausen, District of Herford, North Rhine-Westphalia, Germany. 52 13 29.1 N 8 31 02.98 E Gymnasium Koblenzer Straße The Gymnasium Koblenzer Straße, also known as Kobi, is a German grammar school in Urdenbach, an urban borough of Düsseldorf. 51 08 58.67 N 6 53 08.95 E Göltzschtalbrücke 50 37 21.29 N 12 14 37.46 E Hamburg Hamburg is a City-State in the North of Germany and one of the biggest seaports in Europe. 53 34 07 N 10 02 19 E Hamburger Rathaus 53 33 01 N 9 59 32 E +Hameau de la Reine 48 49 07 N 2 06 46 E +Hampigny 48 27 21 N 4 35 24 E Hansestaden Visby The Hanseatic town Visby was founded in the 10th century, on the then independent Baltic Sea island of Gotland. 57 38 20 N 18 17 40 E +Château d'Harcourt The Château d'Harcourt is a castle located in the commune of Harcourt in the Eure département of France 49 10 26 N 0 47 11 E Haus Werburg Haus Werburg is a small water castle in Spenge, Kreis Herford. 52 08 30.78 N 8 28 33.66 E Hazmburk Hazmburk is a hill with castle in České středohoří in Czech Republic. 50 26 03 N 14 00 52 E Heckenstallerstraße The Heckenstallerstraße is a street in Munich, part of the Mittlerer Ring around the city centre. 48 06 35.77 N 11 31 42 E -Heiligenhäuschen Neun Morgen, Burggrumbach 49 52 42.52 N 10 01 26.96 E Hellig Kors Kirke Hellig Kors Kirke is a church on Nørrebro in Copenhagen, Denmark. 55 41 15.72 N 12 33 05.04 E Henninger-Turm The 120-m-high Henninger Turm is located in Frankfurt-Sachsenhausen in Germany. 50 05 50.16 N 8 41 36.77 E Herford Herford is a city in North Rhine-Westphalia, Germany. 52 06 57.81 N 8 40 12.11 E @@ -260,61 +381,80 @@ Holy Trinity Cathedral in Odessa 46 28 34.36 N 30 44 18.43 E Hoppenlaufriedhof The Hoppenlaufriedhof is a cemetery in Stuttgart. 48 46 54 N 9 10 05 E Horyuji Hōryū-ji (法隆寺, "Temple of the Flourishing Law") is a Buddhist temple in Ikaruga, Nara Prefecture, Japan. 34 36 53.06 N 135 44 03.02 E Hospitalkirche (Stuttgart) 48 46 40 N 9 10 22 E -Hôtel du Gouverneur, Belfort 47 38 16.16 N 6 51 49.16 E +Humprecht Humprecht is a castle on top of a hill near Sobotka in the Hradec Králové region in the Czech Republic. 50 28 13 N 15 10 11 E +Hôtel Biron 48 51 19 N 2 18 57 E +Hôtel d'Ulmo 43 35 51 N 1 26 59.28 E +Hôtel de Bourgtheroulde 49 26 31.2 N 1 05 17.77 E +Hôtel de Sens 48 51 12 N 2 21 33 E +Hôtel de Soubise 48 51 38 N 2 21 30 E +Hôtel Lutetia 48 51 04 N 2 19 39 E +Hôtel Négresco 43 41 40 N 7 15 27 E Igreja de São Martinho de Aldoar 41 10 14.58 N 8 40 13.83 W Igrexa de San Pedro de Vilanova de Dozón The romanic parochial Church of San Pedro of Dozón 42 35 06.77 N 8 01 27.02 W Igrexa de Santa María de Cambre 43 17 32.03 N 8 20 34.4 W Igrexa de Santo Antolín de Toques 42 58 41.36 N 7 58 59.23 W Illa de Cortegada 42 37 05.87 N 8 47 02.7 W Iloilo City The City of Iloilo is the capital city of the Provinces of the Philippines of Iloilo. 10 41 24 N 122 33 00 E -Im Klosterhof Nr 4, Kennelbach Vbg, Aufbahrungshalle 47 28 49.67 N 9 45 59.27 E -Info:Kusterdingen Kusterdingen is a municipality situated between Tübingen and Reutlingen in Baden-Württemberg in Germany. 48 31 20.28 N 9 07 15.24 E -Info:Mössingen Mössingen is a municipality circa 15km south of Tübingen in Baden-Württemberg in Germany. 48 24 23 N 9 03 27 E +Innerer Plauenscher Friedhof Cemetery „Innerer Plauenscher Friedhof“ near church „Auferstehungskirche“ in Dresden-Plauen 51 01 42.38 N 13 42 15.91 E Institut de théologie orthodoxe Saint-Serge 48 52 59.98 N 2 23 01.23 E Isarring The Isarring is a street in Munich, part of the Mittlerer Ring around the city centre. 48 09 36.65 N 11 36 03.89 E -Isla San Carlos Peninsula of San Carlos, commonly known as San Carlos Island, part of Venezuela, is located north of the island of Toas, between the Gulf of Venezuela and Lake Maracaibo. 10 59 42.73 N 71 36 43.16 W +Isfahan اصفهان 32 39 05 N 51 40 45 E +Isla San Carlos Isla San Carlos or peninsula of San Carlos, is part of Venezuela and is located north of the island of Toas. 10 59 42.73 N 71 36 43.16 W Ivrea Ivrea is a town in Piemonte in Italy. 45 27 50.4 N 7 52 42.96 E Jagdschloss Wermsdorf 51 16 59.52 N 12 56 21.85 E -Jesuitenkirche (Wien) The Jesuit church is a prominent church in Vienna, Austria. 48 12 32.95 N 16 22 39.48 E +Jardin de l'État The Jardin de l'État is a botanical garden on Réunion island. 20 53 12 S 55 27 04 E +Jardin des Tuileries 48 51 50 N 2 19 34 E +Jaroměř Pond 50 21 22.45 N 15 55 17.21 E +Jesuitenkirche (Wien) The Jesuitenkirche (Jesuit church) is a prominent church in Vienna, Austria. 48 12 32.95 N 16 22 39.48 E Jin Mao Tower Jin Mao Building 31 14 14 N 121 30 05 E Johannesburg Johannesburg is the largest city in South Africa. 26 12 16 S 28 02 44 E Jubiläumssäule 48 46 42.85 N 9 10 47.53 E Jumkils kyrka Jumkils kyrka tillhör Bälingebygdens församling, Upplands västra kontrakt, Uppsala stift / Diocese of Uppsala. 59 56 33.2 N 17 25 23.7 E +Jüdischer Friedhof Haunetal 50 45 12.6 N 9 41 00.6 E Kaaba 21 25 21.11 N 39 49 34.41 E Kagawa prefecture Kagawa Prefecture (香川県, Kagawa-ken?) is a prefecture of Japan located on Shikoku island. The capital is Takamatsu. 34 20 24.4 N 134 02 35.8 E Kansai International Airport Kansai International Airport, is an international airport located on an artificial island in the middle of Osaka Bay, off the shore of Sennan district of Osaka, Japan. 34 26 03 N 135 13 58 E Karlsfried Karlsfried Castle is situated nearby the town of Zittau in Sachsen, Germany. 50 50 05.05 N 14 47 32.29 E Katharinenhospital Stuttgart 48 48 01.87 N 9 12 21.6 E -Kennesaw State Soccer Stadium New KSU Stadium is a new stadium near Kennesaw, Georgia that is currently being constructed. 34 01 46.43 N 84 34 03.63 W -Kiel Kiel is the Capital city and most populous city of the northern German state Schleswig-Holstein. 54 19 31 N 10 08 26 E -Kleinmarkthalle Frankfurt 50 06 45 N 8 41 01 E +Kathmandu 27 43 00 N 85 22 00 E +Kiel Kiel is the capital city and most populous city of the northern German state Schleswig-Holstein. 54 19 31 N 10 08 26 E +Kleinmarkthalle Frankfurt Vegetable stall 50 06 45 N 8 41 01 E +Klimkówka - stary dwór 49 35 18.78 N 21 49 50.14 E Kloster Ettal The monastery of Ettal is a Benedictine monastery in Bavaria/Germany near Oberammergau. 47 34 09.33 N 11 05 40.42 E Kotohira Gu Kotohira Gu in Kotohira, Kagawa prefecture, Japan. 34 11 02.2 N 133 48 34 E Kreis Herford 52 06 57.54 N 8 39 40.42 E Kreis Minden-Lübbecke Der Kreis Minden-Lübbecke ist ein Landkreis im Osten des Landes Nordrhein-Westfalen mit Sitz in Minden. 52 16 53.48 N 8 54 41.71 E -Kreuzschlepper Am Weiher 1, Holzhausen 50 07 06.2 N 10 11 07.12 E +Kreuz im Heldenhain, Geschwister-Scholl-Weg, Ruhland 51 27 30.61 N 13 52 01.48 E +Kriegerdenkmal in Arnsdorf (Ruhland) 51 25 48.02 N 13 51 05.5 E Kronprinzenpalais (Stuttgart) 48 46 41.56 N 9 10 39.88 E Krumbach (Schwaben) Krumbach (Schwaben) ist eine Stadt im Landkreis Günzburg, Regierungsbezirk Schwaben, Bayern. 48 14 35 N 10 21 48 E Krummesse Krummesse is a village in Schleswig-Holstein, Germany, which partly belongs to Kreis Herzogtum Lauenburg and partly to Lübeck. 53 46 45 N 10 38 30 E -Kuala Lumpur 3 08 08.5 N 101 41 16.8 E +Kuala Lumpur 3 09 35 N 101 42 00 E Kunsthalle Bremen 53 04 22 N 8 48 49 E Kuressaare 58 09 00 N 22 16 48 E Kusterdingen 48 31 20.28 N 9 07 15.24 E Lac Blanc (Orbey) Dans le massif des Vosges, près d'Orbey. 48 07 31.32 N 7 05 34.95 E +Lac Chambon 45 34 13 N 2 55 18 E Lac de Serre-Ponçon Le Lac de Serre-Ponçon, vallé de l'Ubaye, Hautes-Alpes, France 44 30 33.26 N 6 22 10.8 E +Lac des Dix 46 03 25.74 N 7 23 51.17 E Lalín Lalín is a municipality in Galicia, Spain in the province of Pontevedra. 42 39 36.8 N 8 06 43.31 W -Langes Tannen The domain Langes Tannen now belongs to the town of Uetersen. 53 41 32.67 N 9 40 28.6 E -Large Hadron Collider 46 16 17 N 6 03 48.5 E +Langes Tannen 53 41 32.67 N 9 40 28.6 E +Large Hadron Collider CERN collider near Geneva, Switzerland 46 16 17 N 6 03 48.5 E Larouco Larouco is a municipality in Galicia, in the province of Ourense. 42 20 48.74 N 7 09 37.22 W -Laza Laza is a municipality in Galicia, in the province of Ourense. 42 03 36.69 N 7 27 37.14 W -Le Crozet (Loire) Le Crozet est une commune française, située dans le département de la Loire et la région Rhône-Alpes. 46 10 11 N 3 51 25 E +Lavoir de Gex 46 20 02.04 N 6 03 30.02 E +Laza Laza is a municipality in Galicia, in the province of Ourense. 42 03 36.68 N 7 27 37.14 W +Le Louxor 48 52 00 N 2 20 59 E Le Sauze-du-Lac Le Sauze-du-Lac est un petit village de les Hautes-Alpes, prés du lac de Serre-Ponçon. 44 28 42.94 N 6 18 52.35 E +Le Train Bleu 48 50 42 N 2 22 24 E +Legnano Legnano is a town in the north-west of Lombardy, situated on the flat lands of the Po Valley between Milan and Lake Maggiore. 45 35 48.84 N 8 54 32 E Leiro Leiro is a municipality in Galicia, in the province of Ourense. 42 22 11.51 N 8 07 27.42 W +Les Invalides 48 51 18 N 2 18 45 E Levanto Levanto is a village in Liguria in Italy. 44 10 15.82 N 9 36 41.68 E +Lighthouses at Cape Arkona There are two lighthouses and one bearing tower at Cape Arkona. 54 40 47 N 13 25 57 E Ligne Aubagne - Fuveau 43 23 41.38 N 5 33 59.44 E Lima 12 05 36 S 77 02 48 W Loro Parque Puerto de la Cruz, Tenerife, Canarias, España 28 24 30.18 N 16 33 51.25 W -Lothian Buses: Central garage Central garage in Annandale Street, Broughton, is one of Lothian Buses' depot sites. 55 57 40.43 N 3 11 16.73 W +Loschwitzer Friedhof Cemetery "Loschwitzer Friedhof" in Dresden-Loschwitz 51 02 46 N 13 49 18.98 E Luise-Kiesselbach-Platz The Luise-Kiesselbach-Platz is a square in the southwest of Munich. 48 06 44.17 N 11 31 03.27 E Lunds domkyrka Lunds domkyrka or Lund Cathedral is the cathedral of Lund in Skåne in southern Sweden. 55 42 14.59 N 13 11 36.91 E LWL-Freilichtmuseum Detmold The LWL-Freilichtmuseum Detmold (earlier name: Westfälisches Freilichtmuseum Detmold) is a museum for folklife studies in the town of Detmold, Germany. 51 55 25 N 8 52 12 E @@ -322,13 +462,16 @@ Madone 45 39 00 N 9 32 53 E Magasin-caverne du Salbert 47 39 20.34 N 6 49 22.79 E Magazzini del Cotone di Genova I Magazzini del Cotone sono una delle principali strutture del porto antico di Genova. 44 24 29.75 N 8 55 16.68 E Mahabodhi Temple The Mahabodhi Temple is a Buddhist temple in Bodh Gaya, the location where Siddhartha Gautama, the Buddha, attained enlightenment. 24 41 46 N 84 59 29 E +Maison de François Coignet 48 55 51.24 N 2 20 30.44 E +Maison de la Mutualité 48 50 55.5 N 2 21 02 E Maisons de la rue Jeanne-Mance 45 30 31.9 N 73 34 11.02 W Manneken Pis van Brussel Manneken Pis in Brussels 50 50 42 N 4 21 00 E -Manzaneda Manzaneda is a municipality in Galicia, in the province of Ourense. 42 18 35.34 N 7 13 58.01 W -Mary's Tomb 'egg' (ornament hung on oil lamp chains) with winged seraphim. Armenian artwork 31 46 48.5 N 35 14 21.41 E +Manufacture nationale de Sèvres 48 49 43 N 2 13 21 E +Manzaneda Manzaneda is a municipality in Galicia, in the province of Ourense. 42 18 35.33 N 7 13 58.01 W Marín 42 23 31.28 N 8 42 16.58 W Maside Maside is a municipality in Galicia, in the province of Ourense. 42 24 44.75 N 8 01 31.85 W Matitone (Genova) Il Matitone è un grattacielo di Genova dalla struttura a forma di lapis. È situato nella zona portuale di San Benigno, a breve distanza dalla torre della Lanterna. 44 24 40.59 N 8 54 25.07 E +Matteus kyrka, Stockholm 59 20 43.08 N 18 02 32.94 E Maximiliansbrücke in München 48 08 12.42 N 11 35 31.45 E Meaño Meaño is a municipality in Galicia, Spain in the province of Pontevedra. 42 26 31.95 N 8 46 46.02 W Meis Meis is a municipality in Galicia, Spain in the province of Pontevedra. 42 30 49.01 N 8 41 27.14 W @@ -338,27 +481,27 @@ Millennium Town Park The Millennium Town Park is a public park in Saint Helier, Minden Minden is a German city in North Rhine-Westphalia. 52 17 20.18 N 8 55 04.19 E Mindener Dom Cathedral in Minden, District of Minden-Lübbecke, North Rhine-Westphalia, Germany. 52 17 19.85 N 8 55 09.94 E Mindener Kreisbahnen Kreisbahnen Minden, ein Unternehmen aus dem 19. Jahrhundert, das in Abwandlungen noch heute besteht. 52 18 01.14 N 8 54 50.98 E -Miquel Marti i Pol.jpg Monument al Llibre, Saltamartí 41 23 21.34 N 2 10 05.05 E Mittagskogel Mittagskogel is a peak in the Karawanken mountain chain in Carinthia / Austria / EU. 46 30 26.54 N 13 57 08.1 E Moaña Moaña is a municipality in Galicia, Spain in the province of Pontevedra. 42 17 05.52 N 8 44 57.56 W -Moe, Victoria Moe, Victoria 38 10 20 S 146 16 04 E +Moe, Victoria 38 10 20 S 146 16 04 E Molino Stucky Il Molino Stucky è uno storico edificio di Venezia. È un esempio di architettura industriale neogotica. 45 25 41.55 N 12 19 11.95 E -Mollet del Vallès Mollet del Vallès is a village in the comarca of Vallès Oriental (Catalonia, Spain). 41 32 35.1 N 2 12 59.63 E Monastery of San Paio de Diomondi 42 36 13.6 N 7 42 34.1 W -Monforte de Lemos 42 31 19.98 N 7 30 46.55 W +Monforte de Lemos 42 31 19.98 N 7 30 46.56 W Monte Amiata Il Monte Amiata è un monte situato nella Toscana. 42 54 00 N 11 38 00 E Monte Musinè el Monte Musinè es una cima de los Alpes Grayos, en Italia. 45 06 50 N 7 27 16 E Monterrei Monterrei is a municipality in Galicia, in the province of Ourense. 41 56 51.19 N 7 26 58.52 W Montevideo 34 52 01 S 56 10 00 W -Montsec d'Ares El Montsec d'Ares és el sector central de la Serra del Montsec, entre els congostos deTerradets a l'est i el de Mont-rebei a l'oest 42 02 27.96 N 0 47 33.68 E +Montmartre Cemetery Montmartre Cemetery (Fr: Cimetière de Montmartre) is a famous cemetery located at 37 Avenue Samson, in the 18th arrondissement of Paris, France. 48 53 16 N 2 19 49 E Monument de Joseph Sec Monument Joseph Sec, 8 avenue Pasteur, Aix-en-Provence, France. 43 31 59.44 N 5 26 46.71 E Monument international de la Réformation 46 12 00.78 N 6 08 45.19 E -Mos Mos is a municipality in Galicia, Spain in the province of Pontevedra. 42 11 39.08 N 8 39 11.18 W +Mos Mos is a municipality in Galicia, Spain in the province of Pontevedra. 42 11 39.08 N 8 39 11.19 W Mosteiro de San Clodio de Leiro 42 22 02.61 N 8 06 54.12 W Mosteiro de San Salvador de Celanova 42 09 06.55 N 7 57 24.85 W Mosteiro de Santa María de Aciveiro 42 37 03 N 8 18 06 W Mońki Railway station 53 24 00 N 22 47 00 E Mugardos Mugardos is a municipality in the province A Coruña, Galicia, Spain. 43 27 43.7 N 8 15 12.52 W +Muntic Muntic is a village in Istria, Croatia. 44 55 00 N 13 56 00 E +Murol 45 34 23 N 2 56 35 E Muros 42 46 28.69 N 9 03 23.75 W Murray House 22 13 05.15 N 114 12 34.96 E Murviel-lès-Béziers 43 26 29 N 3 08 42 E @@ -366,13 +509,16 @@ Museo Civico d'Arte Antica di Torino Il Museo Civico d'Arte Antica è un polo mu Museo della Storia del Genoa 44 24 37.8 N 8 56 11.39 E Museo Regionale della Fauna Alpina - Alpenfaunamuseum "Beck-Peccoz" Un museo sulla fauna alpina situato a Gressoney-Saint-Jean, in Valle d'Aosta, Italia. 45 46 35.57 N 7 49 36.84 E Museum für Moderne Kunst The Museum für Moderne Kunst (MMK) is an art museum in Frankfurt am Main. 50 06 42.56 N 8 41 03.86 E +Mérida (Spain) 38 54 57 N 6 20 00 W Möja kyrka 59 24 18.8 N 18 52 53 E Mössingen 48 24 23 N 9 03 27 E -Nagaoka Tenman-gū 日本語: 長岡天満宮, 京都府長岡京市。 34 55 22.2 N 135 41 10.8 E +Mühlen am Löbauer Wasser 51 12 02.21 N 14 39 21.09 E Narón Narón is a municipality in the province A Coruña, Galicia, Spain. 43 32 14.91 N 8 10 53.28 W -Neda Neda is a municipality in the province A Coruña, Galicia, Spain. 43 29 58.67 N 8 09 21.51 W +Necrópole Megalítica da Lameira de Cima Necrópole Megalítica da Lameira de Cima, freguesia de Antas, Penedono, Portugal. 40 56 09.92 N 7 20 57.52 W +Neda Neda is a municipality in the province A Coruña, Galicia, Spain. 43 29 58.68 N 8 09 21.51 W +Nedroma 35 00 47.01 N 1 44 51.12 W Neues Rathaus München 48 08 16.07 N 11 34 33.35 E -Neues Schloss, Stuttgart The south front parallels the Planie and the Alte Schloss. 48 46 41 N 9 10 55 E +Neues Schloss, Stuttgart 48 46 41 N 9 10 55 E New Mosque in Istanbul New Mosque (Yeni Cami) in Istanbul 41 01 01.05 N 28 58 19.2 E Niujie Mosque The Niujie Mosque (Chinese: 牛街清真寺; pinyin: niújiē qīngzhēnsì) is the oldest mosque in Beijing, China. It was built in 996 and completely rebuilt under the Kangxi Emperor (1622-1722). 39 53 04 N 116 21 29 E Nizza-Ufer The Nizza bank is a park with mild microclimate at the Main river bank in Frankfurt am Main. The Park was constructed in 1860. 50 06 15.68 N 8 40 14.37 E @@ -380,12 +526,12 @@ Noorderplantsoen The Noorderplantsoen is a park in the Dutch city of Groningen. Nullamunjie Olive Grove, Victoria Nullamunjie olive groves are situated in the mountains of eastern Victoria, Australia on the slopes of Mount Stawell and near the banks of the Tambo River. 37 11 05 S 147 43 58 E O Barco de Valdeorras 42 24 38.82 N 6 58 33.51 W O Bolo O Bolo is a municipality in Galicia, in the province of Ourense. 42 18 27.11 N 7 05 52.28 W -O Carballiño 42 25 53.18 N 8 04 38.48 W +O Carballiño 42 25 53.18 N 8 04 38.47 W O Grove 42 29 34.29 N 8 52 04.9 W O Pereiro de Aguiar O Pereiro de Aguiar is a municipality in Galicia, in the province of Ourense. 42 20 47.83 N 7 48 06.62 W O Porriño O Porriño is a municipality in Galicia, Spain in the province of Pontevedra. 42 09 41.84 N 8 37 15 W -O Rosal O Rosal is a municipality in Galicia, Spain in the province of Pontevedra. 41 56 07.29 N 8 50 05.63 W -O Ézaro, Dumbría 42 54 38.2 N 9 07 54.11 W +O Rosal O Rosal is a municipality in Galicia, Spain in the province of Pontevedra. 41 56 07.3 N 8 50 05.63 W +O Ézaro, Dumbría 42 54 38.2 N 9 07 54.12 W Obermillstatt 46 48 36.91 N 13 35 29.41 E Obervellach 46 55 55.76 N 13 12 06.68 E Ocean Park, Hong Kong 22 14 45.1 N 114 10 33.3 E @@ -394,62 +540,96 @@ Oia Oia is a municipality in Galicia, Spain in the province of Pontevedra. 42 00 Oleiros Oleiros is a municipality in the province A Coruña, Galicia, Spain. 43 19 57.16 N 8 18 54.38 W Oleiros, Ribeira 42 36 11.29 N 9 00 20.52 W Olgastraße (Stuttgart) 48 46 04.17 N 9 10 44.23 E +Oradour-sur-Glane Oradour-sur-Glane was a village in the Limousin region of Vichy France that came under direct German control in 1942. 45 55 55 N 1 01 54 E +Oran 35 41 49 N 0 37 59 W Orangerie (Neustrelitz) Die Orangerie in Neustrelitz wurde bereits 1755 erbaut und erfuhr 1840 bis 1842 einen grundlegenden Umbau durch Friedrich Wilhelm Buttel. 53 21 40 N 13 03 27 E Organización Médica Colegial de España Spanish Medical Colleges Organization 40 24 57.24 N 3 41 49.49 W Ortigueira Ortigueira is a municipality in the province A Coruña, Galicia, Spain. 43 41 12.94 N 7 51 10.47 W -Os Blancos Os Blancos is a municipality in Galicia, in the province of Ourense. 41 59 50.61 N 7 45 12.35 W +Os Blancos Os Blancos is a municipality in Galicia, in the province of Ourense. 41 59 50.61 N 7 45 12.34 W Ouaisné 49 10 45.75 N 2 11 10.77 W Oza-Cesuras Oza-Cesuras, municipality in the province of A Coruña, in Galicia (Spain). 43 16 44.24 N 8 12 55.95 W -Ozurgeti Ozurgeti is a town and the regional administrative centre of Western Georgian province of Guria, former Macharadze or Makharadze (named in honor of Filipp Makaradze). 41 55 26.26 N 42 00 33.84 E +Ozurgeti Ozurgeti is a town and the regional administrative centre of Western Georgian province of Guria, former Macharadze or Makharadze. 41 55 26.26 N 42 00 33.84 E Oímbra 41 53 07.94 N 7 28 19.78 W -Palacete de Belomonte Palacete de Belomonte / Palácio de Belomonte / Casa dos Pacheco Pereira. Em Portugal, Porto, Porto, São Nicolau. 41 08 33.18 N 8 36 58.35 W +Palace and park of Versailles 48 48 15.85 N 2 07 23.38 E +Palais Brongniart The Palais Brongniart was the site of the Paris Stock Exchange until 1987. 48 52 09.01 N 2 20 26.98 E +Palais de justice de Paris Palais de Justice is a building complex on the Île-de-la-Cité in Paris. It was built on the site of the Palais de la Cité, the residence of the Kings of France until the 14th century. 48 51 20.6 N 2 20 42.18 E +Palais de l'Élysée 48 52 13 N 2 18 59 E +Palais des Papes The Palais des Papes in Avignon, France was the residency of popes during the 14th and 15th century. 43 57 02 N 4 48 25 E 43.95068 N 4.80704 E 43.95068 4.80704 +Palais du parlement de Bretagne 48 06 46.08 N 1 40 40.01 W Palais du parlement du Dauphiné 45 11 35.97 N 5 43 42.86 E +Palais du Tau 49 15 11 N 4 02 04 E +Palais ducal de Nevers The Palais Ducal in Nevers, France 46 59 18 N 3 09 30 E Palais Garnier The Palais Garnier, also known as Opéra Garnier or Opéra national de Paris is an opera house situated at the northern end of the avenue de l'Opera, in Paris. 48 52 19 N 2 19 54 E +Palais Granvelle (Besançon) 47 14 08.52 N 6 01 35.76 E +Palais Idéal 45 15 22.85 N 5 01 42.74 E +Palais Longchamp 43 18 15.48 N 5 23 40.2 E +Palais Royal 48 51 52.5 N 2 20 15.1 E Palmenhaus, Vienna Vienna's Palmenhaus ('palm house', a Jugendstil greenhouse built in 1901) is a building in Vienna's 1st district, beneath 'Burggarten' and Hofburg. 48 12 18 N 16 22 01 E Palácio de Estói 37 05 47.79 N 7 53 44.05 W Palácio Nacional da Pena 38 47 15.24 N 9 23 25.75 W Palácio Nacional de Mafra O Palácio Nacional de Mafra é um palácio e mosteiro monumental de estilo neoclássico localizado em Mafra (Portugal) a poucos mais de 50 quilómetros de Lisboa. 38 56 12 N 9 19 35 W Pamukkale Pamukkale is a natural site and attraction and a UNESCO World Heritage Site in south-western Turkey. 37 55 25 N 29 07 24 E +Panthéon de Paris 48 50 46 N 2 20 45 E +Parc Astérix 48 27 21 N 4 35 24 E Parc du Thabor 48 06 51 N 1 40 12 W +Parc Monceau 48 52 45.84 N 2 18 33.23 E +Parc Natural del Delta de l'Ebre Village 40 42 09 N 0 48 32 E +Paris-Gare de Lyon 48 50 41 N 2 22 25 E +Emin Pasha Emin Pasha (born Eduard Schnitzer, 1840-1892) was a German explorer of Central Africa. 42 12 40.68 N 20 44 28.26 E +Petit Palais The Petit Palais is a palace in Paris, France built for the Universal Exhibition in 1900. It now houses the Musée des Beaux-Arts de la Ville de Paris (Paris Museum of Fine Arts). 48 51 57.72 N 2 18 52.39 E Petuelpark 48 10 39.64 N 11 34 45.32 E Petuelring The Petuelring is a street in Munich, part of the Mittlerer Ring around the city centre. 48 10 40.25 N 11 34 19 E Petueltunnel The Petueltunnel is a tunnel that runs beneath the Petuelring between the Boroughs Milbertshofen-Am Hart and Schwabing-West in Munich. 48 10 39.9 N 11 34 37.48 E +Phare de Chassiron 46 02 48.12 N 1 24 37.01 W Philadelphia City Hall Philadelphia City Hall is the seat of government for the city of Philadelphia, Pennsylvania. 39 57 08.28 N 75 09 48.96 W Piazza Corvetto (Genova) Piazza Corvetto è una delle principali piazze di Genova 44 24 36.1 N 8 56 18.2 E Piazza della Vittoria (Genoa) Piazza della Vittoria è una delle principali piazze di Genova. 44 24 11.12 N 8 56 42.45 E -Piazza Navona Piazza Navona is a square in Parione, the VI. Rione (district) from Rome / Italy / EU. 41 53 56.21 N 12 28 23.15 E +Piazza delle Erbe (Verona) 45 26 36.74 N 10 59 48.53 E +Piazza Navona Piazza Navona is a square in Parione, the VI. Rione (district) from Rome, Italy. 41 53 56.21 N 12 28 23.15 E Piazza Venezia Piazza Venezia is Located in Rome. 41 53 47 N 12 28 57 E Pieve di San Giorgio di Valpolicella The Pieve di San Giorgio di Valpolicella is a particularly large and elaborate Pieve (ancient church) of the city of Sant'Ambrogio di Valpolicella, Italy. 45 32 07 N 10 51 00 E Piode Piode è un comune della Valsesia, in provincia di Vercelli, Piemonte, Italia. 45 46 09.98 N 8 02 57.28 E -Piñor Piñor is a municipality in Galicia, in the province of Ourense. 42 29 50.7 N 8 00 21.4 W -Place royale du Peyrou 43 36 40 N 3 52 15 E +Piñor Piñor is a municipality in Galicia, in the province of Ourense. 42 29 50.69 N 8 00 21.39 W +Place de la Concorde The Place de la Concorde is one of the major public squares in Paris, France. 48 51 56 N 2 19 16 E +Place des Vosges Place des Vosges in Paris, France 48 51 20 N 2 21 56 E Plaridel Airport 14 53 27.91 N 120 51 09.9 E Plaça Catalunya Plaça Catalunya (or Plaza de Cataluña in spanish) is a large square in central Barcelona. 41 23 13.21 N 2 10 12.09 E -Ponte de Rande 42 17 18.66 N 8 39 37.45 W +Pont Ambroix The Pont Ambroix was a Roman bridge across the Vidourle in Gallargues-le-Montueux, Gard department, and Villetelle, Hérault department, both Languedoc-Roussillon, France. 43 43 02 N 4 09 07 E +Pont Camille-de-Hogues 46 48 49.12 N 0 32 15.32 E +Pont de la Concorde 48 51 47.99 N 2 19 09.98 E +Pont du Gard The Pont du Gard is a Roman aqueduct bridge near Nîmes, France. 43 56 50.28 N 4 32 07.8 E +Pont Flavien The Pont Flavien is a Roman bridge in Saint-Chamas, Bouches-du-Rhône department, Provence-Alpes-Côte d'Azur, France. 43 32 29 N 5 02 35 E +Pont Julien The Pont Julien is a Roman bridge near Bonnieux, Vaucluse department, Provence-Alpes-Côte d’Azur, France. 43 51 45 N 5 18 28 E +Pont Neuf 48 51 26.81 N 2 20 29.82 E +Pont sur la Laye Roadway 43 55 48 N 5 45 23 E +Ponte de Rande The Rande Bridge is a cable-stayed bridge near Vigo, Galicia, Spain. 42 17 18.66 N 8 39 37.45 W Ponte Sant'Angelo (Rome) The Ponte Sant'Angelo is a Roman bridge across the Tiber in Rome, Italy. 41 54 06.46 N 12 27 59.24 E Pontedeume 43 24 13.68 N 8 09 46.85 W -Pontevedra 42 25 50.84 N 8 38 56.28 W +Pontevedra 42 25 50.84 N 8 38 56.27 W Pordoi Pass The Pordoi Pass is an Alpine pass in the Dolomites. 46 29 19.33 N 11 48 52.54 E Port of Kobe Port of Kobe in Kobe, Hyōgo Prefecture, Japan 34 40 39.17 N 135 13 36.97 E Porta Pia 41 54 33 N 12 30 04 E Porta san Sebastiano 41 52 25 N 12 30 07 E Porta Soprana (Genova) Porta Soprana a Genova. 44 24 19.76 N 8 56 05.6 E Porta Westfalica 52 14 22.66 N 8 55 14.31 E -Porto antico di Genova 44 24 34.22 N 8 55 04.27 E +Porto antico di Genova 44 24 34.22 N 8 55 04.26 E Porto Azzurro 42 46 05.02 N 10 23 48.33 E Portor, Negreira 42 54 45.44 N 8 41 52.17 W Potemkin Stairs 46 29 18.75 N 30 44 31.18 E Praia de Cabanas 43 24 55.21 N 8 10 23.62 W Prinzenbau Stuttgart 48 46 38.55 N 9 10 40.96 E +Promenade du Peyrou 43 36 40 N 3 52 15 E +Prytanée national militaire 47 42 10.09 N 0 04 35.77 W +Puerto de Navacerrada 40 47 19.04 N 4 00 13.23 W Puits Arthur-de-Buyer 47 40 37.23 N 6 36 51.29 E Punta Manara Manara Bivouac 44 15 19.08 N 9 24 20.88 E -Punxín Punxín is a municipality in Galicia, in the province of Ourense. 42 22 06.15 N 8 00 03.17 W +Punxín Punxín is a municipality in Galicia, in the province of Ourense. 42 22 06.15 N 8 00 03.16 W Quartier de Beaugrenelle 48 51 03.87 N 2 17 07.61 E -Rairiz de Veiga 42 04 57.65 N 7 49 56.93 W +Rairiz de Veiga 42 04 57.65 N 7 49 56.92 W Ravensberger Bahn Die Ravensberger Bahn ist eine Eisenbahnstrecke von Bielefeld nach Rahden. 52 13 36.48 N 8 31 20.75 E -Redondela Redondela is a municipality in Galicia, Spain in the province of Pontevedra. 42 17 03 N 8 36 31.13 W Regattastrecke Oberschleißheim Die Regattastrecke Oberschleißheim ist ein künstlicher, rechteckiger Grundwassersee im Norden von München, angelegt für die Olympischen Sommerspiele 1972. 48 14 33.67 N 11 30 54.38 E Reggia di Caserta La Reggia di Caserta, Palazzo Reale, è stata la dimora della dinastia dei Borboni, sovrani del Regno delle due Sicilie. È situata a Caserta, in Campania (Italia). 41 04 26.27 N 14 19 36.92 E +Reservoir in Stara Morawa Reservoir in Stara Morawa near Stronie Śląskie (Lower Silesian Voivodeship, Poland) 50 16 31.15 N 16 52 47.84 E Rianxo Rianxo is a port town in Galicia, Spain, in the province of A Coruña. 42 38 38.62 N 8 48 44.67 W Ribadavia 42 17 13.06 N 8 08 34.54 W Ribadumia Ribadumia is a municipality in Galicia, Spain in the province of Pontevedra. 42 30 50.38 N 8 45 25.68 W @@ -460,91 +640,105 @@ Rio Marina Rio Marina is a village in Isola d'Elba Toscana in Italy 42 48 44.2 N Risiera di San Sabba La Risiera di San Sabba è stato un campo di concentramento nazista, attivo negli ultimi anni della seconda guerra mondiale a Trieste, Italia. 45 37 26.09 N 13 47 22.1 E Riós Riós is a municipality in Galicia, in the province of Ourense. 41 58 29.53 N 7 16 55.68 W Robben Island Robben Island, Cape Town, South Africa 33 48 24.24 S 18 21 58.4 E -Rocca Grimalda Rocca Grimalda è un comune dell'Alto Monferrato, in provincia di Alessandria, Piemonte, Italia. 44 40 17.48 N 8 38 55.12 E -Rodeiro Rodeiro is a municipality in Galicia, Spain in the province of Pontevedra. 42 39 00.44 N 7 56 59.93 W +Rocca Grimalda Rocca Grimalda è un comune dell'Alto Monferrato, in provincia di Alessandria, Piemonte, Italia. 44 40 17.48 N 8 38 55.11 E +Rodeiro Rodeiro is a municipality in Galicia, Spain in the province of Pontevedra. 42 39 00.44 N 7 56 59.94 W +Roman Bridge (Saint-Thibéry) Old mill nearby 43 23 34.39 N 3 25 58.21 E +Roman Bridge (Vaison-la-Romaine) The Roman Bridge at Vaison-la-Romaine is a bridge across the Ouvèze in the Vaucluse department, Provence, France. 44 14 20.3 N 5 04 28.7 E Roman Theatre of Catania 37 30 10.4 N 15 05 00.9 E Roujan 43 30 32.5 N 3 17 15.15 E Río Tambre 42 54 20.32 N 8 41 43.21 W Sacrario Militare di Redipuglia 45 51 05.58 N 13 29 22.49 E Sada Sada is a municipality in the province A Coruña, Galicia, Spain. 43 21 01.43 N 8 15 16.23 W -Safranbolu Safranbolu is a city and district of Karabük Province, Turkey. Safranbolu is one of the World Heritage sites in Turkey. 41 14 41 N 32 41 37 E +Safranbolu Safranbolu is a city and World Heritage site of Karabük Province, Turkey. 41 14 41 N 32 41 37 E Sagrada Família 41 24 12.82 N 2 10 27.64 E +Église Saint-Gervais-Saint-Protais 48 51 19.8 N 2 21 16.6 E Saint-Michel de Nahuze Prieuré du XIe siècle, situé sur la commune de Lagrasse (département de l'Aude), dont les ruines ont été inscrites comme monument historique 43 07 56.82 N 2 37 06.38 E Saint-Nazaire-de-Ladarez 43 30 37 N 3 04 36 E +Sainte-Chapelle 48 51 20 N 2 20 41 E +Château de Saissac The Château de Saissac is a Cathar castle in the Saissac commune, Aude département of France. 43 21 25 N 2 10 04.7 E Salt Lake Temple The Salt Lake Temple, the sixth temple built by the church overall, and the fourth operating temple, is the largest and best-known temple of The Church of Jesus Christ of Latter-day Saints. 40 46 14.45 N 111 53 31.18 W -San Amaro San Amaro is a municipality in Galicia, in the province of Ourense. 42 22 20.07 N 8 04 23.64 W +San Amaro San Amaro is a municipality in Galicia, in the province of Ourense. 42 22 20.07 N 8 04 23.65 W San Clodio, Leiro 42 22 02.61 N 8 06 54.12 W San Cristovo de Cea San Cristovo de Cea is a municipality in Galicia, in the province of Ourense. 42 28 33.22 N 7 59 07.24 W San Giorgio in Lemine San Giorgio in Lemine is a church in the comune of Almenno San Salvatore, Bergamo, Lombardy, Italy 45 44 46 N 9 35 50 E +San Marco (Milan) 45 28 23.77 N 9 11 20 E San Sadurniño San Sadurniño is a municipality of Spain in the province of A Coruña, in the autonomous community of Galicia. 43 33 44.93 N 8 03 17.21 W Sandiás Sandiás is a municipality in Galicia, in the province of Ourense. 42 06 38.53 N 7 45 28.41 W Sankt Martini 53 04 30 N 8 48 15 E Sant'Anastasia Sant'Anastasia è una chiesa di Roma. 41 53 17.6 N 12 29 03 E +Santa Maria degli Scalzi (Venice) 45 26 29.4 N 12 19 19.56 E +Santa Maria dei Servi (Padua) 45 24 16.56 N 11 52 32.63 E Santiago de Chile Santiago is the capital of Chile, it is also the country's industrial and commercial center. 33 27 00 S 70 40 00 W -Santiago de Compostela 42 52 50.24 N 8 32 39.96 W Sanxenxo 42 24 19.2 N 8 48 23.66 W Sarreaus 42 05 13.93 N 7 36 10.99 W Sarria 42 46 38.13 N 7 24 54.31 W Schallenberg 46 49 34 N 7 47 50 E Schenkendorfstraße The Schenkendorfstraße is a street in Munich, part of the Mittlerer Ring around the city centre. 48 10 34.21 N 11 35 29.04 E -Schloss Eutin The Castle of Eutin is located in Eutin in Schleswig-Holstein. It was built onto the foundations of a medieval castle. 54 08 15.75 N 10 37 13.22 E +Schloss Eutin The Castle of Eutin is located in Eutin in Schleswig-Holstein. 54 08 15.75 N 10 37 13.22 E Schloss Hüffe Schloss Hüffe ist ein Ende des 13. Jahrhunderts errichtetes Wasserschloss in der Ortschaft Lashorst der Stadt Preußisch Oldendorf im Kreis Minden-Lübbecke. 52 20 05.99 N 8 30 55.72 E Schloss Kalkum Schloss Kalkum is a water castle in Kalkum in the north of Düsseldorf, about two kilometers northeast of Kaiserwerth. 51 18 14.9 N 6 45 26.7 E -Schloss Nymphenburg Nymphenburg Palace is a château in Munich. Its construction was started during the 17th century by the Prince-elector Ferdinand Maria of Bavaria. 48 09 29 N 11 30 13 E +Schloss Nymphenburg Nymphenburg Palace is a château in Munich. 48 09 29 N 11 30 13 E Schloss Petershagen Das Schloss Petershagen in Petershagen ist ein Wasserschloss im Stil der Weserrenaissance. 52 22 46.16 N 8 58 18.62 E Schloss Rosenstein 48 48 15 N 9 11 30 E Schlosskirche (Neustrelitz) Die Neustrelitzer Schlosskirche wurde durch Friedrich Wilhelm Buttel erbaut und ist dessen Hauptwerk in der Stadt. 53 21 35 N 13 03 32 E Schlossplatz Stuttgart 48 46 42.81 N 9 10 47.45 E +Serres d'Auteuil The Jardin des serres d'Auteuil is a botanical garden in the Bois de Boulogne, Paris 16th, located at 3 avenue de la Porte d'Auteuil and 1 avenue Gordon-Bennett. 48 50 49 N 2 15 8 E Sestri Levante Sestri Levante is a town in Liguria in Italy. 44 16 17.76 N 9 23 33 E Shokeda Shokeda is a religious settlement in Israel, in north-west part of Negev, south to Sderot and east to the Gaza Strip. 31 25 19.56 N 34 31 28.2 E -Sigüeiro, A Barciela, Oroso 42 58 06.42 N 8 26 33.38 W +Sidi Okba 34 45 00 N 5 54 00 E +Sigüeiro, A Barciela, Oroso 42 58 06.42 N 8 26 33.39 W Silleda 42 41 49.46 N 8 14 50.28 W Singapore Singapore is a city state at the southern tip of peninsular Malaysia. It is an island approximately 40 by 20 km in size inhabited by more than five million people. 1 18 00 N 103 48 00 E Singapore Zoo 1 24 15.9 N 103 47 28.1 E Sistiana Sistiana is a town near Trieste. 45 46 09.98 N 13 38 01.98 E Sistine Chapel The Sistine Chapel is located in the Vatican and is decorated with frescoes by Michelangelo. 41 54 11 N 12 27 16 E Sohland am Rotstein Sohland am Rotstein is a municipality in Saxony, Germany. 51 07 00 N 14 47 00 E -Sopron Sopron (pronounced "shop-ron"; German: Ödenburg) is a city in Hungary near the Austrian border. It is located a short train ride from Vienna. 47 41 12 N 16 34 49 E +Sopron Sopron (pronounced "shop-ron"; German: Ödenburg) is a city in Hungary near the Austrian border. 47 41 12 N 16 34 49 E Spenge Spenge is a northrhine-westphalian town in the administrative district Kreis Herford. 52 08 33.29 N 8 28 59.9 E Spišský hrad The ruins of Spiš Castle are situated above the town of Spišské Podhradie and the village of Žehra in the Spiš region in eastern Slovakia. 48 59 58.5 N 20 46 03.3 E St. Jürgenskirche (Lilienthal) The church of St. Jürgen is in the same named district of Lilienthal in the county of Osterholz in Lower Saxony, Germany. 53 10 36 N 8 48 29 E St. Maria in der Kupfergasse St. Maria in der Kupfergasse is a baroque church in Cologne. 50 56 23.2 N 6 57 01.04 E Stadtplatz (Steyr) 48 02 20.42 N 14 25 08.89 E Stalis 35 17 47.4 N 25 25 25.9 E +Stefanskyrkan, Stockholm 59 20 51.38 N 18 03 16.7 E Steigfriedhof The Steigfriedhof is a cemetery in the Stadtbezirk Bad Cannstatt in Stuttgart. 48 48 41.9 N 9 12 28.95 E Stift Göttweig Göttweig Abbey is a Benedictine monastery in Lower Austria. 48 22 00.2 N 15 36 45.5 E +Stockholms stadshus 59 19 38.57 N 18 03 15.67 E +Stockholms universitet Stockholm University, founded 1878, with about 37,000 students. 59 21 46.68 N 18 03 31.4 E Städtisches Lapidarium Stuttgart 48 46 03.36 N 9 10 04.58 E -Sultanahmet Camii The Sultan Ahmed Mosque (in Turkish Sultanahmet Camii, in English commonly called the Blue Mosque) is a mosque in Istanbul. It is regarded as one of the greatest masterpieces of Islamic architecture. 41 00 19.3 N 28 58 36.6 E +Sultanahmet Camii The Sultan Ahmed Mosque (in Turkish Sultanahmet Camii, in English commonly called the Blue Mosque) is a mosque in Istanbul. 41 00 19.3 N 28 58 36.6 E Synchrotron Soleil 48 42 33 N 2 08 41 E Süleymaniye camii The Mosque of Suleiman I in Istanbul. 41 00 58.3 N 28 57 50 E Tarancón Tarancón village and municipality in the province of Cuenca, part of the autonomous community of Castile-La Mancha, Spain. 40 00 45.89 N 3 00 14.85 W Teixido, Cedeira 43 42 36.72 N 7 59 00.44 W -Villa romana de Tejada The ancient roman villa of La Tejeda is an archaeological site from II to V centuries in Quintanilla de la Cueza, Cervatos de la Cueza (Palencia, Spain). 40 58 36 N 4 48 25 W -Temple Neuf (Strasbourg) 48 35 00 N 7 44 54 E +Villa romana de Tejada The ancient roman villa of La Tejada is an archaeological site from II to V centuries in Quintanilla de la Cueza, Cervatos de la Cueza (Palencia, Spain). 40 58 36 N 4 48 25 W Templer Cemetery, Jerusalem Jerusalem, German Colony, Emek Refaim street 39 31 45 47.46 N 35 13 08.68 E Texas State Capitol The Texas State Capitol, located in Downtown Austin, Texas, is the fourth building to serve as the seat of Texas government. 30 16 29 N 97 44 26 W The Dormition Cathedral in Odessa 46 28 31.07 N 30 43 55.42 E The Spice Bazaar, Istanbul 41 01 00.7 N 28 58 15.13 E +Théâtre municipal de Besançon 47 14 03.84 N 6 01 33.89 E Tiberias 32 47 20.04 N 35 31 20.28 E -Tomiño Tomiño is a municipality in Galicia, Spain in the province of Pontevedra. 41 59 31.78 N 8 44 32.53 W +Toblinger Knoten The Toblinger Knoten is a mountain in the Sexten Dolomites in South Tyrol. 46 38 31 N 12 18 29 E +Tomiño Tomiño is a municipality in Galicia, Spain in the province of Pontevedra. 41 59 31.78 N 8 44 32.54 W Toosa Toosa is a village in Punjab. 30 44 45.31 N 75 41 14.53 E Topkapı Sarayı İznik tiles: camp of the Mount Arafat 41 00 45.7 N 28 59 03.25 E Torre de San Sadurniño 42 30 25 N 8 49 16 W Torre de Vilanova dos Infantes 42 09 58.36 N 7 57 16.62 W Torres de Altamira 42 52 39.02 N 8 41 15.17 W -Torres do Oeste 42 40 35.81 N 8 43 32.45 W +Torres de Oeste 42 40 35.81 N 8 43 32.46 W Torreón dos Andrade 43 24 27.98 N 8 10 17.88 W -Transfiguration Cathedral in Odessa 46 28 59.44 N 30 43 51.75 E +Toshkent 41 18 00 N 69 16 00 E +Tour Eiffel 48 51 30 N 2 17 39 E +Tour Goguin 46 59 03.84 N 3 09 19.91 E +Transfiguration Cathedral in Odessa Odessa Cathedral of God's Transfiguration 46 28 59.44 N 30 43 51.75 E Trappentreustraße The Trappentreustraße is a street in Munich, part of the Mittlerer Ring around the city centre. 48 08 12.34 N 11 32 03.84 E -Trappentreutunnel The Trappentreutunnel is a tunnel that runs beneath the Trappentreustraße in the borough Schwanthalerhöhe in Munich. 48 08 08.78 N 11 32 03.17 E +Trappentreutunnel The Trappentreutunnel is a tunnel that runs beneath the Trappentreustraße in the Borough Schwanthalerhöhe in Munich. 48 08 08.78 N 11 32 03.17 E Trastevere Restaurant in Via della Lungaretta 41 53 22.54 N 12 28 12.76 E Travemünde Travemünde is a part of Lübeck in Germany at the Baltic Sea. 53 58 00 N 10 52 00 E -Treuners Altstadtmodell The Treuner brothers' Altstadtmodell is a scale model which shows the oldtown of Frankfurt am Main prior to the air-raid damages in 1943 and 1944. The ruin model shows the destroyed old town of Frankfurt in spring 1945. 50 06 35 N 8 40 57 E -Triumphal arch Barcelona 41 23 27 N 2 10 50 E +Trinitatisfriedhof, Dresden Cemetery „Trinitatisfriedhof“ in Dresden-Johannstadt 51 03 15.08 N 13 46 20.93 E Twierdza Osowiec Dry moat around the fort No 3 53 27 44 N 22 37 38 E Třebechovice pod Orebem Třebechovice pod Orebem is a town in Czech Republic in Hradec Králové Region 50 12 07 N 15 59 38 E Uetersen 53 41 14 N 9 40 09 E -Universidad de Alcalá 40 28 58.53 N 3 21 47.27 W Université Lille Nord de France 50 36 33.38 N 3 08 29.72 E Upper Darby, Pennsylvania 39 57 40.2 N 75 15 32.06 W Urgnano 45 35 50 N 9 41 42 E @@ -554,10 +748,10 @@ Valparaíso 33 02 45.4 S 71 36 58.9 W Varallo Varallo is a town in Valsesia, Piedmont, Italy, named also Varallo Sesia. 45 49 10.95 N 8 15 19.01 E Varigotti Varigotti è una frazione del comune di Finale Ligure, in provincia di Savona (riviera ligure di ponente, Liguria, Italia). 44 10 53.83 N 8 23 45.56 E Verducido, Pontevedra 42 28 35.96 N 8 37 06.66 W -Verín Verín is a municipality in Galicia, in the province of Ourense. 41 56 22.46 N 7 26 19.59 W -Via dell'Amore (Cinque Terre) La Via dell'Amore è una passeggiata turistica di circa un chilometro che, costeggiando il mare, congiunge le località di Riomaggiore e Manarola, nelle Cinque Terre, in Liguria (Italia). 44 06 06.07 N 9 44 00.37 E -Via Dolorosa signs 31 46 46.48 N 35 13 56.63 E -Viana do Bolo 42 10 46.42 N 7 06 44.8 W +Verín Verín is a municipality in Galicia, in the province of Ourense. 41 56 22.47 N 7 26 19.59 W +Via dell'Amore (Cinque Terre) The Via dell'Amore (=way of love) is a hiking path above the sea linking the villages of Riomaggiore and Manarola in the Cinque Terre (Liguria, Italia). 44 06 06.07 N 9 44 00.37 E +Viana do Bolo 42 10 46.42 N 7 06 44.81 W +Vieilles prisons d'Annecy Ancient Jails or the Palais de l'Île of Annecy. 45 53 54.89 N 6 07 36.59 E Vigo Vigo is a Spanish municipality and the largest city in Galicia. 42 14 00.05 N 8 42 37.59 W Vilagarcía de Arousa 42 35 33.08 N 8 46 36.13 W Vilamarín Vilamarín is a municipality in Galicia, in the province of Ourense. 42 27 51.29 N 7 53 23.64 W @@ -565,58 +759,168 @@ Vilanova de Arousa Vilanova de Arousa is a municipality in Galicia, Spain in the Vilar de Santos Vilar de Santos is a municipality in Galicia, in the province of Ourense. 42 05 10.72 N 7 47 44.37 W Vilardevós 41 54 25.21 N 7 18 45.37 W Vilariño de Conso 42 09 56.5 N 7 11 02.5 W -Vilasobroso, Mondariz 42 12 17.3 N 8 27 20.51 W +Vilasobroso, Mondariz 42 12 17.31 N 8 27 20.51 W Vilassar de Mar Vilassar de Mar is a village in the county of the Maresme, Catalonia. 41 30 19 N 2 23 34 E Villa Berg 48 47 31.34 N 9 12 27.17 E Villa d'Este (Tivoli) The Villa d'Este in Tivoli is a masterpiece of Italian architecture and garden design. 41 57 45 N 12 47 46 E +Villa Ephrussi de Rothschild 43 41 48 N 7 19 42.5 E +Villa Les Glycines 48 40 46 N 6 10 56 E +Villa Majorelle 48 41 8 N 6 9 50 E Villa Schneider Villa Schneider è un palazzo storico di Biella (Piemonte, Italia), servito come quartier generale delle SS durante la seconda guerra mondiale. 45 33 49.52 N 8 03 04.87 E Village Vanguard The Village Vanguard is a jazzclub in Greenwich Village (New York City). 40 44 09.64 N 74 00 05.81 W Villeneuve-d'Ascq Villeneuve-d'Ascq is a commune within Lille Metropolis, in northern France 50 37 24 N 3 08 42 E -Wakayama prefecture Wakayama Prefecture is located in the Kinki Region of the island of Honshu, Japan. 34 13 33.7 N 135 10 03 E Walhalla, Victoria 37 56 37.43 S 146 27 02.75 E Walt Disney Concert Hall 34 03 19.36 N 118 14 59.61 W Wayside Cross Villenstraße, Bonn-Dottendorf 50 42 15.73 N 7 06 43.18 E Weckschnapp 50 56 57.48 N 6 57 55.5 E West Lake 30 15 00 N 120 09 00 E Whitemarsh Hall 40 04 42 N 75 07 44 W -Wild fire area at lake Norra Hörken Norra Hörken is a lake situated between Ludvika and Ljusnarsberg municipalities in central Sweden. This gallery page shows some ecological aspects of a small wildfire in a rather typical Scandinavian forest. 60 02 30 N 14 54 37 E -Wilhelmsstift Tübingen 48 31 16.19 N 9 03 18.52 E +Wilhelmsstift Tübingen 48 31 16.19 N 9 03 18.53 E Woodlawn Cemetery (Bronx) Gate on Jerome Avenue 40 53 30 N 73 52 12 W Xinzo de Limia Xinzo de Limia is a municipality in Galicia, in the province of Ourense. 42 03 36 N 7 43 19.87 W -Yad Vashem The memorial of the murdered jews of Suwałki and the neighbour communities, Yad Vashem, Jerusalem 31 46 23.55 N 35 10 29.2 E +Xàbia Xàbia is a village in the province of Alicante. 38 47 21 N 0 09 47 E +Yad Vashem 31 46 23.55 N 35 10 29.2 E Yao Yao (八尾市, Yao-shi) is a city in Osaka, Japan. 34 37 36.9 N 135 36 03 E -Zwangsarbeitslager Rees-Groin und Bienen 51 46 44.76 N 6 24 25.92 E -Zwehrenturm The Zwehrenturm from 1330 is a remain of Kassel's medieval defensive wall. Temporary it contained an observatory. 51 18 49 N 9 29 54 E +Zwehrenturm The Zwehrenturm from 1330 is a remain of Kassel's medieval defensive wall. 51 18 49 N 9 29 54 E +Äußerer Plauenscher Friedhof Cemetery „Äußerer Plauenscher Friedhof“ in Dresden-Plauen 51 01 11.89 N 13 42 26.03 E École centrale de Lille École Centrale de Lille is a graduate engineering school located in campus Lille I within Université Lille Nord de France. 50 36 21.62 N 3 08 13.63 E +Église de l'Assomption simultanée (La Petite-Pierre) 48 51 25.2 N 7 18 55.84 E +Église de la Sainte-Trinité (Lauterbourg) 48 58 30.1 N 8 10 40.77 E +Église de Saint-Lothain 46 49 27.84 N 5 38 30.12 E Église de Saint-Paul de Frontignan 43 26 50.47 N 3 45 18.66 E +Église des Augustins (Ribeauvillé) 48 11 43.44 N 7 19 08.87 E Église des Jésuites (Molsheim) 48 32 25.3 N 7 29 45 E +Église des Saints-Innocents (Blienschwiller) 48 20 25.8 N 7 25 06.17 E Église Notre-Dame (Guebwiller) 47 54 20.75 N 7 12 52.56 E +Église Notre-Dame de la Dalbade 43 35 51.36 N 1 26 32.89 E +Église Notre-Dame-de-l'Assomption (Bergheim) 48 12 18 N 7 21 53.28 E +Église Notre-Dame-de-l'Assomption (Bernardswiller) 48 27 10.08 N 7 27 49.57 E +Église Notre-Dame-de-l'Assomption (Monswiller) 48 45 17.61 N 7 22 39.66 E +Église Notre-Dame-de-l'Assomption (Rosenwiller) 48 30 21.96 N 7 26 25.48 E +Église Notre-Dame-de-l'Assomption (Rouffach) 47 57 24 N 7 18 02 E +Église Notre-Dame-de-la-Nativité (Saverne) 48 44 28.32 N 7 21 50.36 E +Église protestante (Balbronn) 48 35 05.28 N 7 26 15.97 E +Église protestante (Baldenheim) 48 14 15 N 7 32 14.35 E +Église protestante (Berg) 48 53 52.08 N 7 09 24.01 E +Église protestante (Bischheim) 48 36 55.08 N 7 45 21.89 E +Église protestante (Fouday) 48 25 17.76 N 7 11 12.95 E +Église protestante (Harskirchen) 48 56 02.04 N 7 02 20.15 E +Église protestante (Scharrachbergheim) 48 35 35.52 N 7 29 55.9 E +Église protestante (Schiltigheim) 48 36 21.96 N 7 45 05.04 E +Église protestante (Weiterswiller) 48 51 10.44 N 7 24 50.9 E +Église protestante du Temple Neuf (Strasbourg) 48 35 00 N 7 44 54 E +Église Saint-Eustache de Paris 48 51 48 N 2 20 42 E +Église Saint-Laurent (Paris) 48 52 29.45 N 2 21 29.92 E Église Saint-Michel d'Ernolsheim-lès-Saverne Bells 48 47 27.85 N 7 22 47.57 E +Église Saint-Nicolas-du-Chardonnet St. Nicolas du Chardonnet is a church in the centre of Paris, France located in the 5th arrondissement. 48 50 57 N 2 21 01 E +Église Saint-Pierre de Montmartre Saint-Pierre de Montmartre is a church in Paris 48 53 12 N 2 20 31 E Église Saint-Pierre-de-Rhèdes 43 35 16.22 N 3 04 43.64 E Église Saint-Pierre-Saint-Paul de Rueil-Malmaison 48 52 35.4 N 2 10 53.15 E +Église Saint-Sulpice 48 51 04 N 2 20 05 E +Église Saint-Étienne-du-Mont Saint-Étienne-du-Mont church is located in Paris, nearby Panthéon. 48 50 47 N 2 20 52 E +Église Sainte-Marie-Madeleine de Rennes-le-Château 42 55 41.05 N 2 15 45.69 E Église Saints-Pierre-et-Paul (Andlau) 48 23 16.3 N 7 24 54.3 E +Église Saints-Pierre-et-Paul (Eguisheim) 48 02 32.28 N 7 18 21.2 E +Église Saints-Pierre-et-Paul (Hohatzenheim) 48 42 44.64 N 7 36 59.11 E Église Saints-Pierre-et-Paul (Neuwiller-lès-Saverne) 48 49 25 N 7 24 20 E +Église Saints-Pierre-et-Paul (Obernai) 48 27 47.88 N 7 28 54.48 E +Église Saints-Pierre-et-Paul (Ottmarsheim) 47 47 13.2 N 7 30 25.2 E +Église Saints-Pierre-et-Paul (Rosheim) 48 29 48 N 7 28 14 E +Église Saints-Pierre-et-Paul (Sigolsheim) 48 08 04.2 N 7 18 03.1 E Église Saints-Pierre-et-Paul (Wissembourg) 49 02 14 N 7 56 30 E +Église St Antoine de Padoue (Saverne) The cloister 48 44 29.4 N 7 21 40.82 E +Église St Arbogast (Offenheim) 48 37 53.76 N 7 36 59.54 E +Église St Barthélemy (Sarrewerden) 48 55 22.8 N 7 04 56.93 E +Église St Benoît (Bergholtzzell) 47 55 51.34 N 7 13 54.48 E +Église St Blaise (Valff) 48 25 12.36 N 7 31 06.96 E Église St Cyriaque (Altorf) 48 31 22.7 N 7 31 50 E +Église St Gall (Niedermorschwihr) 48 05 57.84 N 7 16 26.47 E +Église St Gall protestante (Domfessel) 48 57 06.48 N 7 09 07.56 E +Église St Georges (Châtenois) 48 16 09.12 N 7 23 51 E +Église St Georges (Sélestat) 48 15 36 N 7 27 24.12 E +Église St Grégoire (Ribeauvillé) 48 11 49.2 N 7 19 00.41 E +Église St Guillaume protestante (Strasbourg) 48 34 55.5 N 7 45 28 E +Église St Hippolyte (Saint-Hippolyte) 48 14 01.68 N 7 22 04.01 E +Église St Jacques-le-Majeur (Kuttolsheim) 48 38 37.32 N 7 31 41.34 E +Église St Jacques-le-Majeur simultanée (Dettwiller) 48 45 12.6 N 7 27 56.77 E +Église St Jacques-le-Majeur simultanée (Hunawihr) 48 10 42.24 N 7 18 38.02 E +Église St Jean (Strasbourg) 48 35 04 N 7 44 25 E +Église St Jean protestante (Wissembourg) 49 02 18.96 N 7 56 33.36 E Église St Jean-Baptiste (Saint-Jean-Saverne) 48 46 18.7 N 7 21 48.5 E +Église St Jean-Baptiste (Surbourg) 48 54 34.2 N 7 50 50.28 E +Église St Jean-Baptiste (Wattwiller) 47 50 07.72 N 7 10 37.27 E +Église St Jean-Baptiste simultanée (Hohwiller) 48 45 12.78 N 7 27 56.77 E +Église St Laurent (Dieffenbach-au-Val) 48 18 44.64 N 7 19 41.34 E +Église St Laurent protestante (Dorlisheim) 48 31 30 N 7 29 13.99 E Église St Léger (Guebwiller) 47 54 42.1 N 7 12 33.75 E +Église St Léger (Murbach) 47 55 24 N 7 09 29 E +Église St Martin (Ammerschwihr) 48 07 37.92 N 7 16 54.01 E +Église St Martin (Ebersheim) 48 18 14.04 N 7 30 14.08 E +Église St Martin (Pfaffenheim) 47 59 05.28 N 7 17 08.59 E +Église St Martin protestante (Barr) 48 24 33.84 N 7 26 51.47 E +Église St Martin protestante (Westhoffen) 48 36 01.8 N 7 26 30.3 E Église St Maurice (Ebersmunster) 48 18 39.5 N 7 31 37 E +Église St Maurice (Fegersheim) 48 29 23.64 N 7 40 50.27 E +Église St Maurice (Orschwiller) 48 14 27.24 N 7 22 44.62 E +Église St Maurice (Soultz-Haut-Rhin) 47 53 13.2 N 7 13 48.29 E +Église St Maurice (Soultz-les-Bains) 48 34 17.4 N 7 29 09.35 E +Église St Maurice (Willgottheim) 48 40 14.52 N 7 30 33.23 E +Église St Michel (Reichshoffen) 48 55 54.84 N 7 39 52.02 E +Église St Michel (Weyersheim) 48 43 06.24 N 7 48 07.96 E +Église St Médard (Bœrsch) 48 28 40.44 N 7 26 24.4 E +Église St Nicolas (Haguenau) 48 49 13 N 7 47 32 E +Église St Nicolas (Neuve-Église) 48 19 50.88 N 7 18 48.24 E +Église St Nicolas (Wingersheim) 48 43 18.84 N 7 38 08.02 E +Église St Pantaléon (Gueberschwihr) 48 00 16.92 N 7 16 29.78 E +Église St Paul protestante (Strasbourg) 48 35 11 N 7 45 35 E +Église St Pierre "Dompeter" (Molsheim, Avolsheim) 48 33 24.12 N 7 30 19.59 E Église St Pierre le Jeune catholique (Strasbourg) 48 35 18.35 N 7 44 55.75 E Église St Pierre le Jeune protestante (Strasbourg) 48 35 08 N 7 44 47 E +Église St Rémi (Itterswiller) 48 21 51.48 N 7 25 37.42 E +Église St Sébastien (Soultzmatt) 47 57 37.08 N 7 14 15.36 E Église St Thomas protestante (Strasbourg) 48 34 47 N 7 44 44 E +Église St Trophime (Eschau) 48 29 25.08 N 7 42 57.96 E +Église St Ulrich (Altenstadt) 49 01 49.8 N 7 58 05.88 E +Église St Ulrich (Wittersheim) 48 46 53.04 N 7 39 27.47 E +Église St Étienne (Rosheim) 48 29 43.8 N 7 27 59.72 E +Église St Étienne (Seltz) 48 53 37.32 N 8 06 28.44 E +Église St Étienne simultanée (Wangen) 48 37 01.56 N 7 27 53.68 E +Église Ste Anne (Turckheim) 48 05 15.72 N 7 16 41.12 E +Église Ste Aurélie protestante (Strasbourg) 48 34 53 N 7 44 00 E +Église Ste Colombe (Hattstatt) 48 00 44.28 N 7 18 06.01 E +Église Ste Croix (Kaysersberg) Lamentation of Christ 48 08 20.04 N 7 15 48.56 E +Église Ste Croix (Rountzenheim) 48 49 08.76 N 8 00 26.39 E +Église Ste Foy (Sélestat) 48 15 33.67 N 7 27 21.81 E +Église Ste Lucie (Niederhergheim) 47 59 10.32 N 7 23 48.41 E Église Ste Madeleine (Strasbourg) 48 34 48 N 7 45 17 E +Église Ste Marguerite (Geispolsheim) 48 30 50.4 N 7 38 36.06 E +Église Ste Odile (Lapoutroie) 48 09 08.64 N 7 10 03.97 E +Église Ste Odile (Wintzfelden) 47 58 32.88 N 7 11 49.92 E +Église Ste Walburge (Walbourg) 48 53 05.51 N 7 47 21.97 E +Églises St Pierre le Vieux (Strasbourg) 48 34 58 N 7 44 24 E Đình Bảng Bảng Communal House (Đình Bảng in Vietnamese) is one of largest and finest village communal houses in Việt Nam. It is located in Đình Bảng commune, Từ Sơn district, Bắc Ninh province. 21 06 29.99 N 105 57 06.31 E +Архангельск 64 33 00 N 40 32 00 E +Астана 51 11 00 N 71 24 00 E +Астрахань 46 20 00 N 48 01 00 E +Барнаул 53 21 24 N 83 47 14 E Боровск Borovsk 55 12 27.19 N 36 29 05.05 E -กรุงเทพมหานคร Bangkok 13 45 08 N 100 29 38 E +Душанбе 38 34 23 N 68 47 11 E +Екатеринбург 59 57 00 N 30 19 00 E +Москва 55 45 21 N 37 37 04 E +Нижний Новгород 56 19 37 N 44 00 27 E +Санкт-Петербург 59 57 00 N 30 19 00 E +Северодвинск 64 34 00 N 39 51 00 E +กรุงเทพมหานคร Bangkok 13 45 00 N 100 31 00 E სვეტიცხოველი 41 50 31 N 44 43 16 E ჯვარი 41 30 06.84 N 44 26 24.72 E 鹿児島市 Kagoshima(鹿児島市; -shi) is a city in Japan and the capital city of Kagoshima Prefecture. 31 35 48.5 N 130 33 25.7 E 薩摩川内市 Satsumasendai is a city in Kagoshima prefecture, Japan. 31 48 48.5 N 130 18 14.3 E -上海 Shanghai is the largest city in China and is divided into 18 districts and one county (the island in the Yangtze River). It is located on the coast of eastern China at the mouth of the Chang Jiang (Yangtze River), and borders the provinces of Jiangsu and Zhejiang. 31 10 00 N 121 29 00 E +上海 Shanghai is the largest city in China and is divided into 18 districts and one county (the island in the Yangtze River). It is located on the coast of eastern China at the mouth of the Chang Jiang (Yangtze River), and borders the provinces of Jiangsu and Zhejiang. 31 10 00 N 121 28 00 E +东平 Daqing River 35 54 30 N 116 18 00 E 中南海 Zhongnanhai (Chinese: 中南海; pinyin: Zhōngnánhăi) is a complex of buildings in Beijing, China which serves as the central headquarters for the Communist Party of China and the government of the People's Republic of China. 39 54 41 N 116 22 50 E -九寨沟 Jiuzhaigou Valley (Chinese: 九寨沟; pinyin: Jiǔzhàigōu; lit. "Nine Stockades Gully") is a nature reserve in Aba(阿坝) Tibetan and Qiang Autonomous District, northern Sichuan province, China. It is known for its many multi-level waterfalls and colorful lakes, and was declared a UNESCO World Heritage Site in 1992. 33 09 34 N 103 52 40 E +九寨沟 Jiuzhaigou Valley (Chinese: 九寨沟; pinyin: Jiǔzhàigōu; lit. "Nine Stockades Gully") is a nature reserve in Aba(阿坝) Tibetan and Qiang Autonomous District, northern Sichuan province, China. 33 09 34 N 103 52 40 E 云南 Yunnan(云南) is a Chinese southwest border province, with the most varied nationalities in China. There are 52 nationalities of people living in Yunnan, out of 56 total throughout China. 25 03 00 N 101 52 00 E -京都市 Kyoto is a city in Japan. It was the capital of Japan from 794 to 1869. 35 00 41.8 N 135 46 05.2 E +京都市 Kyoto is a city in Japan. It was the capital of Japan from 794 to 1869. 35 00 42 N 135 46 05 E 兵馬俑 Terracotta Army 34 23 05.7 N 109 16 23.1 E 別府市 Beppu is a famous onsen city in Oita Prefecture on the island of Kyushu in Japan. 33 17 04.6 N 131 29 28.6 E 北九州市 Kitakyūshū is a city in Fukuoka Prefecture on the island of Kyushu, Japan. 33 53 00.3 N 130 52 30.7 E @@ -625,10 +929,12 @@ Zwehrenturm The Zwehrenturm from 1330 is a remain of Kassel's medieval defensive 南京 Nanjing (南京) is the capital of Jiangsu Province of China. It was the Chinese capital from 1927-1949. 32 03 00 N 118 46 00 E 南法華寺 Minamihokke-ji is the Buddhist temple in Takatori, Nara prefecture, Japan. 34 25 35.1 N 135 48 35.5 E 台南市 22 59 00 N 120 11 00 E +名古屋市 35 07 00 N 136 56 00 E 和歌山市 Wakayama (和歌山市, Wakayama-shi) is the capital city of Wakayama Prefecture in the Kansai region of Japan. 34 13 49.3 N 135 10 14.7 E -四川 Sichuan(四川) is the most densely populated province in west China. It is famous for its peppery dishes. 30 08 00 N 102 56 00 E +哈尔滨 Harbin is a sub-provincial city in north-east China and the capital of the Heilongjiang Province. 45 48 05 N 126 31 45 E +四川 Nature reserve in Aba(阿坝) Tibetan and Qiang Autonomous District, northern Sichuan province, China. It is known for its many multi-level waterfalls and colorful lakes, and was declared a UNESCO World Heritage Site in 1992. 30 08 00 N 102 56 00 E 圆明园 Yuanmingyuan (pinyin: Yuanmingyuan, 圆明园), or the old Summer Palace in Peking. 40 00 26 N 116 17 33 E -大津市 Otsu is a city in Japan. It was the capital of Japan from 667 to 672. 35 01 04.1 N 135 51 17 E +大津市 Ōtsu is a city in Japan. It was the capital of Japan from 667 to 672. 35 01 04.1 N 135 51 17 E 大阪市 Osaka 34 41 37.5 N 135 30 07.6 E 天坛 Temple of Heaven in Beijing. 39 52 56.1 N 116 24 23.7 E 天安門 Tiananmen (Gate of Heavenly Peace) in Beijing was the southern gate of the Imperial City in Beijing. 39 54 26.4 N 116 23 27.9 E @@ -636,27 +942,25 @@ Zwehrenturm The Zwehrenturm from 1330 is a remain of Kassel's medieval defensive 宇治市 Uji is a city in Kyoto prefecture. The Byodoin (an ancient Buddhist temple) and the Ujigami Shrine are famous landmarks in Uji. 34 53 03.7 N 135 47 59.3 E 小田原市 Odawara, Kanagawa 35 15 52.6 N 139 09 08 E 屋久島 Yakushima island 30 20 00 N 130 30 00 E -川崎市 Kawasaki, Kanagawa 35 31 51.2 N 139 42 10.8 E 广东 Guangdong (广东) is a coastal province in southern China adjacent to Hongkong and Macao. 23 24 00 N 113 30 00 E -广州 Guangzhou is the capital of Guangdong Province in southern China. The city was formerly known internationally as Canton City or simply Canton. 23 07 43.67 N 113 15 32.31 E +广州 Guangzhou is the capital of Guangdong Province in southern China. The city was formerly known internationally as Canton City or simply Canton. 23 07 43.66 N 113 15 32.31 E 広島市 Hiroshima is one of largest cities in Japan and the capital of Hiroshima prefecture. 34 23 06.9 N 132 27 19.1 E 徳島市 Tokushima is the capital city of Tokushima prefecture on the island of Kyushu in Japan. 34 04 13 N 134 33 17.8 E -成都 Chengdu is the capital of Sichuan province in China. 30 39 49 N 104 04 00 E +成都 Chengdu is the capital city and prefecture-level division of Sichuan Province, in southwestern China. 30 39 49 N 104 04 00 E 景山公园 Jingshan Park, Dongcheng District, Beijing. 39 55 24.5 N 116 23 26.2 E -杭州 Hangzhou (杭州) is a picturesque city in south China. It is the capital of Zhejiang Province and visited by Marco Polo. 30 15 00 N 120 10 00 E +杭州 Hangzhou (杭州) is a picturesque city in south China. It is the capital of Zhejiang Province and was visited by Marco Polo. 30 15 00 N 120 10 00 E 武汉 Wuhan is the capital of the Chinese province of Hubei 30 34 21 N 114 16 45 E -滕王阁 28 41 02.76 N 115 52 32.88 E -熊本市 Kumamoto is the capital city of Kumamoto Prefecture, on the island of Kyushu, Japan. 32 48 10.8 N 130 42 28.3 E +深圳 Shenzhen is a major city in Guangdong Province, China. 22 32 06 N 114 03 14.4 E +滕王阁 The Pavilion of Prince Teng or Tengwang Pavilion is a building in the north west of the city of Nanchang, in Jiangxi province, China. 28 41 02.76 N 115 52 32.88 E 相模原市 Sagamihara, Kanagawa 35 34 17.1 N 139 22 23.3 E 祇園 Gion (祇園) is a district of Kyoto, Japan, originally developed in the middle ages. 35 00 13 N 135 46 30 E 福岡市 Fukuoka, Fukuoka 33 35 24.5 N 130 24 06.2 E 紫禁城 The Forbidden City (紫禁城), located at the centre of Beijing, China, was the imperial palace of the last two imperial dynasties of China (from 1420 to 1924). 39 54 50.1 N 116 23 27.6 E 西安 Xi'an is an ancient city located in north central China. It was the capital of various dynasties from 1046 B.C. to 907 A.D, and it has been known under a number of different names including most notably Chang'an during the Tang dynasty. 34 16 00 N 108 57 00 E 逗子市 Zushi (逗子市 Zushi-shi) is a city located in Kanagawa, Japan. 35 17 44.2 N 139 34 49.2 E -那覇市 26 12 44.4 N 127 40 44.8 E +重庆 Chongqing is located in the southwest of China, is China's largest and most populous municipality. 29 33 00 N 106 33 00 E 鎌倉 Zeniarai Benten shrine 35 19 09.3 N 139 32 48.1 E -颐和园 The Summer Palace (pinyin: Yiheyuan, 颐和园) is a former imperial palace in northwest of Beijing, China. It has been transformed in a public garden. 39 59 51 N 116 16 08.04 E -香港 Hong Kong 22 16 01 N 114 11 17 E -高松市 日本語: 高松市は日本の瀬戸内海沿岸に位置する香川県の県都である。四国の北端に位置することや本州に近いことから、港町として発展してきた歴史をもつ。 34 20 34.1 N 134 02 47.8 E +颐和园 The Summer Palace is a former imperial palace in northwest of Beijing, China. It has been transformed in a public garden. 39 59 51 N 116 16 08.04 E +香港 22 16 01 N 114 11 17 E 高雄市 Kaohsiung is a city in Taiwan. 22 38 00 N 120 16 00 E - +서울특별시 Seoul is the capital of South Korea. 37 35 00 N 127 00 00 E diff --git a/tim/prune/function/AddMapSourceDialog.java b/tim/prune/function/settings/AddMapSourceDialog.java similarity index 99% rename from tim/prune/function/AddMapSourceDialog.java rename to tim/prune/function/settings/AddMapSourceDialog.java index 9207ac5..b9ea26f 100644 --- a/tim/prune/function/AddMapSourceDialog.java +++ b/tim/prune/function/settings/AddMapSourceDialog.java @@ -1,4 +1,4 @@ -package tim.prune.function; +package tim.prune.function.settings; import java.awt.BorderLayout; import java.awt.Component; diff --git a/tim/prune/function/MapSourceListModel.java b/tim/prune/function/settings/MapSourceListModel.java similarity index 96% rename from tim/prune/function/MapSourceListModel.java rename to tim/prune/function/settings/MapSourceListModel.java index 489f95d..dc7954a 100644 --- a/tim/prune/function/MapSourceListModel.java +++ b/tim/prune/function/settings/MapSourceListModel.java @@ -1,4 +1,4 @@ -package tim.prune.function; +package tim.prune.function.settings; import javax.swing.AbstractListModel; diff --git a/tim/prune/function/SaveConfig.java b/tim/prune/function/settings/SaveConfig.java similarity index 99% rename from tim/prune/function/SaveConfig.java rename to tim/prune/function/settings/SaveConfig.java index c229437..d449051 100644 --- a/tim/prune/function/SaveConfig.java +++ b/tim/prune/function/settings/SaveConfig.java @@ -1,4 +1,4 @@ -package tim.prune.function; +package tim.prune.function.settings; import java.awt.BorderLayout; import java.awt.Component; diff --git a/tim/prune/function/SetAltitudeTolerance.java b/tim/prune/function/settings/SetAltitudeTolerance.java similarity index 95% rename from tim/prune/function/SetAltitudeTolerance.java rename to tim/prune/function/settings/SetAltitudeTolerance.java index c51a56a..1049851 100644 --- a/tim/prune/function/SetAltitudeTolerance.java +++ b/tim/prune/function/settings/SetAltitudeTolerance.java @@ -1,10 +1,11 @@ -package tim.prune.function; +package tim.prune.function.settings; import tim.prune.App; import tim.prune.DataSubscriber; import tim.prune.UpdateMessageBroker; import tim.prune.config.Config; import tim.prune.data.Unit; +import tim.prune.function.SingleNumericParameterFunction; /** * Function to set the tolerance for the altitude range calculations diff --git a/tim/prune/function/SetColours.java b/tim/prune/function/settings/SetColours.java similarity index 99% rename from tim/prune/function/SetColours.java rename to tim/prune/function/settings/SetColours.java index 33b4357..7c0fdf0 100644 --- a/tim/prune/function/SetColours.java +++ b/tim/prune/function/settings/SetColours.java @@ -1,4 +1,4 @@ -package tim.prune.function; +package tim.prune.function.settings; import java.awt.BorderLayout; import java.awt.Component; diff --git a/tim/prune/function/settings/SetDisplaySettings.java b/tim/prune/function/settings/SetDisplaySettings.java new file mode 100644 index 0000000..63286c5 --- /dev/null +++ b/tim/prune/function/settings/SetDisplaySettings.java @@ -0,0 +1,274 @@ +package tim.prune.function.settings; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.ListCellRenderer; +import javax.swing.border.EtchedBorder; + +import tim.prune.App; +import tim.prune.DataSubscriber; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.config.Config; +import tim.prune.gui.GuiGridLayout; +import tim.prune.gui.WholeNumberField; +import tim.prune.gui.map.WpIconLibrary; + +/** + * Class to show the dialog for setting the display settings + * like line width, antialiasing, waypoint icons + */ +public class SetDisplaySettings extends GenericFunction +{ + /** + * Inner class to render waypoint icons + */ + class IconComboRenderer extends JLabel implements ListCellRenderer + { + /** Cached icons for each waypoint type */ + private ImageIcon[] _icons = new ImageIcon[WpIconLibrary.WAYPT_NUMBER_OF_ICONS]; + + /** Constructor */ + IconComboRenderer() + { + setOpaque(true); + } + + /** Get the label text at the given index */ + private String getLabel(int inIndex) + { + return I18nManager.getText("dialog.displaysettings.wpicon." + WpIconLibrary.getIconName(inIndex)); + } + + /** Get the image icon at the given index */ + private ImageIcon getIcon(int inIndex) + { + if (_icons[inIndex] == null) + { + _icons[inIndex] = WpIconLibrary.getIconDefinition(inIndex, 1).getImageIcon(); + } + return _icons[inIndex]; + } + + /** @return a label to display the combo box entry */ + public Component getListCellRendererComponent( + JList inList, Integer inValue, int inIndex, + boolean inSelected, boolean inFocus) + { + if (inSelected) { + setBackground(inList.getSelectionBackground()); + setForeground(inList.getSelectionForeground()); + } else { + setBackground(inList.getBackground()); + setForeground(inList.getForeground()); + } + setIcon(getIcon(inValue)); + setText(getLabel(inValue)); + return this; + } + } + + + // Members of SetDisplaySettings + private JDialog _dialog = null; + private WholeNumberField _lineWidthField = null; + private JCheckBox _antialiasCheckbox = null; + private JComboBox _wpIconCombobox = null; + private JRadioButton[] _sizeRadioButtons = null; + private JButton _okButton = null; + + + /** + * Constructor + * @param inApp app object + */ + public SetDisplaySettings(App inApp) + { + super(inApp); + } + + /** + * Return the name key for this function + */ + public String getNameKey() + { + return "function.setdisplaysettings"; + } + + /** + * @return the contents of the window as a Component + */ + private Component makeContents() + { + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout(0, 5)); + JPanel midPanel = new JPanel(); + midPanel.setLayout(new BoxLayout(midPanel, BoxLayout.Y_AXIS)); + midPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); + + JPanel linesPanel = new JPanel(); + linesPanel.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3)) + ); + GuiGridLayout grid = new GuiGridLayout(linesPanel); + + // line width + JLabel lineWidthLabel = new JLabel(I18nManager.getText("dialog.displaysettings.linewidth")); + grid.add(lineWidthLabel); + _lineWidthField = new WholeNumberField(1); + grid.add(_lineWidthField); + // Antialiasing + _antialiasCheckbox = new JCheckBox(I18nManager.getText("dialog.displaysettings.antialias"), false); + grid.add(_antialiasCheckbox); + grid.add(new JLabel("")); + + linesPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + midPanel.add(linesPanel); + midPanel.add(Box.createVerticalStrut(10)); + + // Panel for waypoint icons + JPanel waypointsPanel = new JPanel(); + waypointsPanel.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3)) + ); + waypointsPanel.setLayout(new BoxLayout(waypointsPanel, BoxLayout.Y_AXIS)); + // Select which waypoint icon to use + JPanel iconPanel = new JPanel(); + GuiGridLayout iconGrid = new GuiGridLayout(iconPanel); + JLabel headerLabel = new JLabel(I18nManager.getText("dialog.displaysettings.waypointicons")); + headerLabel.setAlignmentX(Component.LEFT_ALIGNMENT); + iconGrid.add(headerLabel); + _wpIconCombobox = new JComboBox(new Integer[] {0, 1, 2, 3, 4}); + _wpIconCombobox.setRenderer(new IconComboRenderer()); + iconGrid.add(_wpIconCombobox); + waypointsPanel.add(iconPanel); + // Select size of waypoints + JPanel sizePanel = new JPanel(); + sizePanel.setLayout(new FlowLayout(FlowLayout.CENTER)); + _sizeRadioButtons = new JRadioButton[3]; + ButtonGroup sizeRadioGroup = new ButtonGroup(); + final String[] sizeKeys = {"small", "medium", "large"}; + for (int i=0; i<3; i++) + { + _sizeRadioButtons[i] = new JRadioButton(I18nManager.getText("dialog.displaysettings.size." + sizeKeys[i])); + sizeRadioGroup.add(_sizeRadioButtons[i]); + sizePanel.add(_sizeRadioButtons[i]); + } + waypointsPanel.add(sizePanel); + waypointsPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + midPanel.add(waypointsPanel); + + mainPanel.add(midPanel, BorderLayout.CENTER); + + // button panel at bottom + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + // OK button + _okButton = new JButton(I18nManager.getText("button.ok")); + _okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + finish(); + } + }); + buttonPanel.add(_okButton); + // Cancel button + 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; + } + + + /** + * Show window + */ + public void begin() + { + if (_dialog == null) + { + _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey())); + _dialog.setLocationRelativeTo(_parentFrame); + _dialog.getContentPane().add(makeContents()); + _dialog.pack(); + } + // Set values from config + int lineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH); + if (lineWidth < 1 || lineWidth > 4) {lineWidth = 2;} + _lineWidthField.setValue(lineWidth); + _antialiasCheckbox.setSelected(Config.getConfigBoolean(Config.KEY_ANTIALIAS)); + _wpIconCombobox.setSelectedIndex(Config.getConfigInt(Config.KEY_WAYPOINT_ICONS)); + selectIconSizeRadio(Config.getConfigInt(Config.KEY_WAYPOINT_ICON_SIZE)); + _dialog.setVisible(true); + } + + /** + * Select the corresponding radio button according to the numeric value + * @param inValue numeric value saved in Config + */ + private void selectIconSizeRadio(int inValue) + { + if (inValue < 0 || inValue >= _sizeRadioButtons.length) + { + inValue = 1; + } + if (_sizeRadioButtons[inValue] != null) + { + _sizeRadioButtons[inValue].setSelected(true); + } + } + + /** + * @return numeric value of selected icon size according to radio buttons + */ + private int getSelectedIconSize() + { + for (int i=0; i<_sizeRadioButtons.length; i++) + { + if (_sizeRadioButtons[i] != null && _sizeRadioButtons[i].isSelected()) + { + return i; + } + } + return 1; // default is medium + } + + /** + * Save settings and close + */ + public void finish() + { + // update config + int lineWidth = _lineWidthField.getValue(); + if (lineWidth < 1 || lineWidth > 4) {lineWidth = 2;} + Config.setConfigInt(Config.KEY_LINE_WIDTH, lineWidth); + Config.setConfigBoolean(Config.KEY_ANTIALIAS, _antialiasCheckbox.isSelected()); + Config.setConfigInt(Config.KEY_WAYPOINT_ICONS, _wpIconCombobox.getSelectedIndex()); + Config.setConfigInt(Config.KEY_WAYPOINT_ICON_SIZE, getSelectedIconSize()); + // refresh display + UpdateMessageBroker.informSubscribers(DataSubscriber.MAPSERVER_CHANGED); + _dialog.dispose(); + } +} diff --git a/tim/prune/function/SetLanguage.java b/tim/prune/function/settings/SetLanguage.java similarity index 97% rename from tim/prune/function/SetLanguage.java rename to tim/prune/function/settings/SetLanguage.java index 832b521..642e715 100644 --- a/tim/prune/function/SetLanguage.java +++ b/tim/prune/function/settings/SetLanguage.java @@ -1,4 +1,4 @@ -package tim.prune.function; +package tim.prune.function.settings; import java.awt.BorderLayout; import java.awt.Component; @@ -43,13 +43,13 @@ public class SetLanguage extends GenericFunction /** Names of languages for display in dropdown (not translated) */ private static final String[] LANGUAGE_NAMES = {"afrikaans", "\u010de\u0161tina", "deutsch", "english", "american english", "espa\u00F1ol", "fran\u00E7ais", "italiano", "magyar", "nederlands", "polski", "portugu\u00EAs", "rom\u00E2n\u0103", - "\u0440\u0443\u0441\u0441\u043a\u0438\u0439 (russian)", "\u4e2d\u6587 (chinese)", + "suomi", "\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", "\u0443\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430 \u043c\u043e\u0432\u0430 (ukrainian)" + "\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 = {"af", "cz", "de", "en", "en_us", "es", "fr", "it", "hu", - "nl", "pl", "pt", "ro", "ru", "zh", "ja", "ko", "de_ch", "tr", "uk" + "nl", "pl", "pt", "ro", "fi", "ru", "zh", "ja", "ko", "de_ch", "uk" }; diff --git a/tim/prune/function/SetMapBgFunction.java b/tim/prune/function/settings/SetMapBgFunction.java similarity index 99% rename from tim/prune/function/SetMapBgFunction.java rename to tim/prune/function/settings/SetMapBgFunction.java index a875d00..862c9d8 100644 --- a/tim/prune/function/SetMapBgFunction.java +++ b/tim/prune/function/settings/SetMapBgFunction.java @@ -1,4 +1,4 @@ -package tim.prune.function; +package tim.prune.function.settings; import java.awt.BorderLayout; import java.awt.Component; diff --git a/tim/prune/function/SetPathsFunction.java b/tim/prune/function/settings/SetPathsFunction.java similarity index 99% rename from tim/prune/function/SetPathsFunction.java rename to tim/prune/function/settings/SetPathsFunction.java index a5131af..a5d0871 100644 --- a/tim/prune/function/SetPathsFunction.java +++ b/tim/prune/function/settings/SetPathsFunction.java @@ -1,4 +1,4 @@ -package tim.prune.function; +package tim.prune.function.settings; import java.awt.BorderLayout; import java.awt.FlowLayout; diff --git a/tim/prune/function/sew/SplitSegmentsFunction.java b/tim/prune/function/sew/SplitSegmentsFunction.java index 91a7d52..6fec075 100644 --- a/tim/prune/function/sew/SplitSegmentsFunction.java +++ b/tim/prune/function/sew/SplitSegmentsFunction.java @@ -1,77 +1,20 @@ 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.function.DistanceTimeLimitFunction; 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 +public class SplitSegmentsFunction extends DistanceTimeLimitFunction { - /** 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 */ @@ -86,161 +29,18 @@ public class SplitSegmentsFunction extends GenericFunction 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() + protected void performFunction() { // 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) { + final int timeLimitSeconds = getTimeLimitInSeconds(); + final boolean splitByTime = (timeLimitSeconds > 0); + final double distLimitRadians = getDistanceLimitRadians(); + final boolean splitByDistance = (distLimitRadians > 0.0); + if (!splitByTime && !splitByDistance) { return; // neither option selected } @@ -257,8 +57,8 @@ public class SplitSegmentsFunction extends GenericFunction if (!currPoint.isWaypoint()) { boolean splitHere = (prevPoint != null) - && ((checkDistLimit && DataPoint.calculateRadiansBetween(prevPoint, currPoint) > distLimitRadians) - || (checkTimeLimit && currPoint.hasTimestamp() && prevPoint.hasTimestamp() + && ((splitByDistance && DataPoint.calculateRadiansBetween(prevPoint, currPoint) > distLimitRadians) + || (splitByTime && currPoint.hasTimestamp() && prevPoint.hasTimestamp() && currPoint.getTimestamp().getSecondsSince(prevPoint.getTimestamp()) > timeLimitSeconds)); if (splitHere && !currPoint.getSegmentStart()) { diff --git a/tim/prune/gui/CombinedListAndModel.java b/tim/prune/gui/CombinedListAndModel.java new file mode 100644 index 0000000..4b94ceb --- /dev/null +++ b/tim/prune/gui/CombinedListAndModel.java @@ -0,0 +1,110 @@ +package tim.prune.gui; + +import javax.swing.DefaultListModel; +import javax.swing.JList; +import javax.swing.ListSelectionModel; + +import tim.prune.I18nManager; + + +/** + * Listbox class which also contains its own string model. + * Also has the ability to limit its size and show a single + * text instead of a huge list + */ +public class CombinedListAndModel extends JList +{ + private DefaultListModel _model = null; + private final int _key; + private int _maxNumEntries = 0; + private boolean _tooManyEntries = false; + private boolean _unlimited = false; + + + /** + * Constructor + */ + public CombinedListAndModel(int inKey) + { + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + _model = new DefaultListModel(); + setModel(_model); + _key = inKey; + } + + /** + * @param inMaxNum maximum number of entries to allow + */ + public void setMaxNumEntries(int inMaxNum) + { + _maxNumEntries = inMaxNum; + } + + /** + * @param inUnlimited true if list is temporarily unlimited + */ + public void setUnlimited(boolean inUnlimited) + { + _unlimited = inUnlimited; + } + + /** + * @return key + */ + public int getKey() + { + return _key; + } + + /** + * @param inItem String to add to the list + */ + public void addItem(String inItem) + { + if (!_tooManyEntries) + { + _model.addElement(inItem); + if (_maxNumEntries > 0 && !_unlimited + && _model.getSize() > _maxNumEntries) + { + _tooManyEntries = true; + _model.clear(); + _model.addElement(I18nManager.getText("dialog.settimezone.list.toomany")); + } + } + } + + /** + * @return the selected String, or null + */ + public String getSelectedItem() + { + final int selectedIndex = getSelectedIndex(); + if (_tooManyEntries || selectedIndex < 0) + { + return null; + } + return _model.getElementAt(selectedIndex); + } + + /** + * Clear the list + */ + public void clear() + { + _model.clear(); + _tooManyEntries = false; + _unlimited = false; + } + + /** + * @param inItem item to select + */ + public void selectItem(String inItem) + { + if (!_tooManyEntries && inItem != null) + { + this.setSelectedValue(inItem, true); + } + } +} diff --git a/tim/prune/gui/DetailsDisplay.java b/tim/prune/gui/DetailsDisplay.java index fb868e8..2fa98dc 100644 --- a/tim/prune/gui/DetailsDisplay.java +++ b/tim/prune/gui/DetailsDisplay.java @@ -8,6 +8,7 @@ import java.awt.Font; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.TimeZone; import javax.swing.BorderFactory; import javax.swing.Box; @@ -25,6 +26,7 @@ import tim.prune.GenericFunction; import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.Config; +import tim.prune.config.TimezoneHelper; import tim.prune.data.AltitudeRange; import tim.prune.data.AudioClip; import tim.prune.data.Coordinate; @@ -86,6 +88,8 @@ public class DetailsDisplay extends GenericDisplay // Units private JComboBox _coordFormatDropdown = null; private JComboBox _distUnitsDropdown = null; + // Timezone + private TimeZone _timezone = null; // Cached labels private static final String LABEL_POINT_SELECTED = I18nManager.getText("details.index.selected") + ": "; @@ -299,6 +303,10 @@ public class DetailsDisplay extends GenericDisplay UnitSet unitSet = UnitSetLibrary.getUnitSet(_distUnitsDropdown.getSelectedIndex()); String distUnitsStr = I18nManager.getText(unitSet.getDistanceUnit().getShortnameKey()); String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey()); + if (_timezone == null || (inUpdateType | UNITS_CHANGED) > 0) { + _timezone = TimezoneHelper.getSelectedTimezone(); + } + if (_track == null || currentPoint == null) { _indexLabel.setText(I18nManager.getText("details.nopointselection")); @@ -326,11 +334,13 @@ public class DetailsDisplay extends GenericDisplay (LABEL_POINT_ALTITUDE + currentPoint.getAltitude().getValue(altUnit) + " " + I18nManager.getText(altUnit.getShortnameKey())) : ""); - if (currentPoint.hasTimestamp()) { - _ptDateLabel.setText(LABEL_POINT_DATE + currentPoint.getTimestamp().getDateText()); - _ptTimeLabel.setText(LABEL_POINT_TIME + currentPoint.getTimestamp().getTimeText()); + if (currentPoint.hasTimestamp()) + { + _ptDateLabel.setText(LABEL_POINT_DATE + currentPoint.getTimestamp().getDateText(_timezone)); + _ptTimeLabel.setText(LABEL_POINT_TIME + currentPoint.getTimestamp().getTimeText(_timezone)); } - else { + else + { _ptDateLabel.setText(""); _ptTimeLabel.setText(""); } @@ -477,7 +487,9 @@ public class DetailsDisplay extends GenericDisplay String shortPath = shortenPath(fullPath); _photoPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath); _photoPathLabel.setToolTipText(currentPhoto.getFullPath()); - _photoTimestampLabel.setText(currentPhoto.hasTimestamp()?(LABEL_POINT_TIME + currentPhoto.getTimestamp().getText()):""); + _photoTimestampLabel.setText(currentPhoto.hasTimestamp() ? + (LABEL_POINT_TIME + currentPhoto.getTimestamp().getText(_timezone)) + : ""); _photoConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": " + (currentPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED ? I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes"))); @@ -513,7 +525,9 @@ public class DetailsDisplay extends GenericDisplay String shortPath = shortenPath(fullPath); _audioPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath); _audioPathLabel.setToolTipText(fullPath == null ? "" : fullPath); - _audioTimestampLabel.setText(currentAudio.hasTimestamp()?(LABEL_POINT_TIME + currentAudio.getTimestamp().getText()):""); + _audioTimestampLabel.setText(currentAudio.hasTimestamp() ? + (LABEL_POINT_TIME + currentAudio.getTimestamp().getText(_timezone)) + : ""); int audioLength = currentAudio.getLengthInSeconds(); _audioLengthLabel.setText(audioLength < 0?"":LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(audioLength)); _audioConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": " @@ -571,7 +585,7 @@ public class DetailsDisplay extends GenericDisplay { result = inCoord.substring(0, chopPos); // Maybe there's an exponential in there too which needs to be appended - int expPos = inCoord.toUpperCase().indexOf("E", chopPos); + int expPos = inCoord.toUpperCase().indexOf("E", chopPos); if (expPos > 0 && expPos < (inCoord.length()-1)) { result += inCoord.substring(expPos); diff --git a/tim/prune/gui/IconManager.java b/tim/prune/gui/IconManager.java index fd0a888..a4572be 100644 --- a/tim/prune/gui/IconManager.java +++ b/tim/prune/gui/IconManager.java @@ -30,6 +30,8 @@ public abstract class IconManager public static final String POINTS_DISCONNECTED_BUTTON = "points_disconnected.png"; /** Icon for points hidden, just lines icon on main map display */ public static final String POINTS_HIDDEN_BUTTON = "points_hidden.png"; + /** Icon for points, lines and arrows on main map display */ + public static final String POINTS_WITH_ARROWS_BUTTON = "points_arrows.png"; /** Icon for edit mode button on main map display when not selected */ public static final String EDIT_MODE_BUTTON = "drag_points_icon.gif"; /** Icon for edit mode button on main map display when selected */ @@ -86,6 +88,9 @@ public abstract class IconManager /** Icon for a given entry being empty (blank) */ public static final String ENTRY_NONE = "entry_none.gif"; + public static final String WAYPOINT_ICON_PREFIX = "wpicon_"; + public static final String WAYPOINT_ICON_SUFFIX = ".png"; + /** * Get the specified image * @param inFilename filename of image (using constants) diff --git a/tim/prune/gui/ImageUtils.java b/tim/prune/gui/ImageUtils.java index d152ecc..c3c4c20 100644 --- a/tim/prune/gui/ImageUtils.java +++ b/tim/prune/gui/ImageUtils.java @@ -39,6 +39,9 @@ public abstract class ImageUtils */ public static BufferedImage createScaledImage(Image inImage, int inWidth, int inHeight) { + if (inWidth <= 0 || inHeight <= 0) { + return null; + } // create smaller image and force its loading Image smallerImage = inImage.getScaledInstance(inWidth, inHeight, Image.SCALE_SMOOTH); Image tempImage = new ImageIcon(smallerImage).getImage(); diff --git a/tim/prune/gui/MenuManager.java b/tim/prune/gui/MenuManager.java index 38ed02b..08ecf18 100644 --- a/tim/prune/gui/MenuManager.java +++ b/tim/prune/gui/MenuManager.java @@ -51,7 +51,6 @@ public class MenuManager implements DataSubscriber private JMenuItem _exportKmlItem = null; private JMenuItem _exportGpxItem = null; private JMenuItem _exportPovItem = null; - private JMenuItem _exportSvgItem = null; private JMenuItem _exportImageItem = null; private JMenu _recentFileMenu = null; private JMenuItem _undoItem = null; @@ -82,6 +81,7 @@ public class MenuManager implements DataSubscriber private JMenuItem _rearrangeWaypointsItem = null; private JMenuItem _splitSegmentsItem = null; private JMenuItem _sewSegmentsItem = null; + private JMenuItem _createMarkerWaypointsItem = null; private JMenuItem _cutAndMoveItem = null; private JMenuItem _convertNamesToTimesItem = null; private JMenuItem _deleteFieldValuesItem = null; @@ -94,9 +94,9 @@ public class MenuManager implements DataSubscriber private JMenuItem _lookupSrtmItem = null; private JMenuItem _downloadSrtmItem = null; private JMenuItem _nearbyWikipediaItem = null; + private JMenuItem _nearbyOsmPoiItem = null; private JMenuItem _showPeakfinderItem = null; private JMenuItem _showGeohackItem = null; - private JMenuItem _showPanoramioItem = null; private JMenuItem _searchOpencachingDeItem = null; private JMenuItem _searchMapillaryItem = null; private JMenuItem _downloadOsmItem = null; @@ -123,7 +123,6 @@ public class MenuManager implements DataSubscriber private JMenuItem _correlateAudiosItem = null; private JMenuItem _selectNoAudioItem = null; private JCheckBoxMenuItem _onlineCheckbox = null; - private JCheckBoxMenuItem _antialiasCheckbox = null; private JCheckBoxMenuItem _autosaveSettingsCheckbox = null; // ActionListeners for reuse by menu and toolbar @@ -236,9 +235,6 @@ public class MenuManager implements DataSubscriber // Pov _exportPovItem = makeMenuItem(FunctionLibrary.FUNCTION_POVEXPORT, false); fileMenu.add(_exportPovItem); - // Svg - _exportSvgItem = makeMenuItem(FunctionLibrary.FUNCTION_SVGEXPORT, false); - fileMenu.add(_exportSvgItem); // Image _exportImageItem = makeMenuItem(FunctionLibrary.FUNCTION_IMAGEEXPORT, false); fileMenu.add(_exportImageItem); @@ -289,12 +285,12 @@ public class MenuManager implements DataSubscriber onlineMenu.add(_nearbyWikipediaItem); JMenuItem searchWikipediaNamesItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_WIKIPEDIA); onlineMenu.add(searchWikipediaNamesItem); + _nearbyOsmPoiItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_OSMPOIS); + onlineMenu.add(_nearbyOsmPoiItem); _showPeakfinderItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_PEAKFINDER, "webservice.peakfinder"), false); onlineMenu.add(_showPeakfinderItem); _showGeohackItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_GEOHACK, "webservice.geohack"), false); onlineMenu.add(_showGeohackItem); - _showPanoramioItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_PANORAMIO, "webservice.panoramio"), false); - onlineMenu.add(_showPanoramioItem); onlineMenu.addSeparator(); _searchOpencachingDeItem = makeMenuItem(new SearchOpenCachingDeFunction(_app), false); @@ -358,6 +354,9 @@ public class MenuManager implements DataSubscriber // Sew track segments _sewSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SEW_SEGMENTS, false); trackMenu.add(_sewSegmentsItem); + // Create marker waypoints + _createMarkerWaypointsItem = makeMenuItem(FunctionLibrary.FUNCTION_CREATE_MARKER_WAYPOINTS, false); + trackMenu.add(_createMarkerWaypointsItem); trackMenu.addSeparator(); _learnEstimationParams = makeMenuItem(FunctionLibrary.FUNCTION_LEARN_ESTIMATION_PARAMS, false); trackMenu.add(_learnEstimationParams); @@ -637,22 +636,15 @@ public class MenuManager implements DataSubscriber settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_PATHS)); // Set colours settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS)); - // Set line width used for drawing - settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_LINE_WIDTH))); - // Use antialias or not - _antialiasCheckbox = new JCheckBoxMenuItem(I18nManager.getText("menu.settings.antialias"), false); - _antialiasCheckbox.setSelected(Config.getConfigBoolean(Config.KEY_ANTIALIAS)); - _antialiasCheckbox.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Config.setConfigBoolean(Config.KEY_ANTIALIAS, _antialiasCheckbox.isSelected()); - UpdateMessageBroker.informSubscribers(MAPSERVER_CHANGED); - } - }); - settingsMenu.add(_antialiasCheckbox); + // display settings + JMenuItem setDisplaySettingsItem = makeMenuItem(FunctionLibrary.FUNCTION_SET_DISPLAY_SETTINGS); + settingsMenu.add(setDisplaySettingsItem); // Set language settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE)); // Set altitude tolerance settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_ALTITUDE_TOLERANCE))); + // Set timezone + settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_TIMEZONE)); settingsMenu.addSeparator(); // Save configuration settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SAVECONFIG)); @@ -865,15 +857,16 @@ public class MenuManager implements DataSubscriber _exportKmlItem.setEnabled(hasData); _exportGpxItem.setEnabled(hasData); _exportPovItem.setEnabled(hasMultiplePoints); - _exportSvgItem.setEnabled(hasMultiplePoints); _exportImageItem.setEnabled(hasMultiplePoints); _compressItem.setEnabled(hasData); _markRectangleItem.setEnabled(hasData); _markUphillLiftsItem.setEnabled(hasData && _track.hasAltitudeData()); _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints()); _rearrangeWaypointsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints()); - _splitSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3); - _sewSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3); + final boolean hasSeveralTrackPoints = hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3; + _splitSegmentsItem.setEnabled(hasSeveralTrackPoints); + _sewSegmentsItem.setEnabled(hasSeveralTrackPoints); + _createMarkerWaypointsItem.setEnabled(hasSeveralTrackPoints); _selectAllItem.setEnabled(hasData); _selectNoneItem.setEnabled(hasData); _show3dItem.setEnabled(hasMultiplePoints); @@ -885,6 +878,7 @@ public class MenuManager implements DataSubscriber _uploadGpsiesItem.setEnabled(hasData && _track.hasTrackPoints()); _lookupSrtmItem.setEnabled(hasData); _nearbyWikipediaItem.setEnabled(hasData); + _nearbyOsmPoiItem.setEnabled(hasData); _downloadOsmItem.setEnabled(hasData); _getWeatherItem.setEnabled(hasData); _findWaypointItem.setEnabled(hasData && _track.hasWaypoints()); @@ -913,7 +907,6 @@ public class MenuManager implements DataSubscriber _duplicatePointItem.setEnabled(hasPoint); _showPeakfinderItem.setEnabled(hasPoint); _showGeohackItem.setEnabled(hasPoint); - _showPanoramioItem.setEnabled(hasPoint); _searchOpencachingDeItem.setEnabled(hasPoint); _searchMapillaryItem.setEnabled(hasPoint); // is it a waypoint? diff --git a/tim/prune/gui/TripleStateCheckBox.java b/tim/prune/gui/MultiStateCheckBox.java similarity index 62% rename from tim/prune/gui/TripleStateCheckBox.java rename to tim/prune/gui/MultiStateCheckBox.java index 6bd6001..65a5e87 100644 --- a/tim/prune/gui/TripleStateCheckBox.java +++ b/tim/prune/gui/MultiStateCheckBox.java @@ -7,16 +7,19 @@ import javax.swing.ImageIcon; import javax.swing.JCheckBox; /** - * Class to represent a checkbox with three states, through which it cycles - * Instead of calling isChecked, need to use getCurrentState which will - * return 0, 1 or 2 + * Class to represent a checkbox with multiple states, through which it cycles. + * Instead of calling isChecked, callers need to use getCurrentState which will + * return 0 up to (n-1) for n states. */ -public class TripleStateCheckBox extends JCheckBox implements ItemListener +public class MultiStateCheckBox extends JCheckBox implements ItemListener { /** Array of icons to be used */ - private ImageIcon[] _icons = new ImageIcon[3]; - /** Current state 0, 1 or 2 */ + private ImageIcon[] _icons = null; + /** Current state 0 to n-1 */ private int _currState = 0; + /** Number of states n */ + private final int _numStates; + /** Inner class to proxy the listening events */ private class ProxyListener implements ItemListener @@ -33,22 +36,31 @@ public class TripleStateCheckBox extends JCheckBox implements ItemListener } } - /** Constructor */ - public TripleStateCheckBox() + /** + * Constructor + * @param inNumStates number of states to cycle through + */ + public MultiStateCheckBox(int inNumStates) { + _numStates = (inNumStates > 0) ? inNumStates : 1; + _icons = new ImageIcon[_numStates]; addItemListener(this); } - /** Set the current state */ + /** + * @param inState state to set + */ public void setCurrentState(int inState) { - _currState = inState % 3; + _currState = inState % _numStates; setIcon(_icons[_currState]); setSelected(false); - setSelectedIcon(_icons[(_currState+1)%3]); + setSelectedIcon(_icons[(_currState+1) % _numStates]); } - /** @return current state 0, 1 or 2 */ + /** + * @return current state 0 to n-1 + */ public int getCurrentState() { return _currState; @@ -56,12 +68,12 @@ public class TripleStateCheckBox extends JCheckBox implements ItemListener /** * Set the icon to use for the given index - * @param inIndex index 0, 1 or 2 + * @param inIndex index 0 to n-1 * @param inIcon icon to use for that state */ public void setIcon(int inIndex, ImageIcon inIcon) { - _icons[inIndex % 3] = inIcon; + _icons[inIndex % _numStates] = inIcon; } @Override diff --git a/tim/prune/gui/PhotoThumbnail.java b/tim/prune/gui/PhotoThumbnail.java index 3e8b093..458ee8d 100644 --- a/tim/prune/gui/PhotoThumbnail.java +++ b/tim/prune/gui/PhotoThumbnail.java @@ -129,14 +129,16 @@ public class PhotoThumbnail extends JPanel implements Runnable { if (_inPanel) { - // use either exif thumbnail or photo scaled down to sensible size + _thumbnail = null; + // try to use exif thumbnail if (_photo.getExifThumbnail() != null) { // Use exif thumbnail Image image = new ImageIcon(_photo.getExifThumbnail()).getImage(); _thumbnail = ImageUtils.createScaledImage(image, image.getWidth(null), image.getHeight(null)); image = null; } - else + // Maybe there's no thumbnail, maybe the load of the thumbnail failed + if (_thumbnail == null) { // no exif thumbnail available, going to have to read whole thing int picWidth = _photo.getWidth(); diff --git a/tim/prune/gui/colour/ColourerCaretaker.java b/tim/prune/gui/colour/ColourerCaretaker.java index facbb26..84f1553 100644 --- a/tim/prune/gui/colour/ColourerCaretaker.java +++ b/tim/prune/gui/colour/ColourerCaretaker.java @@ -46,7 +46,7 @@ public class ColourerCaretaker implements DataSubscriber public void dataUpdated(byte inUpdateType) { if ((inUpdateType & - (DataSubscriber.DATA_ADDED_OR_REMOVED | DataSubscriber.DATA_EDITED)) > 0 + (DataSubscriber.DATA_ADDED_OR_REMOVED | DataSubscriber.DATA_EDITED | DataSubscriber.UNITS_CHANGED)) > 0 && _colourer != null) { _colourer.calculateColours(_app.getTrackInfo()); diff --git a/tim/prune/gui/colour/DateColourer.java b/tim/prune/gui/colour/DateColourer.java index 5a6306b..6dfeca6 100644 --- a/tim/prune/gui/colour/DateColourer.java +++ b/tim/prune/gui/colour/DateColourer.java @@ -6,20 +6,23 @@ import java.util.Calendar; import java.util.HashMap; import java.util.TimeZone; +import tim.prune.config.Config; import tim.prune.data.DataPoint; import tim.prune.data.Timestamp; import tim.prune.data.Track; import tim.prune.data.TrackInfo; /** - * Point colourer giving a different colour to each date - * Uses the system timezone so may give funny results for - * data from other timezones (eg far-away holidays) + * Point colourer giving a different colour to each date. + * Uses the currently selected timezone, so the results + * may be different when selecting a different timezone */ public class DateColourer extends DiscretePointColourer { // Doesn't really matter what format is used here, as long as dates are different private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance(); + // Selected timezone for deciding which date a timestamp falls on + private TimeZone _selectedTimezone = null; /** * Constructor @@ -37,8 +40,21 @@ public class DateColourer extends DiscretePointColourer * @param inTrackInfo track info object */ @Override - public void calculateColours(TrackInfo inTrackInfo) + public synchronized void calculateColours(TrackInfo inTrackInfo) { + // Note, this method needs to be synchronized because otherwise the + // Calendar objects in the different threads get confused and the + // wrong colours are generated. + + // TODO: Move this timezone-selection into a helper for use by others? + // Select the current timezone + final String zoneId = Config.getConfigString(Config.KEY_TIMEZONE_ID); + if (zoneId == null || zoneId.equals("")) { + _selectedTimezone = TimeZone.getDefault(); + } + else { + _selectedTimezone = TimeZone.getTimeZone(zoneId); + } // initialise the array to the right size Track track = inTrackInfo == null ? null : inTrackInfo.getTrack(); final int numPoints = track == null ? 0 : track.getNumPoints(); @@ -88,24 +104,25 @@ public class DateColourer extends DiscretePointColourer setColour(i, dayIndex); } } + // generate the colours needed generateDiscreteColours(usedDates.size() + 1); } /** - * Find which date (in the system timezone) the given timestamp falls on + * Find which date (in the currently selected timezone) the given timestamp falls on * @param inTimestamp timestamp * @return String containing description of date, or null */ - private static String getDate(Timestamp inTimestamp) + private String getDate(Timestamp inTimestamp) { if (inTimestamp == null || !inTimestamp.isValid()) { return null; } - Calendar cal = inTimestamp.getCalendar(); - // use system time zone - cal.setTimeZone(TimeZone.getDefault()); + Calendar cal = inTimestamp.getCalendar(null); + // use selected time zone, not system one + DEFAULT_DATE_FORMAT.setTimeZone(_selectedTimezone); return DEFAULT_DATE_FORMAT.format(cal.getTime()); } } diff --git a/tim/prune/gui/images/points_arrows.png b/tim/prune/gui/images/points_arrows.png new file mode 100644 index 0000000000000000000000000000000000000000..4957016856118879f710db3a8260914a530aee80 GIT binary patch literal 649 zcmV;40(Sk0P)NdCGxJ6yQ;-S?S8k$}s}>~_ z70f2w3Dq`&W=3Ulq(u}2L9N|HL{Pa3DuP@Xwy8x*8za#-29yXhz{7A zdmX^7C*Z|qt|5g1&*P=290QXENTs@%{qJj1De?g3$z2@C>j9dBk3 z7D1T!_ybi1l{G|P`K8=XU_c~aH)sH9ZG8|8YHN=iL&{qH0(1e5z#wq{(BZ~Nu(TYV zT^U~8QQ+SmBol>Z;0bUE*!UZwJoZc38#@X7XU{L?mi$ugGRRY)0jNP(sry2`CVF1s zyeQ+q6wre(fyxTPZzR0u`-4-kOOYJMPFm}xjS1eXb<;+8c;H|}09{`Xx`7kBI@pf( zBl)K4K6k0F@00^D{(8E$VIxP&`StM#mJr3Lr4HP|EcKpsITwFFxXXu{&?J jbGw#g2SgE>*-rcc+&cVCs9V4<00000NkvXXu0mjfwkZ+t literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/points_connected.png b/tim/prune/gui/images/points_connected.png index 1891af0b696ca136b4c973e562c79a878e4ea460..b44000c2f8e850ebaa6e156e7f4b71bb5516686e 100644 GIT binary patch delta 492 zcmVU9=kBiNX13}@~v6a~@3MwGJ`QVOC%(nxHyP|-p$a(in)h2X`Q-G3}RVF{S5 z52ktWK4##Js496RQZq%*%o@izo>+a{vn{!}v^4Y8vi~QYbWh*U{%T)gi~o_te^VAp5&ZJOSjS_Y;3zN;RHHcjkQU?+0xI7Sej@f-6iqK~3Ftxu+{?QH^46eGdiHx{079;d8d zGw^1eY|vBroH@4watJWs32+y95GT>5NYyWuym7N_ zFIuh3-vE5Yrhi>#gEP?t_4%heK`Ov?U?EPTkDZasW>a9X^UMd=0-^Ez17-#$O~h`+ zN%Setmc#_|B9dk?~5?I3U;CBnNyXcVQI(VzCfo`s9$%_{&f@MBr@}O+DV*MDC9i}bP*}o_x{I8Cj&3^&iDMd=bUSj zs4`}KPZJ+_6g?<{-p|yEXUE6A6R)HO(z@M~+4I}&#x*xRDu2)7#{iW9!ma~sY^+eL z#X-^y0F#7S-9lQ)jybc!MW5xTy-%`2X~M^1;E0qEZaG( z3^2nM8vb_n;>;4j>1ojhZbC&1i*x-eK`eGU%iifMyMmUI}y)&R2c&c}cD&RgRj#weBiQ`*6K00000Ne4wvM6N<$f`g01SpWb4 delta 413 zcmV;O0b>601AzpPZhwABL_t(Ijg?b9OB`Vsoafm$izf;SakG0M2POxBBwP`&5Yq_u z;s@H?Tr*?^GO05CT8 z4!&<3$szyay<9Gdr$Y!;AdQD*KE;T=&V<1^~&3=y-4(Tpui_Xy>pLnmuH!nq1O8;}(cF><+J4mhj z2na#4E+J2tYNq_VM4JZwS=g^;vKll%#(gb);A7=)xyQf6)DU~?SD2J@O3W`6HG?@iG( zjr-_f*TJf+ALBWG#uPqu0#s#PU=bVH+{Y;<1FEu~#ds`FYBYe-MlbFKgq>9kX>mJ_b{Vm>iG5=K|*I30N zo?r)WVhVpc12SY92Ux=wY{V3PbOy9QFR_mY*p4Y2{SzQVp5hqG_=v5TLTbzrUD%^W`ABBn6kDZxmZb6n$B`@AzCYwEuP?f}C?WE$8hJ;eY3 N002ovPDHLkV1kj4j+y`f delta 302 zcmV+}0nz@T1Cs-gZhsI-L_t(Ijiu5dOG8l@hwvMLE|;-3UiF@GEaHKdEe(e|CduMio$=+WYxwr z$?It1J;dy~5+F_T8dkAW4mWU#;ea&BXR%!l&fpX0I1MrD^ndo&es_>2c>|l+#R>}C z;1pLOW?zFIs})5dP4WXAp^hhAeECB$s0l0l!% zE#B}v=zb(1O(*gx%;RA!02Xw={MdlSvJM3N0P(F`fql;7tN;K207*qoM6N<$g1@JV Awg3PC diff --git a/tim/prune/gui/images/wpicon_default_m.png b/tim/prune/gui/images/wpicon_default_m.png new file mode 100644 index 0000000000000000000000000000000000000000..95fffb4899fc1dbe079cf48731244bae5c7f894d GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaT3?y&uT)!Jgu?6^qxNfs>0W#lQF6{zRj3q&S z!3+-1Zlr-YN#5=*3>~bp9zYIffk$L90|U1(2s1LwngcRTz|+MsgyVX0f&$x>hX-01 Z8Tu5Mz6P8)vjZr{;OXk;vd$@?2><|CA#eZy literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/wpicon_pin_l.png b/tim/prune/gui/images/wpicon_pin_l.png new file mode 100644 index 0000000000000000000000000000000000000000..66354c16bac787f32503b46c72c0ed1b203d9915 GIT binary patch literal 554 zcmV+_0@eMAP))YsS7-QC^d z;^O4wFMd}>gx6N_4fAm_xJbs`1tzz`uqF){QUg={r&&{{{n`w<^TWy0d!JM zQvg8b*k%9#00Cl4M??UK1szBL000SaNLh0L00E!?00E!@`peoN00007bV*G`2jB=2 z69y)V$6BQT007!aL_t(2&wY~FR>D9KL{mMi64}v!%D#huD4>EM^Zy@3CYcjL(nG!8 z?&{UDZbf971msgjit1rq1b9YYQvSX`@UA`SYLF&Ab*1QMiLnNn($oGmZjIVWYK@{@ zi-)ZYRJesCrW(vXWH($DZo#%@yIi6X-jo2~oPGBH1VV_BJzv(pbz&3E}xi!EqAoMIODv4F;+~mGxhim^}9k1gBpHXG(qu saqYnPbS{JWA#v|p#T{nOQ-?O_0z0cDR5t8(qyPW_07*qoM6N<$f}o}G*#H0l literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/wpicon_pin_m.png b/tim/prune/gui/images/wpicon_pin_m.png new file mode 100644 index 0000000000000000000000000000000000000000..86c6c3d1a01c047559a75ae77c63a25373d0c9dc GIT binary patch literal 492 zcmVI1qKEN2L}fT2nZ1o5f>L18X6iM9UUMbAS5ItB_$;(DJeNQ zIXpZ(Jv}`?KR--NOi)lzRaI46TU%peV`XJ!YHDh0Yio6Nb$EDqdwY9;fPjdIh?0_$ znVFfJo134XpP-uz*?Ck9F z^78ZZ^Y-@k`uh6&`}_R-{Qdp?{{H^||No?u$%y~}00DGTPE!Ct=GbNc0004EOGiWi zhy@);00009a7bBm0004A0004A0bguxdH?_bzDYzuR4C7NQpXmja4e5 zi1I@UbS!u}?6D4YO==PvoFks3>ffWKw9xu@hJP}W8T4?Omtv@`?&0Z^IcqWg2em2dPI0=V_0V7ZO* i3idz1L9jm+Hu41~To|27bM^`V0000 zuV24@|Ni~w&!2z){{8pw-+%x9{r~?zzb@9~z{A?i zdL}OFVdQ&MBb@0N6m6*8l(j literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/wpicon_plectrum_l.png b/tim/prune/gui/images/wpicon_plectrum_l.png new file mode 100644 index 0000000000000000000000000000000000000000..a1088838166f10e61988cffcdfed1cd9a182d31d GIT binary patch literal 348 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*s3?yAI>n{UQ0X`wFqDw9Unas@05)u+hN=jN< zT6%hV2?+@q85u=IMdjt?H8nN0wY3cm4O6F1U9x1!%9ShEuV4S+!-p?lzWn<2>-X>9 z|NsA=d^y+@XbNLVkY6x^!?PP{AWo9Ey9+}HtE>l*!&%@FSqv1L1Hz2I8d5lcg6t)p zzOL*KID~}Rt=jLJuLcTD@N{tu(Kz3FDU$E70uM`owe-#J1^*XM+o9?ZyX^DP4=O4= zE`c{%>)PfsTskx7`ggy~XE&#KEuDRL*PCbDOSgTSY<6{bNc-`{K{sxy$MzJR5i00B zKlQ6rLFf(kH=@^;Z{HktqWQR@Z1a?)U3t5fIo-d!RC0T6&Xtmx2cIY&Qa{|^J8{B6 m{%`D@Gs73{5qfI6Qh%-v=ktB$2TXu&WAJqKb6Mw<&;$UbppC}> literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/wpicon_plectrum_m.png b/tim/prune/gui/images/wpicon_plectrum_m.png new file mode 100644 index 0000000000000000000000000000000000000000..1b79ac2ee50d4524b8ab9db80fb4efdfbc4d7611 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAIR*HHxV|~84P>&jvkM3ah>3|w zNl6(S8(UdfSzBA%+S+<}c!Y(8rKhK7W@hH+=T}!(U%Ytn?%lheKY#xA?c49)zyJUL z-)yD_G=YJ!B*-tA!Qt7BG!Q4r+uensgH_f8$l)yTh%5$*C4w;Hr+FXkfPyWaE{-7@ z=X)pK7HUx7U|l%-?&&H2|6g5VbU0~Q?V-(9$_xUIY;g%LoyMhXq0vv<{Lgy1WGl1C zTlx3ApQkpZb*XT)%E59TSD&gwS4+A*yZ(vo6?t0rG+L9jZ@R?%xiVkhIK4?bvrl?O zd+LhK*94~~u5_Ba&ftT8hO`gcl(oz?(m&juSkFplw`w@?R}ttQ22WQ%mvv4FO#q7^ BbBX`} literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/wpicon_plectrum_s.png b/tim/prune/gui/images/wpicon_plectrum_s.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c4bab638ec0f2506439979ddd8fb7c7146fad8 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDU1=2vk$jHdT!Xh9b;N#;H z92^`P8d_alJ!Q(2y?ghbJ9qBt)vMR8UHkRx*Z=?jr(GAG2+~jzECyCqR(5uFO-;?l z#>O*e&Ro5E_2($o)N;8%O`2{mLJiCzw;v{*yyD)UH%6b4foCO|{#S9G0 zK*O137`<2M-USM>mw5WRvOnMu66TP&s+!dR6e{p^aSV~ToSdM*BLTEbz{b(QVG=VV zqlo7W6XhOlhUp?fEuPIPzQ(tj7G82#=kfH+%*(P5V?NJx2;h0hB~bp9zYIffk$L9P;5O2Gxo5SSOEpu zOFVsD*&lES35uwn{UR6#+gWt}UyV1DTABj7&^S%*@QJ ztgJjdJiNTTe0+RjVq$V~a=N;@hK7blMn=ZQ#%5+_mX?+wAt9NWnc3OdIXOAS#l@wi zrIRL2TC`}<;lqcI9656I=+X1%&tJZL`RdiHH*VaxdGqG$*RS8cef#t0&tJcO{r&s* z-@kwV|Nno#L8KdGaY>M0FoVOh8)+a;lDE4HLkFv@2av;A;1O926#E0hjMF9Xgwq@;K1&u4)l^)E{ z40a3D4;EbWV%Ohv70y#9j=5YEl5TB1WP0E>N7sCJ#zN^tQpG7&i8L!`S?%R z_U*RU&P|@5`~CUhJiC3yF85oj;+X;)PL{J6^nPGxng4U*S0?2(;@nF@Z^Z%~%;4$j K=d#Wzp$P!en62;t literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/wpicon_ringpt_m.png b/tim/prune/gui/images/wpicon_ringpt_m.png new file mode 100644 index 0000000000000000000000000000000000000000..fabfe84356014ed4f23d65ab6d0524fe9421035a GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAIR*HHxGq;I1TvYJm^e8(MMXt5 zG&Hodv^+gMi;IiP%gZY(E32!kySuyBu3fus-MYPd_Z~fZ^xU~~KYsl9`Sa)R-@pI= z{~vV9ryFPlV@Z%-FoVOh8)+a;lDE4HLkFv@2av;A;1O926k7wrjEZ|g!-0bJo-U3d z8t3Ouv=nPl;9+H6#L=OB=l`l>%Y_ytJPbKGUya+rN#TQ&N|vR$`~ArGxyO0UF69W` z>^Z}G%`EPQPr}W0+asclB6iLw@aa);Q&STX7G~~j^?{C4SPRb xU|O75pX#heQfdElU>%{Y%E?yFvm2iAd{JynS+Calao_Y zQc_u2xvHwFuC8wKhZ8~t^z@bBj&YU@O>C&Zl@7{g=`t|?+|DV_0n*h|tSQ6wH z%;50sMjD8dsIl8_h}y(9LBYL0kWT&s`2Ur}vik)`!lIi_QMYoA|O*zxbExw(}nb9&ZNWss*C NJYD@<);T3K0RV!1T|EE* literal 0 HcmV?d00001 diff --git a/tim/prune/gui/map/MapCanvas.java b/tim/prune/gui/map/MapCanvas.java index 5711951..801adbb 100644 --- a/tim/prune/gui/map/MapCanvas.java +++ b/tim/prune/gui/map/MapCanvas.java @@ -1,39 +1,10 @@ package tim.prune.gui.map; -import java.awt.AlphaComposite; -import java.awt.BasicStroke; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.RenderingHints; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; +import java.awt.*; +import java.awt.event.*; import java.awt.image.BufferedImage; -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSlider; -import javax.swing.SwingUtilities; +import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -44,23 +15,12 @@ import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.ColourScheme; import tim.prune.config.Config; -import tim.prune.data.Checker; -import tim.prune.data.Coordinate; -import tim.prune.data.DataPoint; -import tim.prune.data.DoubleRange; -import tim.prune.data.Field; -import tim.prune.data.FieldList; -import tim.prune.data.Latitude; -import tim.prune.data.Longitude; -import tim.prune.data.MidpointData; -import tim.prune.data.Selection; -import tim.prune.data.Track; -import tim.prune.data.TrackInfo; +import tim.prune.data.*; import tim.prune.function.compress.MarkPointsInRectangleFunction; import tim.prune.function.edit.FieldEdit; import tim.prune.function.edit.FieldEditList; import tim.prune.gui.IconManager; -import tim.prune.gui.TripleStateCheckBox; +import tim.prune.gui.MultiStateCheckBox; import tim.prune.gui.colour.PointColourer; import tim.prune.tips.TipManager; @@ -99,7 +59,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe /** Checkbox for autopan */ private JCheckBox _autopanCheckBox = null; /** Checkbox for connecting track points */ - private TripleStateCheckBox _connectCheckBox = null; + private MultiStateCheckBox _connectCheckBox = null; /** Checkbox for enable edit mode */ private JCheckBox _editmodeCheckBox = null; /** Right-click popup menu */ @@ -128,6 +88,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe private boolean _shownOsmErrorAlready = false; /** Current drawing mode */ private int _drawMode = MODE_DEFAULT; + /** Current waypoint icon definition */ + WpIconDefinition _waypointIconDefinition = null; /** Constant for click sensitivity when selecting nearest point */ private static final int CLICK_SENSITIVITY = 10; @@ -243,10 +205,11 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe _autopanCheckBox.setFocusable(false); // stop button from stealing keyboard focus _topPanel.add(_autopanCheckBox); // Add checkbox button for connecting points or not - _connectCheckBox = new TripleStateCheckBox(); - _connectCheckBox.setIcon(0, IconManager.getImageIcon(IconManager.POINTS_CONNECTED_BUTTON)); - _connectCheckBox.setIcon(1, IconManager.getImageIcon(IconManager.POINTS_DISCONNECTED_BUTTON)); - _connectCheckBox.setIcon(2, IconManager.getImageIcon(IconManager.POINTS_HIDDEN_BUTTON)); + _connectCheckBox = new MultiStateCheckBox(4); + _connectCheckBox.setIcon(0, IconManager.getImageIcon(IconManager.POINTS_WITH_ARROWS_BUTTON)); + _connectCheckBox.setIcon(1, IconManager.getImageIcon(IconManager.POINTS_HIDDEN_BUTTON)); + _connectCheckBox.setIcon(2, IconManager.getImageIcon(IconManager.POINTS_CONNECTED_BUTTON)); + _connectCheckBox.setIcon(3, IconManager.getImageIcon(IconManager.POINTS_DISCONNECTED_BUTTON)); _connectCheckBox.setCurrentState(0); _connectCheckBox.setOpaque(false); _connectCheckBox.setToolTipText(I18nManager.getText("menu.map.connect")); @@ -305,6 +268,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe makePopup(); // Get currently selected map from Config, pass to MapTileManager _tileManager.setMapSource(Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX)); + // Update display settings + dataUpdated(MAPSERVER_CHANGED); } @@ -409,7 +374,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe } // Draw the map contents if necessary - if ((_mapImage == null || _recalculate)) + if (_mapImage == null || _recalculate) { paintMapContents(); _scaleBar.updateScale(_mapPosition.getZoom(), _mapPosition.getYFromPixels(0, 0)); @@ -601,7 +566,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe if (trackOpacity > 0.0f) { // Paint the track points on top - int pointsPainted = 1; + boolean pointsPainted = true; try { if (trackOpacity > 0.9f) @@ -632,7 +597,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe catch (ArrayIndexOutOfBoundsException obe) {} // also ignore // Zoom to fit if no points found - if (pointsPainted <= 0 && _checkBounds) + if (!pointsPainted && _checkBounds) { zoomToFit(); _recalculate = true; @@ -652,9 +617,9 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe /** * Paint the points using the given graphics object * @param inG Graphics object to use for painting - * @return number of points painted, if any + * @return true if any points or lines painted */ - private int paintPoints(Graphics inG) + private boolean paintPoints(Graphics inG) { // Set up colours final ColourScheme cs = Config.getColourScheme(); @@ -674,6 +639,9 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe final int[] xPixels = new int[numPoints]; final int[] yPixels = new int[numPoints]; + final int pointSeparationForArrowsSqd = 350; + final int pointSeparation1dForArrows = (int) (Math.sqrt(pointSeparationForArrowsSqd) * 0.7); + // try to set line width for painting if (inG instanceof Graphics2D) { @@ -681,16 +649,20 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe if (lineWidth < 1 || lineWidth > 4) {lineWidth = 2;} ((Graphics2D) inG).setStroke(new BasicStroke(lineWidth)); } - int pointsPainted = 0; + + boolean pointsPainted = false; // draw track points inG.setColor(pointColour); int prevX = -1, prevY = -1; final int connectState = _connectCheckBox.getCurrentState(); - final boolean drawLines = (connectState % 2) == 0; // 0 or 2 - final boolean drawPoints = (connectState <= 1); // 0 or 1 + final boolean drawLines = (connectState != 3); // 0, 1 or 2 + final boolean drawPoints = (connectState != 1); // 0, 2 or 3 + final boolean drawArrows = (connectState == 0); // 0 + boolean prevPointVisible = false, currPointVisible = false; boolean anyWaypoints = false; boolean isWaypoint = false; + boolean drawnLastArrow = false; // avoid painting arrows on adjacent lines, looks too busy for (int i=0; i pointSeparation1dForArrows || Math.abs(prevY-py) > pointSeparation1dForArrows)) + { + final double pointSeparationSqd = (prevX-px) * (prevX-px) + (prevY-py) * (prevY-py); + if (pointSeparationSqd > pointSeparationForArrowsSqd) + { + final double midX = (prevX + px) / 2; + final double midY = (prevY + py) / 2; + final boolean midPointVisible = midX >= 0 && midX < winWidth && midY >= 0 && midY < winHeight; + if (midPointVisible) + { + final double alpha = Math.atan2(py - prevY, px - prevX); + //System.out.println("Draw arrow from (" + prevX + "," + prevY + ") to (" + px + "," + py + // + ") with angle" + (int) (alpha * 180/Math.PI)); + final double MID_TO_VERTEX = 3.0; + final double arrowX = MID_TO_VERTEX * Math.cos(alpha); + final double arrowY = MID_TO_VERTEX * Math.sin(alpha); + final double vertexX = midX + arrowX; + final double vertexY = midY + arrowY; + inG.drawLine((int)(midX-arrowX-2*arrowY), (int)(midY-arrowY+2*arrowX), (int)vertexX, (int)vertexY); + inG.drawLine((int)(midX-arrowX+2*arrowY), (int)(midY-arrowY-2*arrowX), (int)vertexX, (int)vertexY); + } + drawnLastArrow = midPointVisible; + } + } + else + { + drawnLastArrow = false; + } } prevX = px; prevY = py; } @@ -755,8 +764,20 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe int py = yPixels[i]; if (px >= 0 && px < winWidth && py >= 0 && py < winHeight) { - inG.fillRect(px-3, py-3, 6, 6); - pointsPainted++; + if (_waypointIconDefinition == null) + { + inG.fillRect(px-3, py-3, 6, 6); + } + else + { + ImageIcon icon = _waypointIconDefinition.getImageIcon(); + if (icon != null) + { + inG.drawImage(icon.getImage(), px-_waypointIconDefinition.getXOffset(), + py-_waypointIconDefinition.getYOffset(), null); + } + } + pointsPainted = true; numWaypoints++; } } @@ -820,7 +841,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe { inG.drawRect(px-1, py-1, 2, 2); inG.drawRect(px-2, py-2, 4, 4); - pointsPainted++; + pointsPainted = true; } } } @@ -1354,9 +1375,20 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe if ((inUpdateType & DataSubscriber.DATA_ADDED_OR_REMOVED) > 0) { _checkBounds = true; } - if ((inUpdateType & DataSubscriber.MAPSERVER_CHANGED) > 0) { + if ((inUpdateType & DataSubscriber.MAPSERVER_CHANGED) > 0) + { // Get the selected map source index and pass to tile manager _tileManager.setMapSource(Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX)); + final int wpType = Config.getConfigInt(Config.KEY_WAYPOINT_ICONS); + if (wpType == WpIconLibrary.WAYPT_DEFAULT) + { + _waypointIconDefinition = null; + } + else + { + final int wpSize = Config.getConfigInt(Config.KEY_WAYPOINT_ICON_SIZE); + _waypointIconDefinition = WpIconLibrary.getIconDefinition(wpType, wpSize); + } } if ((inUpdateType & (DataSubscriber.DATA_ADDED_OR_REMOVED + DataSubscriber.DATA_EDITED)) > 0) { _midpoints.updateData(_track); diff --git a/tim/prune/gui/map/MapSourceLibrary.java b/tim/prune/gui/map/MapSourceLibrary.java index e324af1..5f44d5e 100644 --- a/tim/prune/gui/map/MapSourceLibrary.java +++ b/tim/prune/gui/map/MapSourceLibrary.java @@ -38,14 +38,20 @@ public abstract class MapSourceLibrary */ private static void addFixedSources() { + final String THUNDERFOREST_APIKEY = "c32212f10b13496786b98dc6c42e5c3c"; _sourceList.add(new OsmMapSource("Mapnik", "http://[abc].tile.openstreetmap.org/")); - _sourceList.add(new OsmMapSource("Cyclemap", "http://[abc].tile.opencyclemap.org/cycle/")); + OsmMapSource cycleSource = new OsmMapSource("OpenCycleMap", "http://[abc].tile.thunderforest.com/cycle/"); + cycleSource.setApiKey(THUNDERFOREST_APIKEY); + _sourceList.add(cycleSource); + OsmMapSource outdoorsSource = new OsmMapSource("Outdoors", "http://[abc].tile.thunderforest.com/outdoors/"); + outdoorsSource.setApiKey(THUNDERFOREST_APIKEY); + _sourceList.add(outdoorsSource); _sourceList.add(new OsmMapSource("Reitkarte", "http://topo[234].wanderreitkarte.de/topo/")); _sourceList.add(new MffMapSource("Mapsforfree", "http://maps-for-free.com/layer/relief/", "jpg", "http://maps-for-free.com/layer/water/", "gif", 11)); _sourceList.add(new OsmMapSource("Hikebikemap", "http://[abc].tiles.wmflabs.org/hikebike/", "http://[abc].tiles.wmflabs.org/hillshading/", 18)); - _sourceList.add(new OsmMapSource("Openseamap", "http://tile.openstreetmap.org/", + _sourceList.add(new OsmMapSource("OpenSeaMap", "http://tile.openstreetmap.org/", "http://tiles.openseamap.org/seamark/", 18)); } diff --git a/tim/prune/gui/map/OsmMapSource.java b/tim/prune/gui/map/OsmMapSource.java index 070bb25..925fcf6 100644 --- a/tim/prune/gui/map/OsmMapSource.java +++ b/tim/prune/gui/map/OsmMapSource.java @@ -19,6 +19,9 @@ public class OsmMapSource extends MapSource private String[] _siteNames = null; /** Maximum zoom level */ private int _maxZoom = 0; + /** API key, usually remains empty */ + private String _apiKey = null; + /** * Constructor giving single name and url @@ -93,6 +96,12 @@ public class OsmMapSource extends MapSource _maxZoom = inMaxZoom; } + /** Set the API key (if required) */ + public void setApiKey(String inKey) + { + _apiKey = inKey; + } + /** * Construct a new map source from its config string * @param inConfigString string from Config, separated by semicolons @@ -147,8 +156,15 @@ public class OsmMapSource extends MapSource public String makeURL(int inLayerNum, int inZoom, int inX, int inY) { // Check if the base url has a [1234], if so replace at random - return pickServerUrl(_baseUrls[inLayerNum]) - + inZoom + "/" + inX + "/" + inY + "." + getFileExtension(inLayerNum); + StringBuffer url = new StringBuffer(); + url.append(pickServerUrl(_baseUrls[inLayerNum])); + url.append(inZoom).append('/').append(inX).append('/').append(inY); + url.append('.').append(getFileExtension(inLayerNum)); + if (_apiKey != null) + { + url.append("?apikey=").append(_apiKey); + } + return url.toString(); } /** diff --git a/tim/prune/gui/map/WpIconDefinition.java b/tim/prune/gui/map/WpIconDefinition.java new file mode 100644 index 0000000..36e06f1 --- /dev/null +++ b/tim/prune/gui/map/WpIconDefinition.java @@ -0,0 +1,48 @@ +package tim.prune.gui.map; + +import javax.swing.ImageIcon; + +/** + * Definition of a waypoint icon including name and offsets + */ +public class WpIconDefinition +{ + /** Name of icon, used for finding image file */ + private final String _name; + /** X offset of marker point in image */ + private final int _xOffset; + /** Y offset of marker point in image */ + private final int _yOffset; + /** icon */ + private ImageIcon _icon = null; + + + /** + * Constructor + * @param inName name of icon + * @param inX x offset + * @param inY y offset + */ + public WpIconDefinition(String inName, int inX, int inY) + { + _name = inName; + _xOffset = inX; + _yOffset = inY; + } + + /** @return name of icon */ + public String getName() {return _name;} + /** @return x offset */ + public int getXOffset() {return _xOffset;} + /** @return y offset */ + public int getYOffset() {return _yOffset;} + + /** @param inIcon icon to set */ + public void setIcon(ImageIcon inIcon) {_icon = inIcon;} + + /** @return image icon to display */ + public ImageIcon getImageIcon() + { + return _icon; + } +} diff --git a/tim/prune/gui/map/WpIconLibrary.java b/tim/prune/gui/map/WpIconLibrary.java new file mode 100644 index 0000000..e391d91 --- /dev/null +++ b/tim/prune/gui/map/WpIconLibrary.java @@ -0,0 +1,92 @@ +package tim.prune.gui.map; + +import javax.swing.ImageIcon; + +import tim.prune.gui.IconManager; + +/** + * Class to provide a library of waypoint icon definitions + */ +public abstract class WpIconLibrary +{ + /** Types of waypoint */ + public static final int WAYPT_DEFAULT = 0; + public static final int WAYPT_RING_POINT = 1; + public static final int WAYPT_PLECTRUM = 2; + public static final int WAYPT_CIRCLE = 3; + public static final int WAYPT_PIN = 4; + public static final int WAYPT_NUMBER_OF_ICONS = WAYPT_PIN + 1; + + /** Sizes of icon */ + public static final int SIZE_SMALL = 0; + public static final int SIZE_MEDIUM = 1; + public static final int SIZE_LARGE = 2; + + /** Array of x and y offsets for the icons */ + private static int[] _PIXEL_OFFSETS = null; + + /** Static block to initialise offsets */ + static + { + _PIXEL_OFFSETS = new int[] {0, 0, 0, 0, 0, 0, // default + 8, 13, 12, 22, 14, 26, // ringpt + 7, 15, 12, 24, 14, 27, // plectrum + 8, 8, 12, 12, 14, 14, // ring + 2, 15, 4, 23, 4, 27 // pin + }; + } + + /** @return array of Integers representing waypoint types */ + public static Integer[] getWaypointTypes() + { + return new Integer[] {WAYPT_DEFAULT, WAYPT_RING_POINT, WAYPT_PLECTRUM, WAYPT_CIRCLE, WAYPT_PIN}; + } + + /** + * @param inType icon type + * @return the name of the specified icon, used for settings dialog + */ + public static String getIconName(int inType) + { + switch (inType) + { + case WAYPT_RING_POINT: return "ringpt"; + case WAYPT_PLECTRUM: return "plectrum"; + case WAYPT_CIRCLE: return "ring"; + case WAYPT_PIN: return "pin"; + case WAYPT_DEFAULT: + default: return "default"; + } + } + + /** + * @param inType icon type + * @param inSize icon size (small/medium/large) + * @return icon definition for the specified icon + */ + public static WpIconDefinition getIconDefinition(int inType, int inSize) + { + String iconName = getIconName(inType); + String sizeSuffix = null; + switch (inSize) + { + case SIZE_SMALL: sizeSuffix = "_s"; break; + case SIZE_MEDIUM: sizeSuffix = "_m"; break; + case SIZE_LARGE: sizeSuffix = "_l"; break; + default: sizeSuffix = "_m"; inSize = SIZE_MEDIUM; break; + } + // Look up offsets in the static array + int xOffset = 0, yOffset = 0; + try { + xOffset = _PIXEL_OFFSETS[inType * 6 + inSize * 2]; + yOffset = _PIXEL_OFFSETS[inType * 6 + inSize * 2 + 1]; + } + catch (ArrayIndexOutOfBoundsException obe) {} // ignore, leave offsets at 0 + WpIconDefinition iconDef = new WpIconDefinition(iconName, xOffset, yOffset); + // Get icon + ImageIcon icon = IconManager.getImageIcon(IconManager.WAYPOINT_ICON_PREFIX + + iconDef.getName() + sizeSuffix + IconManager.WAYPOINT_ICON_SUFFIX); + iconDef.setIcon(icon); + return iconDef; + } +} diff --git a/tim/prune/jpeg/ExifGateway.java b/tim/prune/jpeg/ExifGateway.java deleted file mode 100644 index c11ba2e..0000000 --- a/tim/prune/jpeg/ExifGateway.java +++ /dev/null @@ -1,108 +0,0 @@ -package tim.prune.jpeg; - -import java.io.File; -import javax.swing.JOptionPane; -import tim.prune.I18nManager; - -/** - * Skeleton gateway to the Exif functions. - * This is required by Debian to divert Exif handling - * to the external libmetadata-extractor-java library - * instead of the included modified routines. - * - * Switching between internal and external libraries is - * handled by the ExifLibrarySwitch - */ -public abstract class ExifGateway -{ - /** Library object to call */ - private static ExifLibrary _exifLibrary = null; - /** Flag to set whether failure warning has already been shown */ - private static boolean _exifFailWarned = false; - - /** Static block to initialise library */ - static - { - String libraryClass = ExifLibrarySwitch.USE_INTERNAL_LIBRARY?"InternalExifLibrary":"ExternalExifLibrary"; - try - { - _exifLibrary = (ExifLibrary) Class.forName("tim.prune.jpeg." + libraryClass).newInstance(); - } - catch (Throwable nolib) {_exifLibrary = null;} - } - - - /** - * Get the Jpeg data from the given file - * @param inFile file to read - * @return jpeg data, or null if none found - */ - public static JpegData getJpegData(File inFile) - { - try - { - // Call library (if found) - if (_exifLibrary != null) { - JpegData data = _exifLibrary.getJpegData(inFile); - return data; - } - } - catch (LinkageError nolib) { - System.err.println("Link: " + nolib.getMessage()); - nolib.printStackTrace(); - } - // Not successful - warn if necessary - if (!_exifFailWarned) - { - JOptionPane.showMessageDialog(null, I18nManager.getText("error.jpegload.exifreadfailed"), - I18nManager.getText("error.jpegload.dialogtitle"), JOptionPane.WARNING_MESSAGE); - _exifFailWarned = true; - } - return null; - } - - /** - * @return key to use to describe library, matching key for about dialog - */ - public static String getDescriptionKey() - { - String key = ExifLibrarySwitch.USE_INTERNAL_LIBRARY?"internal":"external"; - if (_exifLibrary == null || !_exifLibrary.looksOK()) {key = key + ".failed";} - return key; - } - - - /** - * @param inNumerator numerator from Rational - * @param inDenominator denominator from Rational - * @return the value of the specified number as a positive double. - * Prevents interpretation of 32 bit numbers as negative, and forces a positive answer - */ - public static final double convertToPositiveValue(int inNumerator, int inDenominator) - { - if (inDenominator == 0) return 0.0; - double numeratorDbl = inNumerator; - double denomDbl = inDenominator; - if (inNumerator >= 0) - return numeratorDbl / denomDbl; - final double correction = Math.pow(2.0, 32); - numeratorDbl += correction; - if (inDenominator < 0) denomDbl += correction; - return numeratorDbl / denomDbl; - } - - - /** - * @param inNumerator numerator from Rational - * @param inDenominator denominator from Rational - * @return the value of the specified number as a positive double. - * Forces a positive answer - */ - public static final double convertToPositiveValue(long inNumerator, long inDenominator) - { - if (inDenominator == 0L) return 0.0; - final double numeratorDbl = inNumerator; - final double denomDbl = inDenominator; - return numeratorDbl / denomDbl; - } -} diff --git a/tim/prune/jpeg/ExifLibrary.java b/tim/prune/jpeg/ExifLibrary.java deleted file mode 100644 index 8d00617..0000000 --- a/tim/prune/jpeg/ExifLibrary.java +++ /dev/null @@ -1,15 +0,0 @@ -package tim.prune.jpeg; - -import java.io.File; - -/** - * Interface satisfied by both internal and external Exif implementations - */ -public interface ExifLibrary -{ - /** Get the Jpeg data from the given file */ - public JpegData getJpegData(File inFile); - - /** Check that dependencies are resolved */ - public boolean looksOK(); -} diff --git a/tim/prune/jpeg/ExifLibrarySwitch.java b/tim/prune/jpeg/ExifLibrarySwitch.java deleted file mode 100644 index 01adce1..0000000 --- a/tim/prune/jpeg/ExifLibrarySwitch.java +++ /dev/null @@ -1,10 +0,0 @@ -package tim.prune.jpeg; - -/** - * Empty class just to provide a switch between internal and external libraries - */ -public abstract class ExifLibrarySwitch -{ - /** Flag to specify internal or external library */ - static final boolean USE_INTERNAL_LIBRARY = true; -} diff --git a/tim/prune/jpeg/ExternalExifLibrary.java b/tim/prune/jpeg/ExternalExifLibrary.java deleted file mode 100644 index 4d06a27..0000000 --- a/tim/prune/jpeg/ExternalExifLibrary.java +++ /dev/null @@ -1,150 +0,0 @@ -package tim.prune.jpeg; - -import java.io.File; - -import com.drew.imaging.ImageMetadataReader; -import com.drew.lang.Rational; -import com.drew.metadata.Directory; -import com.drew.metadata.Metadata; -import com.drew.metadata.exif.ExifSubIFDDirectory; -import com.drew.metadata.exif.ExifIFD0Directory; -import com.drew.metadata.exif.ExifReader; -import com.drew.metadata.exif.ExifThumbnailDirectory; -import com.drew.metadata.exif.GpsDirectory; - -/** - * Class to act as a gateway into the external exif library functions. - * This should be the only class with dependence on the lib-metadata-extractor-java - * classes (which are NOT delivered with GpsPrune). - * This class will not compile without this extra dependency (but is not required if - * the ExifGateway uses the InternalExifLibrary instead). - * Should not be included if the internal library will be used (from jpeg.drew package). - */ -public class ExternalExifLibrary implements ExifLibrary -{ - /** - * Use the _external_ exif library to get the data from the given file - * @param inFile file to access - * @return Jpeg data if available, otherwise null - */ - public JpegData getJpegData(File inFile) - { - JpegData data = new JpegData(); - // Read exif data from picture - try - { - Metadata metadata = ImageMetadataReader.readMetadata(inFile); - if (metadata.containsDirectory(GpsDirectory.class)) - { - Directory gpsdir = metadata.getDirectory(GpsDirectory.class); - if (gpsdir.containsTag(GpsDirectory.TAG_LATITUDE) - && gpsdir.containsTag(GpsDirectory.TAG_LONGITUDE) - && gpsdir.containsTag(GpsDirectory.TAG_LATITUDE_REF) - && gpsdir.containsTag(GpsDirectory.TAG_LONGITUDE_REF)) - { - data.setLatitudeRef(gpsdir.getString(GpsDirectory.TAG_LATITUDE_REF)); - Rational[] latRats = gpsdir.getRationalArray(GpsDirectory.TAG_LATITUDE); - double seconds = ExifGateway.convertToPositiveValue(latRats[2].getNumerator(), latRats[2].getDenominator()); - data.setLatitude(new double[] {latRats[0].doubleValue(), - latRats[1].doubleValue(), seconds}); - data.setLongitudeRef(gpsdir.getString(GpsDirectory.TAG_LONGITUDE_REF)); - Rational[] lonRats = gpsdir.getRationalArray(GpsDirectory.TAG_LONGITUDE); - seconds = ExifGateway.convertToPositiveValue(lonRats[2].getNumerator(), lonRats[2].getDenominator()); - data.setLongitude(new double[] {lonRats[0].doubleValue(), - lonRats[1].doubleValue(), seconds}); - } - - // Altitude (if present) - if (gpsdir.containsTag(GpsDirectory.TAG_ALTITUDE) && gpsdir.containsTag(GpsDirectory.TAG_ALTITUDE_REF)) - { - data.setAltitude(gpsdir.getRational(GpsDirectory.TAG_ALTITUDE).intValue()); - byte altRef = (byte) gpsdir.getInt(GpsDirectory.TAG_ALTITUDE_REF); - data.setAltitudeRef(altRef); - } - - // Timestamp and datestamp (if present) - final int TAG_DATESTAMP = 0x001d; - if (gpsdir.containsTag(GpsDirectory.TAG_TIME_STAMP) && gpsdir.containsTag(TAG_DATESTAMP)) - { - Rational[] times = gpsdir.getRationalArray(GpsDirectory.TAG_TIME_STAMP); - data.setGpsTimestamp(new int[] {times[0].intValue(), times[1].intValue(), - times[2].intValue()}); - Rational[] dates = gpsdir.getRationalArray(TAG_DATESTAMP); - if (dates != null) { - data.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()}); - } - } - - // Image bearing (if present) - if (gpsdir.containsTag(GpsDirectory.TAG_IMG_DIRECTION) && gpsdir.containsTag(GpsDirectory.TAG_IMG_DIRECTION_REF)) - { - Rational bearing = gpsdir.getRational(GpsDirectory.TAG_IMG_DIRECTION); - if (bearing != null) { - data.setBearing(bearing.doubleValue()); - } - } - } - - // Tags from Exif directory - if (metadata.containsDirectory(ExifSubIFDDirectory.class)) - { - Directory exifdir = metadata.getDirectory(ExifSubIFDDirectory.class); - - // Take time and date from exif tags - if (exifdir.containsTag(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL)) { - data.setOriginalTimestamp(exifdir.getString(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL)); - } - // Also take "digitized" timestamp - if (exifdir.containsTag(ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED)) { - data.setDigitizedTimestamp(exifdir.getString(ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED)); - } - } - if (metadata.containsDirectory(ExifIFD0Directory.class)) - { - Directory exifdir = metadata.getDirectory(ExifIFD0Directory.class); - - // Photo rotation code - if (exifdir.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) { - data.setOrientationCode(exifdir.getInt(ExifIFD0Directory.TAG_ORIENTATION)); - // NOTE: this presumably takes the _last_ orientation value found, not the first. - } - } - - if (metadata.containsDirectory(ExifThumbnailDirectory.class)) - { - ExifThumbnailDirectory exifdir = metadata.getDirectory(ExifThumbnailDirectory.class); - - // TODO: Check this thumbnail stuff - if (exifdir.hasThumbnailData()) - { - // Make a copy of the byte data - byte[] tdata = exifdir.getThumbnailData(); - byte[] thumb = new byte[tdata.length]; - System.arraycopy(tdata, 0, thumb, 0, tdata.length); - data.setThumbnailImage(thumb); - } - } - - } - catch (Exception e) { - // Exception reading metadata, just ignore it - //System.err.println("Error: " + e.getClass().getName() + " - " + e.getMessage()); - } - return data; - } - - - /** - * Check whether the exifreader class can be correctly resolved - * @return true if it looks ok - */ - public boolean looksOK() - { - try { - String test = ExifReader.class.getName(); - if (test != null) return true; - } - catch (LinkageError le) {} - return false; - } -} diff --git a/tim/prune/jpeg/InternalExifLibrary.java b/tim/prune/jpeg/InternalExifLibrary.java index c8b1708..377155f 100644 --- a/tim/prune/jpeg/InternalExifLibrary.java +++ b/tim/prune/jpeg/InternalExifLibrary.java @@ -1,15 +1,15 @@ package tim.prune.jpeg; import java.io.File; + import tim.prune.jpeg.drew.ExifReader; -import tim.prune.jpeg.drew.JpegException; +import tim.prune.jpeg.drew.ExifException; /** - * Class to act as a gateway into the internal exif library functions. + * Class to act as an entry point to the internal exif library functions. * This should be the only class with dependence on the jpeg.drew package. - * Should not be included if external library will be used (eg Debian). */ -public class InternalExifLibrary implements ExifLibrary +public class InternalExifLibrary { /** * Use the _internal_ exif library to get the data from the given file @@ -20,23 +20,9 @@ public class InternalExifLibrary implements ExifLibrary { JpegData data = null; try { - data = new ExifReader(inFile).extract(); + data = ExifReader.readMetadata(inFile); } - catch (JpegException jpe) {} // data remains null + catch (ExifException jpe) {} // data remains null return data; } - - /** - * Check whether the exifreader class can be correctly resolved - * @return true if it looks ok - */ - public boolean looksOK() - { - try { - String test = ExifReader.class.getName(); - if (test != null) return true; - } - catch (LinkageError le) {} - return false; - } } diff --git a/tim/prune/jpeg/drew/ByteArrayReader.java b/tim/prune/jpeg/drew/ByteArrayReader.java new file mode 100644 index 0000000..3b3d010 --- /dev/null +++ b/tim/prune/jpeg/drew/ByteArrayReader.java @@ -0,0 +1,224 @@ +/* + * Copyright 2002-2015 Drew Noakes + * + * More information about this project is available at: + * + * https://drewnoakes.com/code/exif/ + * https://github.com/drewnoakes/metadata-extractor + */ + +package tim.prune.jpeg.drew; + +import java.io.IOException; + + +/** + * Provides methods to read specific values from a byte array, + * with a consistent, checked exception structure for issues. + * + * @author Drew Noakes https://drewnoakes.com + */ +public class ByteArrayReader +{ + private final byte[] _buffer; + private boolean _isMotorolaByteOrder = true; + + public ByteArrayReader(byte[] buffer) + { + if (buffer == null) + throw new NullPointerException(); + + _buffer = buffer; + } + + public void setMotorolaByteOrder(boolean motorolaByteOrder) + { + _isMotorolaByteOrder = motorolaByteOrder; + } + + public long getLength() + { + return _buffer.length; + } + + protected byte getByte(int index) + { + return _buffer[index]; + } + + protected void validateIndex(int index, int bytesRequested) throws ExifException + { + if (!isValidIndex(index, bytesRequested)) + throw new ExifException("Invalid index " + index); + } + + private boolean isValidIndex(int index, int bytesRequested) + { + return bytesRequested >= 0 + && index >= 0 + && ((long)index + (long)bytesRequested) <= (long)_buffer.length; + } + + public byte[] getBytes(int index, int count) throws ExifException + { + validateIndex(index, count); + + byte[] bytes = new byte[count]; + System.arraycopy(_buffer, index, bytes, 0, count); + return bytes; + } + + /** + * Returns an unsigned 8-bit int calculated from one byte of data at the specified index. + * + * @param index position within the data buffer to read byte + * @return the 8 bit int value, between 0 and 255 + * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative + */ + public short getUInt8(int index) throws ExifException + { + validateIndex(index, 1); + + return (short) (getByte(index) & 0xFF); + } + + /** + * Returns a signed 8-bit int calculated from one byte of data at the specified index. + * + * @param index position within the data buffer to read byte + * @return the 8 bit int value, between 0x00 and 0xFF + * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative + */ + public byte getInt8(int index) throws ExifException + { + validateIndex(index, 1); + + return getByte(index); + } + + /** + * Returns an unsigned 16-bit int calculated from two bytes of data at the specified index. + * + * @param index position within the data buffer to read first byte + * @return the 16 bit int value, between 0x0000 and 0xFFFF + * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative + */ + public int getUInt16(int index) throws ExifException + { + validateIndex(index, 2); + + if (_isMotorolaByteOrder) { + // Motorola - MSB first + return (getByte(index ) << 8 & 0xFF00) | + (getByte(index + 1) & 0xFF); + } else { + // Intel ordering - LSB first + return (getByte(index + 1) << 8 & 0xFF00) | + (getByte(index ) & 0xFF); + } + } + + /** + * Returns a signed 16-bit int calculated from two bytes of data at the specified index (MSB, LSB). + * + * @param index position within the data buffer to read first byte + * @return the 16 bit int value, between 0x0000 and 0xFFFF + * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative + */ + public short getInt16(int index) throws ExifException + { + validateIndex(index, 2); + + if (_isMotorolaByteOrder) { + // Motorola - MSB first + return (short) (((short)getByte(index ) << 8 & (short)0xFF00) | + ((short)getByte(index + 1) & (short)0xFF)); + } else { + // Intel ordering - LSB first + return (short) (((short)getByte(index + 1) << 8 & (short)0xFF00) | + ((short)getByte(index ) & (short)0xFF)); + } + } + + /** + * Get a 32-bit unsigned integer from the buffer, returning it as a long. + * + * @param index position within the data buffer to read first byte + * @return the unsigned 32-bit int value as a long, between 0x00000000 and 0xFFFFFFFF + * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative + */ + public long getUInt32(int index) throws ExifException + { + validateIndex(index, 4); + + if (_isMotorolaByteOrder) { + // Motorola - MSB first (big endian) + return (((long)getByte(index )) << 24 & 0xFF000000L) | + (((long)getByte(index + 1)) << 16 & 0xFF0000L) | + (((long)getByte(index + 2)) << 8 & 0xFF00L) | + (((long)getByte(index + 3)) & 0xFFL); + } else { + // Intel ordering - LSB first (little endian) + return (((long)getByte(index + 3)) << 24 & 0xFF000000L) | + (((long)getByte(index + 2)) << 16 & 0xFF0000L) | + (((long)getByte(index + 1)) << 8 & 0xFF00L) | + (((long)getByte(index )) & 0xFFL); + } + } + + /** + * Returns a signed 32-bit integer from four bytes of data at the specified index the buffer. + * + * @param index position within the data buffer to read first byte + * @return the signed 32 bit int value, between 0x00000000 and 0xFFFFFFFF + * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative + */ + public int getInt32(int index) throws ExifException + { + validateIndex(index, 4); + + if (_isMotorolaByteOrder) { + // Motorola - MSB first (big endian) + return (getByte(index ) << 24 & 0xFF000000) | + (getByte(index + 1) << 16 & 0xFF0000) | + (getByte(index + 2) << 8 & 0xFF00) | + (getByte(index + 3) & 0xFF); + } else { + // Intel ordering - LSB first (little endian) + return (getByte(index + 3) << 24 & 0xFF000000) | + (getByte(index + 2) << 16 & 0xFF0000) | + (getByte(index + 1) << 8 & 0xFF00) | + (getByte(index ) & 0xFF); + } + } + + /** + * Creates a String from the _data buffer starting at the specified index, + * and ending where byte=='\0' or where length==maxLength. + * + * @param index The index within the buffer at which to start reading the string. + * @param maxLengthBytes The maximum number of bytes to read. If a zero-byte is not reached within this limit, + * reading will stop and the string will be truncated to this length. + * @return The read string. + * @throws IOException The buffer does not contain enough bytes to satisfy this request. + */ + public String getNullTerminatedString(int index, int maxLengthBytes) throws ExifException + { + // NOTE currently only really suited to single-byte character strings + + byte[] bytes = getBytes(index, maxLengthBytes); + + // Count the number of non-null bytes + int length = 0; + while (length < bytes.length && bytes[length] != '\0') + length++; + + return new String(bytes, 0, length); + } + + public String getString(int index, int bytesRequested) throws ExifException + { + // TODO: validate index + return new String(getBytes(index, bytesRequested)); + } +} diff --git a/tim/prune/jpeg/drew/ExifException.java b/tim/prune/jpeg/drew/ExifException.java new file mode 100644 index 0000000..6d26ba3 --- /dev/null +++ b/tim/prune/jpeg/drew/ExifException.java @@ -0,0 +1,12 @@ +package tim.prune.jpeg.drew; + +public class ExifException extends Exception +{ + public ExifException(String message) { + super(message); + } + + public ExifException(Throwable cause) { + super(cause); + } +} diff --git a/tim/prune/jpeg/drew/ExifReader.java b/tim/prune/jpeg/drew/ExifReader.java index 4c90cab..c8c1b7c 100644 --- a/tim/prune/jpeg/drew/ExifReader.java +++ b/tim/prune/jpeg/drew/ExifReader.java @@ -1,590 +1,183 @@ -package tim.prune.jpeg.drew; - -import java.io.File; -import java.util.HashMap; - -import tim.prune.jpeg.ExifGateway; -import tim.prune.jpeg.JpegData; - -/** - * Extracts Exif data from a JPEG header segment - * Based on Drew Noakes' Metadata extractor at http://drewnoakes.com - * which in turn is based on code from Jhead http://www.sentex.net/~mwandel/jhead/ - */ -public class ExifReader -{ - /** The JPEG segment as an array of bytes */ - private final byte[] _data; - - /** - * Represents the native byte ordering used in the JPEG segment. If true, - * then we're using Motorola ordering (Big endian), else we're using Intel - * ordering (Little endian). - */ - private boolean _isMotorolaByteOrder; - - /** Thumbnail offset */ - private int _thumbnailOffset = -1; - /** Thumbnail length */ - private int _thumbnailLength = -1; - - /** The number of bytes used per format descriptor */ - private static final int[] BYTES_PER_FORMAT = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8}; - - /** The number of formats known */ - private static final int MAX_FORMAT_CODE = 12; - - // Format types - // Note: Cannot use the DataFormat enumeration in the case statement that uses these tags. - // Is there a better way? - //private static final int FMT_BYTE = 1; - private static final int FMT_STRING = 2; - //private static final int FMT_USHORT = 3; - //private static final int FMT_ULONG = 4; - private static final int FMT_URATIONAL = 5; - //private static final int FMT_SBYTE = 6; - //private static final int FMT_UNDEFINED = 7; - //private static final int FMT_SSHORT = 8; - //private static final int FMT_SLONG = 9; - private static final int FMT_SRATIONAL = 10; - //private static final int FMT_SINGLE = 11; - //private static final int FMT_DOUBLE = 12; - - public static final int TAG_EXIF_OFFSET = 0x8769; - public static final int TAG_INTEROP_OFFSET = 0xA005; - public static final int TAG_GPS_INFO_OFFSET = 0x8825; - public static final int TAG_MAKER_NOTE = 0x927C; - - public static final int TIFF_HEADER_START_OFFSET = 6; - - /** GPS tag version GPSVersionID 0 0 BYTE 4 */ - public static final int TAG_GPS_VERSION_ID = 0x0000; - /** North or South Latitude GPSLatitudeRef 1 1 ASCII 2 */ - public static final int TAG_GPS_LATITUDE_REF = 0x0001; - /** Latitude GPSLatitude 2 2 RATIONAL 3 */ - public static final int TAG_GPS_LATITUDE = 0x0002; - /** East or West Longitude GPSLongitudeRef 3 3 ASCII 2 */ - public static final int TAG_GPS_LONGITUDE_REF = 0x0003; - /** Longitude GPSLongitude 4 4 RATIONAL 3 */ - public static final int TAG_GPS_LONGITUDE = 0x0004; - /** Altitude reference GPSAltitudeRef 5 5 BYTE 1 */ - public static final int TAG_GPS_ALTITUDE_REF = 0x0005; - /** Altitude GPSAltitude 6 6 RATIONAL 1 */ - public static final int TAG_GPS_ALTITUDE = 0x0006; - /** GPS time (atomic clock) GPSTimeStamp 7 7 RATIONAL 3 */ - public static final int TAG_GPS_TIMESTAMP = 0x0007; - /** GPS date (atomic clock) GPSDateStamp 23 1d RATIONAL 3 */ - public static final int TAG_GPS_DATESTAMP = 0x001d; - /** "Original" Exif timestamp */ - public static final int TAG_DATETIME_ORIGINAL = 0x9003; - /** "Creation" or "Digitized" timestamp */ - public static final int TAG_DATETIME_DIGITIZED = 0x9004; - /** Thumbnail offset */ - private static final int TAG_THUMBNAIL_OFFSET = 0x0201; - /** Thumbnail length */ - private static final int TAG_THUMBNAIL_LENGTH = 0x0202; - /** Orientation of image */ - private static final int TAG_ORIENTATION = 0x0112; - /** Bearing direction of image */ - private static final int TAG_BEARING = 0x0011; - - - /** - * Creates an ExifReader for a Jpeg file - * @param inFile File object to attempt to read from - * @throws JpegException on failure - */ - public ExifReader(File inFile) throws JpegException - { - _data = JpegSegmentReader.readExifSegment(inFile); - } - - /** - * Performs the Exif data extraction - * @return the GPS data found in the file - */ - public JpegData extract() - { - JpegData metadata = new JpegData(); - if (_data==null) - return metadata; - - // check for the header length - if (_data.length<=14) - { - metadata.addError("Exif data segment must contain at least 14 bytes"); - return metadata; - } - - // check for the header preamble - if (!"Exif\0\0".equals(new String(_data, 0, 6))) - { - metadata.addError("Exif data segment doesn't begin with 'Exif'"); - return metadata; - } - - // this should be either "MM" or "II" - String byteOrderIdentifier = new String(_data, 6, 2); - if (!setByteOrder(byteOrderIdentifier)) - { - metadata.addError("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier); - return metadata; - } - - // Check the next two values are 0x2A as expected - if (get16Bits(8)!=0x2a) - { - metadata.addError("Invalid Exif start - should have 0x2A at offset 8 in Exif header"); - return metadata; - } - - int firstDirectoryOffset = get32Bits(10) + TIFF_HEADER_START_OFFSET; - - // Check that offset is within range - if (firstDirectoryOffset>=_data.length - 1) - { - metadata.addError("First exif directory offset is beyond end of Exif data segment"); - // First directory normally starts 14 bytes in -- try it here and catch another error in the worst case - firstDirectoryOffset = 14; - } - - HashMap processedDirectoryOffsets = new HashMap(); - - // 0th IFD (we merge with Exif IFD) - processDirectory(metadata, false, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET); - - return metadata; - } - - - /** - * Set the byte order identifier - * @param byteOrderIdentifier String from exif - * @return true if recognised, false otherwise - */ - private boolean setByteOrder(String byteOrderIdentifier) - { - if ("MM".equals(byteOrderIdentifier)) { - _isMotorolaByteOrder = true; - } else if ("II".equals(byteOrderIdentifier)) { - _isMotorolaByteOrder = false; - } else { - return false; - } - return true; - } - - - /** - * Recursive call to process one of the nested Tiff IFD directories. - * 2 bytes: number of tags - * for each tag - * 2 bytes: tag type - * 2 bytes: format code - * 4 bytes: component count - */ - private void processDirectory(JpegData inMetadata, boolean inIsGPS, HashMap inDirectoryOffsets, - int inDirOffset, int inTiffHeaderOffset) - { - // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist - if (inDirectoryOffsets.containsKey(Integer.valueOf(inDirOffset))) - return; - - // remember that we've visited this directory so that we don't visit it again later - inDirectoryOffsets.put(Integer.valueOf(inDirOffset), "processed"); - - if (inDirOffset >= _data.length || inDirOffset < 0) - { - inMetadata.addError("Ignored directory marked to start outside data segment"); - return; - } - - // First two bytes in the IFD are the number of tags in this directory - int dirTagCount = get16Bits(inDirOffset); - // If no tags, exit without complaint - if (dirTagCount == 0) return; - - if (!isDirectoryLengthValid(inDirOffset, inTiffHeaderOffset)) - { - inMetadata.addError("Directory length is not valid"); - return; - } - - inMetadata.setExifDataPresent(); - // Handle each tag in this directory - for (int tagNumber = 0; tagNumber MAX_FORMAT_CODE) - { - inMetadata.addError("Invalid format code: " + formatCode); - continue; - } - - // 4 bytes dictate the number of components in this tag's data - final int componentCount = get32Bits(tagOffset + 4); - if (componentCount < 0) - { - inMetadata.addError("Negative component count in EXIF"); - continue; - } - // each component may have more than one byte... calculate the total number of bytes - final int byteCount = componentCount * BYTES_PER_FORMAT[formatCode]; - final int tagValueOffset = calculateTagValueOffset(byteCount, tagOffset, inTiffHeaderOffset); - if (tagValueOffset < 0 || tagValueOffset > _data.length) - { - inMetadata.addError("Illegal pointer offset value in EXIF"); - continue; - } - - // Check that this tag isn't going to allocate outside the bounds of the data array. - // This addresses an uncommon OutOfMemoryError. - if (byteCount < 0 || tagValueOffset + byteCount > _data.length) - { - inMetadata.addError("Illegal number of bytes: " + byteCount); - continue; - } - - // Calculate the value as an offset for cases where the tag represents a directory - final int subdirOffset = inTiffHeaderOffset + get32Bits(tagValueOffset); - - // Look in both basic Exif tags (for timestamp, thumbnail) and Gps tags (for lat, long, altitude, timestamp) - switch (tagType) - { - case TAG_EXIF_OFFSET: - processDirectory(inMetadata, false, inDirectoryOffsets, subdirOffset, inTiffHeaderOffset); - continue; - case TAG_INTEROP_OFFSET: - // ignore - continue; - case TAG_GPS_INFO_OFFSET: - processDirectory(inMetadata, true, inDirectoryOffsets, subdirOffset, inTiffHeaderOffset); - continue; - case TAG_MAKER_NOTE: - // ignore - continue; - default: - // not a known directory, so must just be a normal tag - if (inIsGPS) - { - processGpsTag(inMetadata, tagType, tagValueOffset, componentCount, formatCode); - } - else - { - processExifTag(inMetadata, tagType, tagValueOffset, componentCount, formatCode); - } - break; - } - } - - // at the end of each IFD is an optional link to the next IFD - final int finalTagOffset = calculateTagOffset(inDirOffset, dirTagCount); - int nextDirectoryOffset = get32Bits(finalTagOffset); - if (nextDirectoryOffset != 0) - { - nextDirectoryOffset += inTiffHeaderOffset; - if (nextDirectoryOffset>=_data.length) - { - // Last 4 bytes of IFD reference another IFD with an address that is out of bounds - return; - } - else if (nextDirectoryOffset < inDirOffset) - { - // Last 4 bytes of IFD reference another IFD with an address before the start of this directory - return; - } - // the next directory is of same type as this one - processDirectory(inMetadata, false, inDirectoryOffsets, nextDirectoryOffset, inTiffHeaderOffset); - } - } - - - /** - * Check if the directory length is valid - * @param dirStartOffset start offset for directory - * @param tiffHeaderOffset Tiff header offeset - * @return true if length is valid - */ - private boolean isDirectoryLengthValid(int inDirStartOffset, int inTiffHeaderOffset) - { - int dirTagCount = get16Bits(inDirStartOffset); - int dirLength = (2 + (12 * dirTagCount) + 4); - if (dirLength + inDirStartOffset + inTiffHeaderOffset >= _data.length) - { - // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier might trigger this - return false; - } - return true; - } - - - /** - * Process a GPS tag and put the contents in the given metadata - * @param inMetadata metadata holding extracted values - * @param inTagType tag type (eg latitude) - * @param inTagValueOffset start offset in data array - * @param inComponentCount component count for tag - * @param inFormatCode format code, eg byte - */ - private void processGpsTag(JpegData inMetadata, int inTagType, int inTagValueOffset, - int inComponentCount, int inFormatCode) - { - try - { - // Only interested in tags latref, lat, longref, lon, altref, alt and gps timestamp - switch (inTagType) - { - case TAG_GPS_LATITUDE_REF: - inMetadata.setLatitudeRef(readString(inTagValueOffset, inFormatCode, inComponentCount)); - break; - case TAG_GPS_LATITUDE: - Rational[] latitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount); - inMetadata.setLatitude(new double[] {latitudes[0].doubleValue(), latitudes[1].doubleValue(), - ExifGateway.convertToPositiveValue(latitudes[2].getNumerator(), latitudes[2].getDenominator())}); - break; - case TAG_GPS_LONGITUDE_REF: - inMetadata.setLongitudeRef(readString(inTagValueOffset, inFormatCode, inComponentCount)); - break; - case TAG_GPS_LONGITUDE: - Rational[] longitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount); - inMetadata.setLongitude(new double[] {longitudes[0].doubleValue(), longitudes[1].doubleValue(), - ExifGateway.convertToPositiveValue(longitudes[2].getNumerator(), longitudes[2].getDenominator())}); - break; - case TAG_GPS_ALTITUDE_REF: - inMetadata.setAltitudeRef(_data[inTagValueOffset]); - break; - case TAG_GPS_ALTITUDE: - inMetadata.setAltitude(readRational(inTagValueOffset, inFormatCode, inComponentCount).intValue()); - break; - case TAG_GPS_TIMESTAMP: - Rational[] times = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount); - inMetadata.setGpsTimestamp(new int[] {times[0].intValue(), times[1].intValue(), times[2].intValue()}); - break; - case TAG_GPS_DATESTAMP: - Rational[] dates = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount); - if (dates != null) { - inMetadata.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()}); - } - else - { - // Not in rational array format, but maybe as String? - String date = readString(inTagValueOffset, inFormatCode, inComponentCount); - if (date != null && date.length() == 10) { - inMetadata.setGpsDatestamp(new int[] {Integer.parseInt(date.substring(0, 4)), - Integer.parseInt(date.substring(5, 7)), Integer.parseInt(date.substring(8))}); - } - } - break; - case TAG_BEARING: - Rational val = readRational(inTagValueOffset, inFormatCode, inComponentCount); - if (val != null) { - inMetadata.setBearing(val.doubleValue()); - } - break; - default: // ignore all other tags - } - } - catch (Exception e) {} // ignore and continue - } - - - /** - * Process a general Exif tag - * @param inMetadata metadata holding extracted values - * @param inTagType tag type (eg latitude) - * @param inTagValueOffset start offset in data array - * @param inComponentCount component count for tag - * @param inFormatCode format code, eg byte - */ - private void processExifTag(JpegData inMetadata, int inTagType, int inTagValueOffset, - int inComponentCount, int inFormatCode) - { - // Only interested in original timestamp, thumbnail offset and thumbnail length - if (inTagType == TAG_DATETIME_ORIGINAL) { - inMetadata.setOriginalTimestamp(readString(inTagValueOffset, inFormatCode, inComponentCount)); - } - else if (inTagType == TAG_DATETIME_DIGITIZED) { - inMetadata.setDigitizedTimestamp(readString(inTagValueOffset, inFormatCode, inComponentCount)); - } - else if (inTagType == TAG_THUMBNAIL_OFFSET) { - _thumbnailOffset = TIFF_HEADER_START_OFFSET + get16Bits(inTagValueOffset); - extractThumbnail(inMetadata); - } - else if (inTagType == TAG_THUMBNAIL_LENGTH) { - _thumbnailLength = get16Bits(inTagValueOffset); - extractThumbnail(inMetadata); - } - else if (inTagType == TAG_ORIENTATION) { - if (inMetadata.getOrientationCode() < 1) { - inMetadata.setOrientationCode(get16Bits(inTagValueOffset)); - } - } - } - - /** - * Attempt to extract the thumbnail image - */ - private void extractThumbnail(JpegData inMetadata) - { - if (_thumbnailOffset > 0 && _thumbnailLength > 0 && inMetadata.getThumbnailImage() == null) - { - byte[] thumbnailBytes = new byte[_thumbnailLength]; - System.arraycopy(_data, _thumbnailOffset, thumbnailBytes, 0, _thumbnailLength); - inMetadata.setThumbnailImage(thumbnailBytes); - } - } - - - /** - * Calculate the tag value offset - * @param inByteCount - * @param inDirEntryOffset - * @param inTiffHeaderOffset - * @return new offset - */ - private int calculateTagValueOffset(int inByteCount, int inDirEntryOffset, int inTiffHeaderOffset) - { - if (inByteCount > 4) - { - // If it's bigger than 4 bytes, the dir entry contains an offset. - // dirEntryOffset must be passed, as some makers (e.g. FujiFilm) incorrectly use an - // offset relative to the start of the makernote itself, not the TIFF segment. - final int offsetVal = get32Bits(inDirEntryOffset + 8); - if (offsetVal + inByteCount > _data.length) - { - // Bogus pointer offset and / or bytecount value - return -1; // signal error - } - return inTiffHeaderOffset + offsetVal; - } - else - { - // 4 bytes or less and value is in the dir entry itself - return inDirEntryOffset + 8; - } - } - - - /** - * Creates a String from the _data buffer starting at the specified offset, - * and ending where byte=='\0' or where length==maxLength. - * @param inOffset start offset - * @param inFormatCode format code - should be string - * @param inMaxLength max length of string - * @return contents of tag, or null if format incorrect - */ - private String readString(int inOffset, int inFormatCode, int inMaxLength) - { - if (inFormatCode != FMT_STRING) return null; - // Calculate length - int length = 0; - while ((inOffset + length)<_data.length - && _data[inOffset + length]!='\0' - && length < inMaxLength) - { - length++; - } - return new String(_data, inOffset, length); - } - - /** - * Creates a Rational from the _data buffer starting at the specified offset - * @param inOffset start offset - * @param inFormatCode format code - should be srational or urational - * @param inCount component count - should be 1 - * @return contents of tag as a Rational object - */ - private Rational readRational(int inOffset, int inFormatCode, int inCount) - { - // Check the format is a single rational as expected - if (inFormatCode != FMT_SRATIONAL && inFormatCode != FMT_URATIONAL - || inCount != 1) return null; - return new Rational(get32Bits(inOffset), get32Bits(inOffset + 4)); - } - - - /** - * Creates a Rational array from the _data buffer starting at the specified offset - * @param inOffset start offset - * @param inFormatCode format code - should be srational or urational - * @param inCount component count - number of components - * @return contents of tag as an array of Rational objects - */ - private Rational[] readRationalArray(int inOffset, int inFormatCode, int inCount) - { - // Check the format is rational as expected - if (inFormatCode != FMT_SRATIONAL && inFormatCode != FMT_URATIONAL) - return null; - // Build array of Rationals - Rational[] answer = new Rational[inCount]; - for (int i=0; i_data.length) - throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " - + offset + " where max index is " + (_data.length - 1) + ")"); - - if (_isMotorolaByteOrder) { - // Motorola - MSB first - return (_data[offset] << 8 & 0xFF00) | (_data[offset + 1] & 0xFF); - } else { - // Intel ordering - LSB first - return (_data[offset + 1] << 8 & 0xFF00) | (_data[offset] & 0xFF); - } - } - - - /** - * Get a 32 bit value from file's native byte order. - */ - private int get32Bits(int offset) - { - if (offset < 0 || offset+4 > _data.length) - throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " - + offset + " where max index is " + (_data.length - 1) + ")"); - - if (_isMotorolaByteOrder) - { - // Motorola - MSB first - return (_data[offset] << 24 & 0xFF000000) | - (_data[offset + 1] << 16 & 0xFF0000) | - (_data[offset + 2] << 8 & 0xFF00) | - (_data[offset + 3] & 0xFF); - } - else - { - // Intel ordering - LSB first - return (_data[offset + 3] << 24 & 0xFF000000) | - (_data[offset + 2] << 16 & 0xFF0000) | - (_data[offset + 1] << 8 & 0xFF00) | - (_data[offset] & 0xFF); - } - } -} +package tim.prune.jpeg.drew; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import tim.prune.jpeg.JpegData; + + +/** + * Extracts Exif data from a JPEG header segment + * Based on Drew Noakes' Metadata extractor at https://drewnoakes.com + * which in turn is based on code from Jhead http://www.sentex.net/~mwandel/jhead/ + */ +public class ExifReader +{ + /** Magic numbers to mark the beginning of all Jpegs */ + private static final int MAGIC_JPEG_BYTE_1 = 0xFF; + private static final int MAGIC_JPEG_BYTE_2 = 0xD8; + + /** 6-byte preamble before starting the TIFF data. */ + private static final String JPEG_EXIF_SEGMENT_PREAMBLE = "Exif\0\0"; + + /** Start of segment marker */ + private static final byte SEGMENT_SOS = (byte) 0xDA; + + /** End of segment marker */ + private static final byte MARKER_EOI = (byte) 0xD9; + + /** + * Processes the provided JPEG data, and extracts the specified JPEG segments into a JpegData object. + * @param inFile a {@link File} from which the JPEG data will be read. + */ + public static JpegData readMetadata(File inFile) throws ExifException + { + JpegData jpegData = new JpegData(); + BufferedInputStream bStream = null; + + try + { + bStream = new BufferedInputStream(new FileInputStream(inFile)); + byte[] segmentBytes = readSegments(bStream); + if (segmentBytes != null) + { + // Got the bytes for the required segment, now extract the data + extract(segmentBytes, jpegData); + } + } + catch (IOException ioe) { + throw new ExifException("IO Exception: " + ioe.getMessage()); + } + finally + { + if (bStream != null) { + try { + bStream.close(); + } catch (IOException ioe) {} + } + } + return jpegData; + } + + /** + * Reads the relevant segment and returns the bytes. + */ + private static byte[] readSegments(final BufferedInputStream bStream) + throws ExifException, IOException + { + // first two bytes should be JPEG magic number + final int magic1 = bStream.read() & 0xFF; + final int magic2 = bStream.read() & 0xFF; + if (magic1 != MAGIC_JPEG_BYTE_1 || magic2 != MAGIC_JPEG_BYTE_2) { + throw new ExifException("Jpeg file failed Magic check"); + } + + final Byte segmentTypeByte = (byte)0xE1; // JpegSegmentType.APP1.byteValue; + + do { + // Find the segment marker. Markers are zero or more 0xFF bytes, followed + // by a 0xFF and then a byte not equal to 0x00 or 0xFF. + + final short segmentIdentifier = (short) bStream.read(); + + // We must have at least one 0xFF byte + if (segmentIdentifier != 0xFF) + throw new ExifException("Expected JPEG segment start identifier 0xFF, not 0x" + Integer.toHexString(segmentIdentifier).toUpperCase()); + + // Read until we have a non-0xFF byte. This identifies the segment type. + byte currSegmentType = (byte) bStream.read(); + while (currSegmentType == (byte)0xFF) { + currSegmentType = (byte) bStream.read(); + } + + if (currSegmentType == 0) + throw new ExifException("Expected non-zero byte as part of JPEG marker identifier"); + + if (currSegmentType == SEGMENT_SOS) { + // The 'Start-Of-Scan' segment's length doesn't include the image data, instead would + // have to search for the two bytes: 0xFF 0xD9 (EOI). + // It comes last so simply return at this point + return null; + } + + if (currSegmentType == MARKER_EOI) { + // the 'End-Of-Image' segment -- this should never be found in this fashion + return null; + } + + // next 2-bytes are : [high-byte] [low-byte] + int segmentLength = (bStream.read() << 8) + bStream.read(); + // segment length includes size bytes, so subtract two + segmentLength -= 2; + + if (segmentLength < 0) + throw new ExifException("JPEG segment size would be less than zero"); + + byte[] segmentBytes = new byte[segmentLength]; + int bytesRead = bStream.read(segmentBytes, 0, segmentLength); + // Bail if not all bytes read in one go - otherwise following sections will be out of step + if (bytesRead != segmentLength) { + throw new ExifException("Tried to read " + segmentLength + " bytes but only got " + bytesRead); + } + // Check whether we are interested in this segment + if (segmentTypeByte == currSegmentType) + { + // Pass the appropriate byte arrays to reader. + if (canProcess(segmentBytes)) { + return segmentBytes; + } + } + + } while (true); + } + + private static boolean canProcess(final byte[] segmentBytes) + { + return segmentBytes.length >= JPEG_EXIF_SEGMENT_PREAMBLE.length() && new String(segmentBytes, 0, JPEG_EXIF_SEGMENT_PREAMBLE.length()).equalsIgnoreCase(JPEG_EXIF_SEGMENT_PREAMBLE); + } + + /** + * Given the bytes, parse them recursively to fill the JpegData + * @param segmentBytes bytes out of the file + * @param jdata jpeg data to be populated + */ + private static void extract(final byte[] segmentBytes, final JpegData jdata) + { + if (segmentBytes == null) + throw new NullPointerException("segmentBytes cannot be null"); + + try + { + ByteArrayReader reader = new ByteArrayReader(segmentBytes); + + // Check for the header preamble + try { + if (!reader.getString(0, JPEG_EXIF_SEGMENT_PREAMBLE.length()).equals(JPEG_EXIF_SEGMENT_PREAMBLE)) { + // TODO what to do with this error state? + System.err.println("Invalid JPEG Exif segment preamble"); + return; + } + } catch (ExifException e) { + // TODO what to do with this error state? + e.printStackTrace(System.err); + return; + } + + // Read the TIFF-formatted Exif data + TiffProcessor.processTiff( + reader, + jdata, + JPEG_EXIF_SEGMENT_PREAMBLE.length() + ); + + } catch (ExifException e) { + // TODO what to do with this error state? + e.printStackTrace(System.err); + } catch (IOException e) { + // TODO what to do with this error state? + e.printStackTrace(System.err); + } + } +} diff --git a/tim/prune/jpeg/drew/ExifTiffHandler.java b/tim/prune/jpeg/drew/ExifTiffHandler.java new file mode 100644 index 0000000..a98e720 --- /dev/null +++ b/tim/prune/jpeg/drew/ExifTiffHandler.java @@ -0,0 +1,171 @@ +/* + * Copyright 2002-2015 Drew Noakes + * + * More information about this project is available at: + * + * https://drewnoakes.com/code/exif/ + * https://github.com/drewnoakes/metadata-extractor + */ +package tim.prune.jpeg.drew; + +import tim.prune.jpeg.JpegData; + +/** + * Implementation of TiffHandler used for handling TIFF tags according to the Exif standard. + * + * @author Drew Noakes https://drewnoakes.com + */ +public class ExifTiffHandler +{ + private JpegData _jpegData = null; + private long _thumbnailOffset = -1L, _thumbnailLength = -1L; + + /** This tag is a pointer to the Exif SubIFD. */ + final int DIR_EXIF_SUB_IFD_OFFSET = 0x8769; + /** This tag is a pointer to the Exif GPS IFD. */ + final int DIR_GPS_INFO_OFFSET = 0x8825; + + private static final int TAG_GPS_LATITUDE_REF = 0x0001; + private static final int TAG_GPS_LATITUDE = 0x0002; + private static final int TAG_GPS_LONGITUDE_REF = 0x0003; + private static final int TAG_GPS_LONGITUDE = 0x0004; + private static final int TAG_GPS_ALTITUDE = 0x0006; + private static final int TAG_GPS_BEARING = 0x0011; + + private static final int TAG_ORIENTATION = 0x0112; + private static final int TAG_THUMBNAIL_OFFSET = 0x0201; + private static final int TAG_THUMBNAIL_LENGTH = 0x0202; + + private static final int TAG_SUB_ORITIME = 0x9003; + private static final int TAG_SUB_DIGITIME = 0x9004; + + + /** + * Constructor + * @param jpegData data object to populate with received results + */ + public ExifTiffHandler(JpegData jpegData) + { + _jpegData = jpegData; + _thumbnailOffset = _thumbnailLength = -1L; + } + + public boolean isTagIfdPointer(int tagType) + { + if (tagType == DIR_EXIF_SUB_IFD_OFFSET) { + return true; + } else if (tagType == DIR_GPS_INFO_OFFSET) { + return true; + } + + return false; + } + + public void completed(final ByteArrayReader reader, final int tiffHeaderOffset) + { + // after the extraction process, if we have the correct tags, we may be able to store thumbnail information + if (_thumbnailOffset >= 0L && _thumbnailLength > 0L) + { + try { + byte[] thumbData = reader.getBytes(tiffHeaderOffset + (int) _thumbnailOffset, (int) _thumbnailLength); + if (thumbData != null) + { + byte[] thumbCopy = new byte[thumbData.length]; + System.arraycopy(thumbData, 0, thumbCopy, 0, thumbData.length); + _jpegData.setThumbnailImage(thumbCopy); + } + } catch (ExifException ex) {} + } + } + + public void setRationalArray(int tagId, Rational[] array) + { + switch (tagId) + { + case TAG_GPS_LATITUDE: + _jpegData.setLatitude(new double[] {array[0].doubleValue(), array[1].doubleValue(), + array[2].convertToPositiveValue()}); + break; + case TAG_GPS_LONGITUDE: + _jpegData.setLongitude(new double[] {array[0].doubleValue(), array[1].doubleValue(), + array[2].convertToPositiveValue()}); + break; + } + } + + public void setRational(int tagId, Rational rational) + { + switch (tagId) + { + case TAG_GPS_ALTITUDE: + _jpegData.setAltitude(rational.intValue()); + return; + case TAG_GPS_BEARING: + _jpegData.setBearing(rational.doubleValue()); + return; + } + // maybe it was an integer passed as a rational? + if (rational.getDenominator() == 1L) { + setIntegerValue(tagId, rational.intValue()); + } + } + + public void setString(int tagId, String string) + { + switch (tagId) + { + case TAG_SUB_ORITIME: + _jpegData.setOriginalTimestamp(string); + break; + case TAG_SUB_DIGITIME: + _jpegData.setDigitizedTimestamp(string); + break; + case TAG_GPS_LATITUDE_REF: + _jpegData.setLatitudeRef(string); + break; + case TAG_GPS_LONGITUDE_REF: + _jpegData.setLongitudeRef(string); + break; + } + } + + public void setIntegerValue(int tagId, int intVal) + { + switch (tagId) + { + case TAG_ORIENTATION: + _jpegData.setOrientationCode(intVal); + break; + case TAG_THUMBNAIL_OFFSET: + _thumbnailOffset = intVal; + break; + case TAG_THUMBNAIL_LENGTH: + _thumbnailLength = intVal; + break; + } + } + + + /** + * Decide, based on the directory id and the tag id, if we want to parse and process it + * @param inDirectoryId + * @param childTagId + * @return true if the tag should be parsed + */ + public boolean isInterestingTag(int inDirectoryId, int childTagId) + { + switch (inDirectoryId) + { + case DIR_GPS_INFO_OFFSET: + return true; + case DIR_EXIF_SUB_IFD_OFFSET: + return childTagId == TAG_SUB_ORITIME + || childTagId == TAG_SUB_DIGITIME; + case 0: + return childTagId == TAG_THUMBNAIL_OFFSET + || childTagId == TAG_THUMBNAIL_LENGTH + || childTagId == TAG_ORIENTATION; + } + return false; + } +} diff --git a/tim/prune/jpeg/drew/JpegException.java b/tim/prune/jpeg/drew/JpegException.java deleted file mode 100644 index d85a3b7..0000000 --- a/tim/prune/jpeg/drew/JpegException.java +++ /dev/null @@ -1,25 +0,0 @@ -package tim.prune.jpeg.drew; - -/** - * Class to indicate a fatal exception processing a jpeg, - * including IO errors and exif errors - */ -public class JpegException extends Exception -{ - /** - * @param message description of error - */ - public JpegException(String message) - { - super(message); - } - - /** - * @param message description of error - * @param cause Throwable which caused the error - */ - public JpegException(String message, Throwable cause) - { - super(message, cause); - } -} diff --git a/tim/prune/jpeg/drew/JpegSegmentData.java b/tim/prune/jpeg/drew/JpegSegmentData.java deleted file mode 100644 index 4b56cbe..0000000 --- a/tim/prune/jpeg/drew/JpegSegmentData.java +++ /dev/null @@ -1,106 +0,0 @@ -package tim.prune.jpeg.drew; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * Class to hold a collection of Jpeg data segments - * Each marker represents a list of multiple byte arrays - * Based on Drew Noakes' Metadata extractor at http://drewnoakes.com - */ -public class JpegSegmentData -{ - /** A map of byte[], keyed by the segment marker */ - private final HashMap> _segmentDataMap = new HashMap>(10); - - - /** - * Add a segment to the collection - * @param inSegmentMarker marker byte - * @param inSegmentBytes data of segment - */ - public void addSegment(byte inSegmentMarker, byte[] inSegmentBytes) - { - List segmentList = getOrCreateSegmentList(inSegmentMarker); - segmentList.add(inSegmentBytes); - } - - - /** - * Get the first segment with the given marker - * @param inSegmentMarker marker byte - * @return first segment with that marker - */ - public byte[] getSegment(byte inSegmentMarker) - { - return getSegment(inSegmentMarker, 0); - } - - - /** - * Get the nth segment with the given marker - * @param inSegmentMarker marker byte - * @param inOccurrence occurrence to get, starting at 0 - * @return byte array from specified segment - */ - public byte[] getSegment(byte inSegmentMarker, int inOccurrence) - { - final List segmentList = getSegmentList(inSegmentMarker); - - if (segmentList==null || segmentList.size()<=inOccurrence) - return null; - else - return segmentList.get(inOccurrence); - } - - - /** - * Get the number of segments with the given marker - * @param inSegmentMarker marker byte - * @return number of segments - */ - public int getSegmentCount(byte inSegmentMarker) - { - final List segmentList = getSegmentList(inSegmentMarker); - if (segmentList == null) - return 0; - else - return segmentList.size(); - } - - - /** - * Get the list of segments with the given marker - * @param inSegmentMarker marker byte - * @return list of segments - */ - private List getSegmentList(byte inSegmentMarker) - { - return _segmentDataMap.get(Byte.valueOf(inSegmentMarker)); - } - - - /** - * Get the specified segment if it exists, otherwise create new one - * @param inSegmentMarker marker byte - * @return list of segments - */ - private List getOrCreateSegmentList(byte inSegmentMarker) - { - List segmentList = null; - Byte key = Byte.valueOf(inSegmentMarker); - if (_segmentDataMap.containsKey(key)) - { - // list already exists - segmentList = _segmentDataMap.get(key); - } - else - { - // create new list and add it - segmentList = new ArrayList(); - _segmentDataMap.put(key, segmentList); - } - return segmentList; - } -} diff --git a/tim/prune/jpeg/drew/JpegSegmentReader.java b/tim/prune/jpeg/drew/JpegSegmentReader.java deleted file mode 100644 index 65515a1..0000000 --- a/tim/prune/jpeg/drew/JpegSegmentReader.java +++ /dev/null @@ -1,130 +0,0 @@ -package tim.prune.jpeg.drew; - -import java.io.*; - -/** - * Class to perform read functions of Jpeg files, returning specific file segments - * Based on Drew Noakes' Metadata extractor at http://drewnoakes.com - */ -public class JpegSegmentReader -{ - /** Start of scan marker */ - private static final byte SEGMENT_SOS = (byte)0xDA; - - /** End of image marker */ - private static final byte MARKER_EOI = (byte)0xD9; - - /** APP1 Jpeg segment identifier -- where Exif data is kept. */ - private static final byte SEGMENT_APP1 = (byte)0xE1; - - /** Magic numbers to mark the beginning of all Jpegs */ - private static final int MAGIC_JPEG_BYTE_1 = 0xFF; - private static final int MAGIC_JPEG_BYTE_2 = 0xD8; - - - /** - * Get the Exif data segment for the specified file - * @param inFile File to read - * @return Exif data segment as byte array - * @throws JpegException on file read errors or exif data errors - */ - public static byte[] readExifSegment(File inFile) throws JpegException - { - JpegSegmentData data = readSegments(inFile); - return data.getSegment(SEGMENT_APP1); - } - - - /** - * Obtain the Jpeg segment data from the specified file - * @param inFile File to read - * @return Jpeg segment data from file - * @throws JpegException on file read errors or exif data errors - */ - private static JpegSegmentData readSegments(File inFile) throws JpegException - { - JpegSegmentData segmentData = new JpegSegmentData(); - BufferedInputStream bStream = null; - - try - { - bStream = new BufferedInputStream(new FileInputStream(inFile)); - // first two bytes should be jpeg magic number - final int magic1 = bStream.read() & 0xFF; - final int magic2 = bStream.read() & 0xFF; - if (magic1 != MAGIC_JPEG_BYTE_1 || magic2 != MAGIC_JPEG_BYTE_2) { - throw new JpegException("not a jpeg file"); - } - - // Loop around segments found - boolean foundExif = false; - do - { - // next byte is 0xFF - byte segmentIdentifier = (byte) (bStream.read() & 0xFF); - if ((segmentIdentifier & 0xFF) != 0xFF) - { - throw new JpegException("expected jpeg segment start 0xFF, not 0x" - + Integer.toHexString(segmentIdentifier & 0xFF)); - } - // next byte is - byte thisSegmentMarker = (byte) (bStream.read() & 0xFF); - // next 2-bytes are : [high-byte] [low-byte] - byte[] segmentLengthBytes = new byte[2]; - bStream.read(segmentLengthBytes, 0, 2); - int segmentLength = ((segmentLengthBytes[0] << 8) & 0xFF00) | (segmentLengthBytes[1] & 0xFF); - // segment length includes size bytes, so subtract two - segmentLength -= 2; - if (segmentLength > bStream.available()) - throw new JpegException("segment size would extend beyond file stream length"); - else if (segmentLength < 0) - throw new JpegException("segment size would be less than zero"); - byte[] segmentBytes = new byte[segmentLength]; - int bytesRead = bStream.read(segmentBytes, 0, segmentLength); - // Bail if not all bytes read in one go - otherwise following sections will be out of step - if (bytesRead != segmentLength) { - throw new JpegException("Tried to read " + segmentLength + " bytes but only got " + bytesRead); - } - if ((thisSegmentMarker & 0xFF) == (SEGMENT_SOS & 0xFF)) - { - // The 'Start-Of-Scan' segment comes last so break out of loop - break; - } - else if ((thisSegmentMarker & 0xFF) == (MARKER_EOI & 0xFF)) - { - // the 'End-Of-Image' segment - should already have exited by now - break; - } - else - { - segmentData.addSegment(thisSegmentMarker, segmentBytes); - } - // loop through to the next segment if exif hasn't already been found - foundExif = (thisSegmentMarker == SEGMENT_APP1); - } - while (!foundExif); - } - catch (FileNotFoundException fnfe) - { - throw new JpegException("Jpeg file not found"); - } - catch (IOException ioe) - { - throw new JpegException("IOException processing Jpeg file: " + ioe.getMessage(), ioe); - } - finally - { - try - { - if (bStream != null) { - bStream.close(); - } - } - catch (IOException ioe) { - throw new JpegException("IOException processing Jpeg file: " + ioe.getMessage(), ioe); - } - } - // Return the result - return segmentData; - } -} diff --git a/tim/prune/jpeg/drew/Rational.java b/tim/prune/jpeg/drew/Rational.java index 3c83b9f..8395898 100644 --- a/tim/prune/jpeg/drew/Rational.java +++ b/tim/prune/jpeg/drew/Rational.java @@ -1,95 +1,113 @@ -package tim.prune.jpeg.drew; - -/** - * Immutable class for holding a rational number without loss of precision. - * Based on Drew Noakes' Metadata extractor at http://drewnoakes.com - */ -public class Rational -{ - /** Holds the numerator */ - private final int _numerator; - - /** Holds the denominator */ - private final int _denominator; - - /** - * Constructor - * @param inNumerator numerator of fraction (upper number) - * @param inDenominator denominator of fraction (lower number) - */ - public Rational(int inNumerator, int inDenominator) - { - // Could throw exception if denominator is zero - _numerator = inNumerator; - _denominator = inDenominator; - } - - - /** - * @return the value of the specified number as a double. - * This may involve rounding. - */ - public double doubleValue() - { - if (_denominator == 0) return 0.0; - return (double)_numerator / (double)_denominator; - } - - /** - * @return the value of the specified number as an int. - * This may involve rounding or truncation. - */ - public final int intValue() - { - if (_denominator == 0) return 0; - return _numerator / _denominator; - } - - /** - * @return the denominator. - */ - public final int getDenominator() - { - return _denominator; - } - - /** - * @return the numerator. - */ - public final int getNumerator() - { - return _numerator; - } - - /** - * Checks if this rational number is an Integer, either positive or negative - * @return true if an integer - */ - public boolean isInteger() - { - // number is integer if the denominator is 1, or if the remainder is zero - return (_denominator == 1 - || (_denominator != 0 && (_numerator % _denominator == 0))); - } - - - /** - * @return a string representation of the object of form numerator/denominator. - */ - public String toString() - { - return "" + _numerator + "/" + _denominator; - } - - - /** - * Compares two Rational instances, returning true if they are equal - * @param inOther the Rational to compare this instance to. - * @return true if instances are equal, otherwise false. - */ - public boolean equals(Rational inOther) - { - // Could also attempt to simplify fractions to lowest common denominator before compare - return _numerator == inOther._numerator && _denominator == inOther._denominator; - } -} \ No newline at end of file +package tim.prune.jpeg.drew; + +/** + * Immutable class for holding a rational number without loss of precision. + * Based on Drew Noakes' Metadata extractor at https://drewnoakes.com + */ +public class Rational +{ + /** Holds the numerator */ + private final long _numerator; + + /** Holds the denominator */ + private final long _denominator; + + /** + * Creates a new (immutable) instance of Rational. + */ + public Rational(long numerator, long denominator) + { + _numerator = numerator; + _denominator = denominator; + } + + /** + * @return the value of the specified number as a double. + * This may involve rounding. + */ + public double doubleValue() + { + if (_denominator == 0L) return 0.0; + return (double)_numerator / (double)_denominator; + } + + /** + * Returns the value of the specified number as an int. + */ + public final int intValue() + { + return (int) longValue(); + } + + /** + * Returns the value of the specified number as a long. + * This may involve rounding or truncation. + * If the denominator is 0, returns 0 to avoid dividing by 0. + */ + public final long longValue() + { + if (_denominator == 0L) return 0L; + return _numerator / _denominator; + } + + /** Returns the denominator */ + public final long getDenominator() + { + return _denominator; + } + + /** Returns the numerator */ + public final long getNumerator() + { + return _numerator; + } + + /** + * @return the value of the specified number as a positive double. + * Prevents interpretation of 32 bit numbers as negative, and forces a positive answer. + * Needed? + */ + public double convertToPositiveValue() + { + if (_denominator == 0L) return 0.0; + double numeratorDbl = _numerator; + double denomDbl = _denominator; + if (_numerator >= 0L) { + // Numerator is positive (but maybe denominator isn't?) + return numeratorDbl / denomDbl; + } + // Add 2^32 to negative doubles to make them positive + final double correction = Math.pow(2.0, 32); + numeratorDbl += correction; + if (_denominator < 0L) { + denomDbl += correction; + } + return numeratorDbl / denomDbl; + } + + /** + * Returns a string representation of the object of form numerator/denominator. + */ + @Override + public String toString() + { + return "" + _numerator + "/" + _denominator; + } + + /** + * Compares two {@link Rational} instances, returning true if they are mathematically + * equivalent. + * + * @param obj the {@link Rational} to compare this instance to. + * @return true if instances are mathematically equivalent, otherwise false. Will also + * give false if obj is not an instance of {@link Rational}. + */ + @Override + public boolean equals( Object obj) + { + if (obj==null || !(obj instanceof Rational)) + return false; + Rational that = (Rational) obj; + return this.doubleValue() == that.doubleValue(); + } +} diff --git a/tim/prune/jpeg/drew/TiffDataFormat.java b/tim/prune/jpeg/drew/TiffDataFormat.java new file mode 100644 index 0000000..6c89fc3 --- /dev/null +++ b/tim/prune/jpeg/drew/TiffDataFormat.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2015 Drew Noakes + * + * More information about this project is available at: + * + * https://drewnoakes.com/code/exif/ + * https://github.com/drewnoakes/metadata-extractor + */ +package tim.prune.jpeg.drew; + +/** + * An enumeration of data formats used by the TIFF specification. + * + * @author Drew Noakes https://drewnoakes.com + */ +public class TiffDataFormat +{ + public static final int CODE_INT8_U = 1; + public static final int CODE_STRING = 2; + public static final int CODE_INT16_U = 3; + public static final int CODE_INT32_U = 4; + public static final int CODE_RATIONAL_U = 5; + public static final int CODE_INT8_S = 6; + public static final int CODE_UNDEFINED = 7; + public static final int CODE_INT16_S = 8; + public static final int CODE_INT32_S = 9; + public static final int CODE_RATIONAL_S = 10; + public static final int CODE_SINGLE = 11; + public static final int CODE_DOUBLE = 12; + + public static int getComponentSize(int tiffFormatCode) + { + switch (tiffFormatCode) + { + case CODE_INT8_U: + case CODE_STRING: + case CODE_INT8_S: + case CODE_UNDEFINED: + return 1; + case CODE_INT16_U: + case CODE_INT16_S: + case CODE_INT32_U: + case CODE_INT32_S: + case CODE_SINGLE: + return 4; + case CODE_RATIONAL_U: + case CODE_RATIONAL_S: + case CODE_DOUBLE: + return 8; + default: + return 0; + } + } +} diff --git a/tim/prune/jpeg/drew/TiffProcessor.java b/tim/prune/jpeg/drew/TiffProcessor.java new file mode 100644 index 0000000..2811a7f --- /dev/null +++ b/tim/prune/jpeg/drew/TiffProcessor.java @@ -0,0 +1,298 @@ +/* + * Copyright 2002-2015 Drew Noakes + * + * More information about this project is available at: + * + * https://drewnoakes.com/code/exif/ + * https://github.com/drewnoakes/metadata-extractor + */ +package tim.prune.jpeg.drew; + + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import tim.prune.jpeg.JpegData; + +/** + * Processes TIFF-formatted data, using an ExifTiffHandler + * + * @author Drew Noakes https://drewnoakes.com + */ +public class TiffProcessor +{ + /** + * Processes a TIFF data sequence. + * + * @param reader the {@link RandomAccessReader} from which the data should be read + * @param jpegData the data to populate + * @param tiffHeaderOffset the offset within reader at which the TIFF header starts + * @throws ExifException if an error occurred during the processing of TIFF data that could + * not be ignored or recovered from + * @throws IOException an error occurred while accessing the required data + */ + public static void processTiff(final ByteArrayReader reader, JpegData jpegData, + final int tiffHeaderOffset) throws ExifException, IOException + { + // This must be either "MM" or "II". + short byteOrderIdentifier = reader.getInt16(tiffHeaderOffset); + + if (byteOrderIdentifier == 0x4d4d) { // "MM" + reader.setMotorolaByteOrder(true); + } else if (byteOrderIdentifier == 0x4949) { // "II" + reader.setMotorolaByteOrder(false); + } else { + throw new ExifException("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier); + } + + + int firstIfdOffset = reader.getInt32(4 + tiffHeaderOffset) + tiffHeaderOffset; + + // David Ekholm sent a digital camera image that has this problem + if (firstIfdOffset >= reader.getLength() - 1) { + //handler.warn("First IFD offset is beyond the end of the TIFF data segment -- trying default offset"); + // First directory normally starts immediately after the offset bytes, so try that + firstIfdOffset = tiffHeaderOffset + 2 + 2 + 4; + } + + // Make a handler object to use for the processing + ExifTiffHandler handler = new ExifTiffHandler(jpegData); + + Set processedIfdOffsets = new HashSet(); + processDirectory(handler, reader, processedIfdOffsets, firstIfdOffset, tiffHeaderOffset, 0); + + handler.completed(reader, tiffHeaderOffset); + } + + /** + * Processes a TIFF IFD. + * + * IFD Header: + *
    + *
  • 2 bytes number of tags
  • + *
+ * Tag structure: + *
    + *
  • 2 bytes tag type
  • + *
  • 2 bytes format code (values 1 to 12, inclusive)
  • + *
  • 4 bytes component count
  • + *
  • 4 bytes inline value, or offset pointer if too large to fit in four bytes
  • + *
+ * + * @param handler the {@link ExifTiffHandler} that will coordinate processing and accept read values + * @param reader the byte reader from which the data should be read + * @param processedIfdOffsets the set of visited IFD offsets, to avoid revisiting the same IFD in an endless loop + * @param ifdOffset the offset within reader at which the IFD data starts + * @param tiffHeaderOffset the offset within reader at which the TIFF header starts + * @param inDirectoryId directory id + * @throws IOException an error occurred while accessing the required data + */ + private static void processDirectory(final ExifTiffHandler handler, + final ByteArrayReader reader, final Set processedIfdOffsets, + final int ifdOffset, final int tiffHeaderOffset, int inDirectoryId) throws ExifException + { + // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist + if (processedIfdOffsets.contains(Integer.valueOf(ifdOffset))) { + return; + } + + // remember that we've visited this directory so that we don't visit it again later + processedIfdOffsets.add(ifdOffset); + + if (ifdOffset >= reader.getLength() || ifdOffset < 0) { + //handler.error("Ignored IFD marked to start outside data segment"); + return; + } + + // First two bytes in the IFD are the number of tags in this directory + int dirTagCount = reader.getUInt16(ifdOffset); + + int dirLength = (2 + (12 * dirTagCount) + 4); + if (dirLength + ifdOffset > reader.getLength()) { + //handler.error("Illegally sized IFD"); + return; + } + + + // Handle each tag in this directory + // + int invalidTiffFormatCodeCount = 0; + for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++) + { + final int tagOffset = calculateTagOffset(ifdOffset, tagNumber); + + // 2 bytes for the tag id + final int childTagId = reader.getUInt16(tagOffset); + + // 2 bytes for the format code + final int formatCode = reader.getUInt16(tagOffset + 2); + final int componentSizeInBytes = TiffDataFormat.getComponentSize(formatCode); + + if (componentSizeInBytes == 0) + { + // This error suggests that we are processing at an incorrect index and will generate + // rubbish until we go out of bounds (which may be a while). + if (++invalidTiffFormatCodeCount > 5) { + //handler.error("Stopping processing as too many errors seen in TIFF IFD"); + return; + } + continue; + } + + // 4 bytes dictate the number of components in this tag's data + final int componentCount = reader.getInt32(tagOffset + 4); + if (componentCount < 0) { + //handler.error("Negative TIFF tag component count"); + continue; + } + + final int byteCount = componentCount * componentSizeInBytes; + + final int tagValueOffset; + if (byteCount > 4) + { + // If it's bigger than 4 bytes, the dir entry contains an offset. + final int offsetVal = reader.getInt32(tagOffset + 8); + if (offsetVal + byteCount > reader.getLength()) { + // Bogus pointer offset and / or byteCount value + //handler.error("Illegal TIFF tag pointer offset"); + continue; + } + tagValueOffset = tiffHeaderOffset + offsetVal; + } + else { + // 4 bytes or less and value is in the dir entry itself. + tagValueOffset = tagOffset + 8; + } + + if (tagValueOffset < 0 || tagValueOffset > reader.getLength()) { + //handler.error("Illegal TIFF tag pointer offset"); + continue; + } + + // Check that this tag isn't going to allocate outside the bounds of the data array. + // This addresses an uncommon OutOfMemoryError. + if (byteCount < 0 || tagValueOffset + byteCount > reader.getLength()) { + //handler.error("Illegal number of bytes for TIFF tag data: " + byteCount); + continue; + } + + // Special handling for tags that point to other IFDs + if (byteCount == 4 && handler.isTagIfdPointer(childTagId)) { + final int subDirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset); + processDirectory(handler, reader, processedIfdOffsets, subDirOffset, tiffHeaderOffset, childTagId); + } + else if (handler.isInterestingTag(inDirectoryId, childTagId)) + { + processTag(handler, childTagId, tagValueOffset, componentCount, formatCode, reader); + } + } + + // at the end of each IFD is an optional link to the next IFD + final int finalTagOffset = calculateTagOffset(ifdOffset, dirTagCount); + int nextIfdOffset = reader.getInt32(finalTagOffset); + if (nextIfdOffset != 0) + { + nextIfdOffset += tiffHeaderOffset; + if (nextIfdOffset >= reader.getLength()) { + // Last 4 bytes of IFD reference another IFD with an address that is out of bounds + // Note this could have been caused by jhead 1.3 cropping too much + return; + } + else if (nextIfdOffset < ifdOffset) { + // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory + return; + } + + processDirectory(handler, reader, processedIfdOffsets, nextIfdOffset, tiffHeaderOffset, inDirectoryId); + } + } + + + /** + * Process a single tag value + */ + private static void processTag(final ExifTiffHandler handler, + final int tagId, final int tagValueOffset, + final int componentCount, final int formatCode, + final ByteArrayReader reader) throws ExifException + { + switch (formatCode) + { + case TiffDataFormat.CODE_STRING: + handler.setString(tagId, reader.getNullTerminatedString(tagValueOffset, componentCount)); + break; + case TiffDataFormat.CODE_RATIONAL_S: + if (componentCount == 1) { + handler.setRational(tagId, new Rational(reader.getInt32(tagValueOffset), reader.getInt32(tagValueOffset + 4))); + } else if (componentCount > 1) { + Rational[] array = new Rational[componentCount]; + for (int i = 0; i < componentCount; i++) + array[i] = new Rational(reader.getInt32(tagValueOffset + (8 * i)), reader.getInt32(tagValueOffset + 4 + (8 * i))); + handler.setRationalArray(tagId, array); + } + break; + case TiffDataFormat.CODE_RATIONAL_U: + if (componentCount == 1) { + handler.setRational(tagId, new Rational(reader.getUInt32(tagValueOffset), reader.getUInt32(tagValueOffset + 4))); + } else if (componentCount > 1) { + Rational[] array = new Rational[componentCount]; + for (int i = 0; i < componentCount; i++) + array[i] = new Rational(reader.getUInt32(tagValueOffset + (8 * i)), reader.getUInt32(tagValueOffset + 4 + (8 * i))); + handler.setRationalArray(tagId, array); + } + break; + case TiffDataFormat.CODE_INT8_S: + if (componentCount == 1) { + handler.setIntegerValue(tagId, reader.getInt8(tagValueOffset)); + } + break; + case TiffDataFormat.CODE_INT8_U: + if (componentCount == 1) { + handler.setIntegerValue(tagId, reader.getUInt8(tagValueOffset)); + } + break; + case TiffDataFormat.CODE_INT16_S: + if (componentCount == 1) { + handler.setIntegerValue(tagId, reader.getInt16(tagValueOffset)); + } + break; + case TiffDataFormat.CODE_INT16_U: + if (componentCount == 1) { + handler.setIntegerValue(tagId, reader.getUInt16(tagValueOffset)); + } + break; + case TiffDataFormat.CODE_INT32_S: + // NOTE 'long' in this case means 32 bit, not 64 + if (componentCount == 1) { + handler.setIntegerValue(tagId, reader.getInt32(tagValueOffset)); + } + break; + case TiffDataFormat.CODE_INT32_U: + // NOTE 'long' in this case means 32 bit, not 64 + if (componentCount == 1) { + handler.setRational(tagId, new Rational(reader.getUInt32(tagValueOffset), 1L)); + } + break; + case TiffDataFormat.CODE_SINGLE: + case TiffDataFormat.CODE_DOUBLE: + case TiffDataFormat.CODE_UNDEFINED: + default: + break; + } + } + + /** + * Determine the offset of a given tag within the specified IFD. + * + * @param ifdStartOffset the offset at which the IFD starts + * @param entryNumber the zero-based entry number + */ + private static int calculateTagOffset(int ifdStartOffset, int entryNumber) + { + // Add 2 bytes for the tag count. + // Each entry is 12 bytes. + return ifdStartOffset + 2 + (12 * entryNumber); + } +} diff --git a/tim/prune/lang/prune-texts_af.properties b/tim/prune/lang/prune-texts_af.properties index 063f494..8864d09 100644 --- a/tim/prune/lang/prune-texts_af.properties +++ b/tim/prune/lang/prune-texts_af.properties @@ -82,7 +82,6 @@ function.sendtogps=Stuur data na GPS function.exportkml=Voer uit na KML function.exportgpx=Voer uit na GPX function.exportpov=Voer uit na POV -function.exportsvg=Voer uit na SVG function.exportimage=Voer uit na beeld function.editwaypointname=Redigeer baken naam function.compress=Kompakteer baan @@ -118,7 +117,6 @@ function.searchopencachingde=Soek OpenCaching.de function.downloadosm=Laai OSM data vir area af function.duplicatepoint=Dupliseer punt function.setcolours=Stel kleure -function.setlinewidth=Stel lyn dikte function.setlanguage=Stel tale function.connecttopoint=Koppel na punt function.disconnectfrompoint=Ontkoppel van huidige punt @@ -261,10 +259,6 @@ dialog.baseimage.zoom=Vergrotings vlak dialog.baseimage.incomplete=Beeld onvolledig dialog.baseimage.tiles=Te\u00ebls dialog.baseimage.size=Beeld groote -dialog.exportsvg.text=Selekteer die parameters vir die SVG uitvoer -dialog.exportsvg.phi=Azimuth hoek \u03D5 -dialog.exportsvg.theta=Opstandings angle \u03B8 -dialog.exportsvg.gradients=Grbruik gradient vir skakering dialog.exportimage.noimagepossible=Kaart beelde moet gestoor word na skuif sodat dit gebruik kan word vir uitvoer dialog.exportimage.drawtrack=Teken baan op kaart dialog.exportimage.drawtrackpoints=Teken baan punte @@ -438,12 +432,12 @@ dialog.deletemarked.nonefound=Geen data punte 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://gpsprune.activityworkshop.net/\n vir meer inligting en gebruikers handleidings. +dialog.help.help=Sien asseblief\n https://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. dialog.about.summarytext2=Dit is vrygestel onder die Gnu GPL gratif, oop w\u00eareldwye gebruik en verbetering.
Kopi\u00ebring, herverspreiding en veranderings word toegelaat en aangemoedig
volgens die kondisies in the aangegte license.txt le\u00ear. -dialog.about.summarytext3=Sien asseblief vir mer inligting en wenke, insluitend
'n nuwe PDF gebruikers gids wat jy kan koop. +dialog.about.summarytext3=Sien asseblief https://activityworkshop.net/ vir mer inligting en wenke, insluitend
'n nuwe PDF gebruikers gids wat jy kan koop. dialog.about.languages=Beskikbare tale dialog.about.translatedby=Afrikaans teks deur ...? dialog.about.systeminfo=Stelsel informasie @@ -454,11 +448,6 @@ dialog.about.systeminfo.povray=Povray geinstalleer dialog.about.systeminfo.exiftool=Exiftool geinstalleer dialog.about.systeminfo.gpsbabel=Gpsbabel geinstalleer dialog.about.systeminfo.gnuplot=Gnuplot geinstalleer -dialog.about.systeminfo.exiflib=Exif biblioteek -dialog.about.systeminfo.exiflib.internal=Intern -dialog.about.systeminfo.exiflib.internal.failed=Interne (nie gevind) -dialog.about.systeminfo.exiflib.external=Ekstern -dialog.about.systeminfo.exiflib.external.failed=Ekstern (nie gevind) dialog.about.yes=Ja dialog.about.no=Nee dialog.about.credits=Krediete @@ -477,7 +466,7 @@ dialog.checkversion.newversion1='n nuwe weergawe van GpsPrune is nou beskikbaar! dialog.checkversion.newversion2=. dialog.checkversion.releasedate1=Hierdie nuwe weergawe was vrygestel op dialog.checkversion.releasedate2=. -dialog.checkversion.download=Om die nuwe weergawe aftelaai, gaan na http://gpsprune.activityworkshop.net/download.html. +dialog.checkversion.download=Om die nuwe weergawe aftelaai, gaan na https://gpsprune.activityworkshop.net/download.html. dialog.keys.intro=Jy kan die volgende kortpadsleutels gebruik in plaas van om die muis te gebruik dialog.keys.keylist=
Pylkie sleutelsSkuif kaart links regs, op, af
Ctrl + links, regs pylkieSelekteer vorige of volgende punt
Ctrl + op, af pylkieVergroot in of uit
Ctrl + PgUp, PgDownSelekteer vorige, volgende segment
Ctrl + Home, EindeSelekteer eerste, laaste punt
DelVerwyder huidige punt
dialog.keys.normalmodifier=Ctrl @@ -552,7 +541,7 @@ dialog.diskcache.deleteall=Verwyder alle te\u00ebls dialog.diskcache.deleted=%d leers verwyder vanuit datastoor dialog.deletefieldvalues.intro=Selekteer die veld om te verwyder vanuit die hudige reeks dialog.deletefieldvalues.nofields=Daar is geen velde om te verwyder vir hierdie reeks nie -dialog.setlinewidth.text=Sleutel die dikte van die lyne om te teken vir die bane (1-4) +dialog.displaysettings.linewidth=Dikte van die lyne vir die bane (1-4) dialog.downloadosm.desc=Bevestig die aflaai van rou OSM data vir die geslekteerde area: dialog.searchwikipedianames.search=Soek vir: dialog.weather.location=Plek @@ -786,7 +775,6 @@ wikipedia.lang=en openweathermap.lang=en webservice.peakfinder=Open Peakfinder.org webservice.geohack=Open Geohack bladsy -webservice.panoramio=Open Panoramio kaart ## Cardinals for 3d plots cardinal.n=N diff --git a/tim/prune/lang/prune-texts_cz.properties b/tim/prune/lang/prune-texts_cz.properties index c060e88..79627f3 100644 --- a/tim/prune/lang/prune-texts_cz.properties +++ b/tim/prune/lang/prune-texts_cz.properties @@ -39,7 +39,7 @@ menu.view.browser.yahoo=Mapy Yahoo menu.view.browser.bing=Mapy Bing menu.settings=Nastaven\u00ed menu.settings.onlinemode=Na\u010d\u00edtat mapy z internetu -menu.settings.antialias=Pou\u017e\u00edt antialiasing +dialog.displaysettings.antialias=Pou\u017e\u00edt antialiasing menu.settings.autosave=P\u0159i ukon\u010den\u00ed automaticky ukl\u00e1dat menu.help=Pomoc # Popup menu for map @@ -83,7 +83,6 @@ function.sendtogps=Poslat data do GPS function.exportkml=Export KML function.exportgpx=Export GPX function.exportpov=Export POV -function.exportsvg=Export SVG function.exportimage=Export obrazu mapy function.editwaypointname=Nastavit n\u00e1zev v\u00fdzna\u010dn\u00e9ho bodu function.compress=Komprimovat stopu @@ -117,7 +116,6 @@ function.mapillary=Hledat fotografie v Mapillary function.downloadosm=St\u00e1hnout data OSM pro oblast function.duplicatepoint=Zdvojit bod function.setcolours=Nastavit barvy -function.setlinewidth=Nastavit tlou\u0161\u0165ku \u010d\u00e1ry function.setlanguage=Nastavit jazyk function.connecttopoint=P\u0159ipojit do bodu function.disconnectfrompoint=Odpojit od bodu @@ -259,10 +257,6 @@ dialog.baseimage.zoom=Zv\u011bt\u0161en\u00ed dialog.baseimage.incomplete=Obr\u00e1zek ne\u00fapln\u00fd dialog.baseimage.tiles=Dla\u017edic dialog.baseimage.size=Velikost obr\u00e1zku -dialog.exportsvg.text=Zvolte parametry exportu do SVG -dialog.exportsvg.phi=Azimut \u03d5 -dialog.exportsvg.theta=V\u00fd\u0161kov\u00fd \u00fahel \u03b8 -dialog.exportsvg.gradients=Vypl\u0148ovat body barevn\u00fdm p\u0159echodem dialog.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=Vykreslit linii stopy dialog.exportimage.drawtrackpoints=Vykreslit body stopy @@ -377,9 +371,14 @@ dialog.gpsies.activity.motorbiking=Motorka dialog.gpsies.activity.snowshoe=Sn\u011b\u017enice dialog.gpsies.activity.sailing=Lo\u010f dialog.gpsies.activity.skating=Bruslen\u00ed +dialog.mapillary.nonefound=Nenalezeny \u017e\u00e1dn\u00e9 fotografie dialog.wikipedia.column.name=N\u00e1zev \u010dl\u00e1nku dialog.wikipedia.column.distance=Vzd\u00e1lenost +dialog.wikipedia.nonefound=Nenalezeny \u017e\u00e1dn\u00e9 body dialog.wikipedia.gallery=Galerie +dialog.osmpois.column.name=N\u00e1zev +dialog.osmpois.column.type=Typ +dialog.osmpois.nonefound=Nenalezeny \u017e\u00e1dn\u00e9 body dialog.correlate.notimestamps=U bod\u016f nejsou \u010dasov\u00e9 zna\u010dky, tak\u017ee nen\u00ed s \u010d\u00edm fotografie sladit. dialog.correlate.nouncorrelatedphotos=V\u0161echny fotografie jsou slad\u011bn\u00e9.\nOpravdu chcete pokra\u010dovat? dialog.correlate.nouncorrelatedaudios=V\u0161echny audionahr\u00e1vky jsou slad\u011bn\u00e9.\nOpravdu chcete pokra\u010dovat? @@ -437,12 +436,12 @@ dialog.deletemarked.nonefound=Nemohou b\u00fdt odstran\u011bny \u017e\u00e1dn\u0 dialog.pastecoordinates.desc=Zadejte sou\u0159adnice dialog.pastecoordinates.coords=Sou\u0159adnice dialog.pastecoordinates.nothingfound=Pros\u00edm ov\u011b\u0159te sou\u0159adnice a zkuste znovu -dialog.help.help=V\u00edce informac\u00ed a tip\u016f (bohu\u017eel nikoli v \u010de\u0161tin\u011b) naleznete na adrese:\n http://gpsprune.activityworkshop.net/ Je tak\u00e9 mo\u017en\u00e9 zakoupit novou u\u017eivatelskou p\u0159\u00edru\u010dku v PDF. +dialog.help.help=V\u00edce informac\u00ed a tip\u016f (bohu\u017eel nikoli v \u010de\u0161tin\u011b) naleznete na adrese:\n https://gpsprune.activityworkshop.net/ Je tak\u00e9 mo\u017en\u00e9 zakoupit novou u\u017eivatelskou p\u0159\u00edru\u010dku v PDF. dialog.about.version=Verze dialog.about.build=Build dialog.about.summarytext1=GpsPrune je program k na\u010d\u00edt\u00e1n\u00ed, zobrazov\u00e1n\u00ed a editaci dat z navigac\u00ed GPS. dialog.about.summarytext2=Je vyd\u00e1n pod licenc\u00ed GNU GPL, tak\u017ee je celosv\u011btov\u011b zdarma a svobodn\u011b k u\u017e\u00edv\u00e1n\u00ed a vylep\u0161ov\u00e1n\u00ed.
Kop\u00edrov\u00e1n\u00ed, redistribuce a \u00fapravy jsou povoleny a podporov\u00e1ny
podle podm\u00ednek popsan\u00fdch v souboru licence.txt. -dialog.about.summarytext3=V\u00edce informac\u00ed a tip\u016f (bohu\u017eel nikoli v \u010de\u0161tin\u011b) naleznete
na adrese: http://activityworkshop.net/. Je tak\u00e9 mo\u017en\u00e9 zakoupit novou u\u017eivatelskou p\u0159\u00edru\u010dku v PDF. +dialog.about.summarytext3=V\u00edce informac\u00ed a tip\u016f (bohu\u017eel nikoli v \u010de\u0161tin\u011b) naleznete
na adrese: https://activityworkshop.net/. Je tak\u00e9 mo\u017en\u00e9 zakoupit novou u\u017eivatelskou p\u0159\u00edru\u010dku v PDF. dialog.about.languages=Dostupn\u00e9 jazyky dialog.about.translatedby=\u010cesk\u00fd p\u0159eklad od prot_d. dialog.about.systeminfo=Syst\u00e9mov\u00e9 informace @@ -453,11 +452,6 @@ dialog.about.systeminfo.povray=Povray nainstalov\u00e1n dialog.about.systeminfo.exiftool=Exiftool nainstalov\u00e1n dialog.about.systeminfo.gpsbabel=Gpsbabel nainstalov\u00e1n dialog.about.systeminfo.gnuplot=Gnuplot nainstalov\u00e1n -dialog.about.systeminfo.exiflib=Knihovna Exif -dialog.about.systeminfo.exiflib.internal=Intern\u00ed -dialog.about.systeminfo.exiflib.internal.failed=Intern\u00ed (nenalezeno) -dialog.about.systeminfo.exiflib.external=Extern\u00ed -dialog.about.systeminfo.exiflib.external.failed=Extern\u00ed (nenalezeno) dialog.about.yes=Ano dialog.about.no=Ne dialog.about.credits=Auto\u0159i a spolupracovn\u00edci @@ -476,7 +470,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://gpsprune.activityworkshop.net/download.html. +dialog.checkversion.download=Novou verzi m\u016f\u017eete st\u00e1hnout na adrese https://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 @@ -542,8 +536,10 @@ dialog.diskcache.deleteall=Smazat v\u0161echny soubory 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.displaysettings.linewidth=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.displaysettings.wpicon.plectrum=Trs\u00e1tko +dialog.displaysettings.wpicon.ring=Krou\u017Eek dialog.searchwikipedianames.search=Vyhledat: dialog.weather.location=M\u00edsto dialog.weather.update=Posledn\u00ed aktualizace @@ -776,7 +772,6 @@ wikipedia.lang=cs openweathermap.lang=en webservice.peakfinder=Otev\u0159\u00edt Peakfinder.org webservice.geohack=Otev\u0159\u00edt Geohack -webservice.panoramio=Otev\u0159\u00edt Panoramio # Cardinals for 3d plots cardinal.n=N @@ -834,7 +829,6 @@ error.jpegload.dialogtitle=Chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed fotografi error.jpegload.nofilesfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory error.jpegload.nojpegsfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory jpeg error.jpegload.nogpsfound=Nenalezena informace GPS -error.jpegload.exifreadfailed=Nepoda\u0159ilo se na\u010d\u00edst informaci Exif. Tu nelze na\u010d\u00edst\nbez intern\u00ed nebo extern\u00ed knihovny. error.audioload.nofilesfound=Nebyly nalezeny \u017e\u00e1dn\u00e9 zvukov\u00e9 soubory. error.gpsload.unknown=Nezn\u00e1m\u00e1 chyba error.undofailed.title=Selhalo undo diff --git a/tim/prune/lang/prune-texts_da.properties b/tim/prune/lang/prune-texts_da.properties index e1d29ae..e975e29 100644 --- a/tim/prune/lang/prune-texts_da.properties +++ b/tim/prune/lang/prune-texts_da.properties @@ -63,7 +63,6 @@ function.sendtogps=Overf\u00f8r data til GPS function.exportkml=Eksport\u00e9r KML function.exportgpx=Eksport\u00e9r GPX function.exportpov=Eksport\u00e9r POV -function.exportsvg=Eksport\u00e9r SVG function.editwaypointname=Ret waypoint-navn function.compress=Komprim\u00e9r spor function.deleterange=Fjern det valgte omr\u00e5de diff --git a/tim/prune/lang/prune-texts_de.properties b/tim/prune/lang/prune-texts_de.properties index 26a3df3..bce51c6 100644 --- a/tim/prune/lang/prune-texts_de.properties +++ b/tim/prune/lang/prune-texts_de.properties @@ -12,8 +12,6 @@ menu.track=Track menu.track.undo=R\u00fcckg\u00e4ngig menu.track.clearundo=Liste der letzten \u00c4nderungen l\u00f6schen menu.track.markrectangle=Punkte im Viereck markieren -function.deletemarked=Markierte Punkte l\u00f6schen -function.rearrangewaypoints=Wegpunkte neu anordnen menu.range=Bereich menu.range.all=Alles markieren menu.range.none=Nichts markieren @@ -39,7 +37,6 @@ menu.view.browser.yahoo=Yahoo Maps menu.view.browser.bing=Bing Maps menu.settings=Einstellungen menu.settings.onlinemode=Karten aus dem Internet laden -menu.settings.antialias=Kantengl\u00e4ttung an menu.settings.autosave=Einstellungen automatisch speichern menu.help=Hilfe # Popup menu for map @@ -83,11 +80,12 @@ function.sendtogps=Zum GPS schicken function.exportkml=KML exportieren function.exportgpx=GPX exportieren function.exportpov=POV exportieren -function.exportsvg=SVG exportieren function.exportimage=Bild exportieren function.editwaypointname=Namen des Punkts bearbeiten function.compress=Track komprimieren function.marklifts=Bergbahnen markieren +function.deletemarked=Markierte Punkte l\u00f6schen +function.rearrangewaypoints=Wegpunkte neu anordnen function.deleterange=Bereich l\u00f6schen function.croptrack=Track zuschneiden function.interpolate=Punkte interpolieren @@ -110,18 +108,20 @@ function.setpaths=Programmpfade setzen function.selectsegment=Aktuellen Abschnitt markieren function.splitsegments=In Trackabschnitte schneiden function.sewsegments=Trackabschnitte zusammenf\u00fcgen +function.createmarkerwaypoints=Wegpunkte im bestimmten Abstand kreieren function.getgpsies=Tracks bei GPSies.com herunterladen function.uploadgpsies=Track zu GPSies.com hochladen function.lookupsrtm=H\u00f6hendaten von SRTM nachschlagen function.downloadsrtm=SRTM Dateien herunterladen function.getwikipedia=Wikipediaartikel in der N\u00e4he nachschlagen function.searchwikipedianames=Wikipedia nach Namen durchsuchen +function.searchosmpois=OpenStreetMap nach Punkten durchsuchen function.searchopencachingde=OpenCaching.de durchsuchen function.mapillary=Mapillary nach Fotos durchsuchen function.downloadosm=OSM-Daten f\u00fcr dieses Gebiet herunterladen function.duplicatepoint=Punkt verdoppeln function.setcolours=Farben einstellen -function.setlinewidth=Liniendicke einstellen +function.setdisplaysettings=Darstellungsoptionen function.setlanguage=Sprache einstellen function.connecttopoint=Mit Punkt verkn\u00fcpfen function.disconnectfrompoint=Vom Punkt trennen @@ -146,6 +146,7 @@ function.diskcache=Karten auf Festplatte speichern function.managetilecache=Kartenkacheln verwalten function.getweatherforecast=Wettervorhersage herunterladen function.setaltitudetolerance=Altitudtoleranz einstellen +function.selecttimezone=Zeitzone bestimmen # Dialogs dialog.exit.confirm.title=GpsPrune beenden @@ -264,10 +265,6 @@ dialog.baseimage.zoom=Zoomstufe dialog.baseimage.incomplete=Bild unvollst\u00e4ndig dialog.baseimage.tiles=Kacheln dialog.baseimage.size=Bildgr\u00f6\u00dfe -dialog.exportsvg.text=W\u00e4hlen Sie die Parameter f\u00fcr den SVG-Export aus -dialog.exportsvg.phi=Richtungswinkel \u03d5 -dialog.exportsvg.theta=Neigungswinkel \u03b8 -dialog.exportsvg.gradients=Farbverl\u00e4ufe verwenden 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 @@ -387,6 +384,9 @@ dialog.wikipedia.column.name=Artikelname dialog.wikipedia.column.distance=Entfernung dialog.wikipedia.nonefound=Keine Punkte gefunden dialog.wikipedia.gallery=Bilder +dialog.osmpois.column.name=Name +dialog.osmpois.column.type=Punkttyp +dialog.osmpois.nonefound=Keine Punkte gefunden dialog.geocaching.nonefound=Keine Punkte gefunden dialog.correlate.notimestamps=Die Punkte enthalten keine Zeitangaben, deshalb k\u00f6nnen die Fotos nicht zugeordnet werden. dialog.correlate.nouncorrelatedphotos=Alle Fotos sind schon zugeordnet.\nWollen Sie trotzdem fortfahren? @@ -446,12 +446,12 @@ 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://gpsprune.activityworkshop.net/ +dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n https://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. dialog.about.summarytext2=Es wird unter der Gnu GPL zur Verf\u00fcgung gestellt zum freien, kostenlosen und offenen Gebrauch und zur Weiterentwicklung.
Das Kopieren, Weiterverbreiten und Ver\u00e4ndern ist erlaubt und willkommen
unter den in der Datei license.txt enthaltenen Bedingungen. -dialog.about.summarytext3=Auf der Seite http://activityworkshop.net/ finden Sie weitere Informationen und Bedienungsanleitungen. +dialog.about.summarytext3=Auf der Seite https://activityworkshop.net/ finden Sie weitere Informationen und Bedienungsanleitungen. dialog.about.languages=Verf\u00fcgbare Sprachen dialog.about.translatedby=Deutsche \u00dcbersetzung von activityworkshop. dialog.about.systeminfo=System-Informationen @@ -462,11 +462,6 @@ dialog.about.systeminfo.povray=Povray installiert dialog.about.systeminfo.exiftool=ExifTool installiert dialog.about.systeminfo.gpsbabel=GPSBabel installiert dialog.about.systeminfo.gnuplot=Gnuplot installiert -dialog.about.systeminfo.exiflib=Exif-Bibliothek -dialog.about.systeminfo.exiflib.internal=Intern -dialog.about.systeminfo.exiflib.internal.failed=Intern (nicht gefunden) -dialog.about.systeminfo.exiflib.external=Extern -dialog.about.systeminfo.exiflib.external.failed=Extern (nicht gefunden) dialog.about.yes=Ja dialog.about.no=Nein dialog.about.credits=Danksagung @@ -485,7 +480,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://gpsprune.activityworkshop.net/download.html. +dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu https://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, abZum vorherigen oder n\u00e4chsten Abschnitt
Strg + Pos1, EndeErsten oder letzten Punkt markieren
EntfAktuellen Punkt entfernen
dialog.keys.normalmodifier=Strg @@ -564,7 +559,17 @@ dialog.diskcache.deleteall=Alle Kacheln l\u00f6schen 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.displaysettings.linewidth=Dicke der Linien in Pixeln (1-4) +dialog.displaysettings.antialias=Kantengl\u00e4ttung an +dialog.displaysettings.waypointicons=Wegpunkt Ikons +dialog.displaysettings.wpicon.default=Punkt +dialog.displaysettings.wpicon.ringpt=Rundes Schild +dialog.displaysettings.wpicon.plectrum=Plektrum +dialog.displaysettings.wpicon.ring=Kreis +dialog.displaysettings.wpicon.pin=Sto\u00dfnadel +dialog.displaysettings.size.small=Klein +dialog.displaysettings.size.medium=Mittel +dialog.displaysettings.size.large=Gro\u00df dialog.downloadosm.desc=Die OpenStreetMap-Daten f\u00fcr das folgende Gebiet werden heruntergeladen (.osm-Datei): dialog.searchwikipedianames.search=Suche nach: dialog.weather.location=Ort @@ -596,6 +601,12 @@ dialog.deletebydate.column.keep=Behalten dialog.deletebydate.column.delete=L\u00f6schen dialog.setaltitudetolerance.text.metres=Mindestabweichung (Meter) f\u00fcr H\u00f6henunterschiede dialog.setaltitudetolerance.text.feet=Mindestabweichung (Feet) f\u00fcr H\u00f6henunterschiede +dialog.settimezone.intro=Hier k\u00f6nnen Sie die Zeitzone ausw\u00e4hlen, in der die Punkt Zeitstempeln angezeigt werden +dialog.settimezone.system=Zeitzone des Systems verweden +dialog.settimezone.custom=Folgende Zeitzone verwenden: +dialog.settimezone.list.toomany=Zu viele zum Anzeigen +dialog.settimezone.selectedzone=Ausgew\u00e4hlte Zeitzone +dialog.settimezone.offsetfromutc=Unterschied zur UTC dialog.autoplay.duration=Abspieldauer (Sek) dialog.autoplay.usetimestamps=Zeitstempeln verwenden dialog.autoplay.rewind=Zum Anfang @@ -806,7 +817,6 @@ wikipedia.lang=de openweathermap.lang=de webservice.peakfinder=Peakfinder.org \u00f6ffnen webservice.geohack=Geohack-Seite \u00f6ffnen -webservice.panoramio=Panoramio Karte \u00f6ffnen # Cardinals for 3d plots cardinal.n=N @@ -864,7 +874,6 @@ error.jpegload.dialogtitle=Fehler beim Laden von Fotos error.jpegload.nofilesfound=Keine Dateien gefunden error.jpegload.nojpegsfound=Keine JPG-Dateien gefunden error.jpegload.nogpsfound=Keine GPS-Information gefunden -error.jpegload.exifreadfailed=Exif-Aufruf fehlgeschlagen. Exif-Information k\u00f6nnen nicht gelesen werden\nwenn nicht eine interne oder externe Bibliothek vorhanden ist. error.audioload.nofilesfound=Keine Audiodateien gefunden error.gpsload.unknown=Unbekannter Fehler error.undofailed.title=Undo fehlgeschlagen diff --git a/tim/prune/lang/prune-texts_de_CH.properties b/tim/prune/lang/prune-texts_de_CH.properties index 9697e39..d7deef6 100644 --- a/tim/prune/lang/prune-texts_de_CH.properties +++ b/tim/prune/lang/prune-texts_de_CH.properties @@ -38,7 +38,7 @@ menu.view.showsidebars=Seiteleischten aazeige menu.view.browser=Karte inem Browser menu.settings=Iistellige menu.settings.onlinemode=Karten uusem Internet lade -menu.settings.antialias=Kantegl\u00e4ttig aa +dialog.displaysettings.antialias=Kantegl\u00e4ttig aa menu.settings.autosave=Iistellige automatisch speichere menu.help=Hilfe # Popup menu for map @@ -82,7 +82,6 @@ function.sendtogps=zum GPS schicke function.exportkml=KML exportier\u00e4 function.exportgpx=GPX exportier\u00e4 function.exportpov=POV exportier\u00e4 -function.exportsvg=SVG exportier\u00e4 function.exportimage=Bild exportier\u00e4 function.editwaypointname=Waypoint Namen editiere function.compress=Track komprimier\u00e4 @@ -107,14 +106,16 @@ function.setmapbg=Karte Hintegrund setz\u00e4 function.selectsegment=Aktuelli Segm\u00e4nt selektiere function.splitsegments=In Tracksegm\u00e4nte schniide function.sewsegments=Tracksegm\u00e4nte z\u00e4mef\u00fcge +function.createmarkerwaypoints=Waypoints inem bestimmten Abstand kreiere 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 nachem Namen durasueche +function.searchosmpois=OpenStreetMap na P\u00fcnkt durasueche function.searchopencachingde=OpenCaching.de durasueche -function.mapillary=Mapillary nach F\u00f6telis durasueche +function.mapillary=Mapillary na F\u00f6telis durasueche function.downloadosm=OSM-Date f\u00fcr dere Gebiet abalad\u00e4 function.duplicatepoint=Punkt verdoppl\u00e4 function.correlatephotos=F\u00f6telis korrelier\u00e4 @@ -130,17 +131,18 @@ function.playaudio=Audiofile abspiel\u00e4 function.stopaudio=Abspielen abbr\u00e4ch\u00e4 function.setpaths=Programmepfade setz\u00e4 function.setcolours=Farben setz\u00e4 -function.setlinewidth=Liniedicke setz\u00e4 +function.setdisplaysettings=Darstelligsoptione function.setlanguage=Sproch setz\u00e4 function.help=Hilfe -function.showkeys=Tastekombinatione aazeige +function.showkeys=Tastekombinatione aazeig\u00e4 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 -function.setaltitudetolerance=H\u00f6chitoleranz iistelle +function.saveconfig=Iistellige speicher\u00e4 +function.diskcache=Karten uufem Disk speicher\u00e4 +function.managetilecache=Kartebildli verwolt\u00e4 +function.getweatherforecast=W\u00e4tterprognose abalad\u00e4 +function.setaltitudetolerance=H\u00f6chitoleranz iistell\u00e4 +function.selecttimezone=Ziitzone bstimm\u00e4 # Dialogs dialog.exit.confirm.title=GpsPrune be\u00e4nde @@ -237,7 +239,7 @@ dialog.exportgpx.desc=Beschriibig dialog.exportgpx.includetimestamps=Au Ziitst\u00e4mpel dialog.exportgpx.copysource=Xml-Qu\u00e4lle kopier\u00e4 dialog.exportgpx.encoding=Enkodierig -dialog.exportgpx.encoding.system=System +dialog.exportgpx.encoding.system=Syschtem dialog.exportgpx.encoding.utf8=UTF-8 dialog.exportpov.text=G\u00e4bet Sie die Parameter ii f\u00fcrs POV Export dialog.exportpov.font=Font @@ -259,10 +261,6 @@ dialog.baseimage.zoom=Zoomstufe dialog.baseimage.incomplete=Bild unvollst\u00e4ndig dialog.baseimage.tiles=Kachle dialog.baseimage.size=Bildgr\u00f6ssi -dialog.exportsvg.text=W\u00e4hlet Sie die Parameter f\u00fcrs SVG Export uus -dialog.exportsvg.phi=Richtigswinkel \u03D5 -dialog.exportsvg.theta=Neigigswinkel \u03B8 -dialog.exportsvg.gradients=Farbeverl\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 @@ -382,6 +380,9 @@ dialog.wikipedia.column.name=Artikelname dialog.wikipedia.column.distance=Entf\u00e4rnig dialog.wikipedia.nonefound=Kei Wiki-Iitr\u00e4ge gfunde dialog.wikipedia.gallery=Fotis +dialog.osmpois.column.name=Name +dialog.osmpois.column.type=Punkttyp +dialog.osmpois.nonefound=Kei P\u00fcnkt gfunde dialog.geocaching.nonefound=Kei C\u00e4ches gfunde dialog.correlate.notimestamps=Es h\u00e4t kei Ziitst\u00e4mpel inem Track inn\u00e4, so s'isch n\u00f6d m\u00f6glech die F\u00f6telis zu korrelier\u00e4. dialog.correlate.nouncorrelatedphotos=Alle F\u00f6telis sin scho korreliert.\nWend Sie trotzdem fortsetz\u00e4? @@ -441,27 +442,22 @@ dialog.deletemarked.nonefound=Kei P\u00fcnkte h\u00e4tte gel\u00f6scht werde k\u 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=Lueget Sie na\n http://gpsprune.activityworkshop.net/\nf\u00fcr wiitere Information und Benutzeraaleitige. +dialog.help.help=Lueget Sie na\n https://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.summarytext3=Bitte lueget Sie na https://activityworkshop.net/ f\u00fcr wiitere Informatione und Benutzeraaleitige. dialog.about.languages=Verf\u00fcegbare Sproche 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 -dialog.about.systeminfo.java3d=Java3d inschtalliert -dialog.about.systeminfo.povray=Povray inschtalliert -dialog.about.systeminfo.exiftool=Exiftool inschtalliert -dialog.about.systeminfo.gpsbabel=Gpsbabel inschtalliert -dialog.about.systeminfo.gnuplot=Gnuplot inschtalliert -dialog.about.systeminfo.exiflib=Exif Bibliothek -dialog.about.systeminfo.exiflib.internal=Intern -dialog.about.systeminfo.exiflib.internal.failed=Intern (n\u00f6d gfunde) -dialog.about.systeminfo.exiflib.external=Ext\u00e4rn -dialog.about.systeminfo.exiflib.external.failed=Ext\u00e4rn (n\u00f6d gfunde) +dialog.about.systeminfo.java3d=Java3d installiert +dialog.about.systeminfo.povray=Povray installiert +dialog.about.systeminfo.exiftool=Exiftool installiert +dialog.about.systeminfo.gpsbabel=Gpsbabel installiert +dialog.about.systeminfo.gnuplot=Gnuplot installiert dialog.about.yes=Ja dialog.about.no=Nei dialog.about.credits=Credits @@ -480,7 +476,7 @@ 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://gpsprune.activityworkshop.net/download.html. +dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na https://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, \u00c4ndeErschti oder letschti Punkt markiere
EntfAktuelli Punkt l\u00f6sche
dialog.keys.normalmodifier=Strg @@ -559,7 +555,16 @@ dialog.diskcache.deleteall=Alli Kachle l\u00f6sche 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.displaysettings.linewidth=Dicke vonen Linien in Pixeln (1-4) +dialog.displaysettings.waypointicons=Waypoint Ikons +dialog.displaysettings.wpicon.default=P\u00fcnktli +dialog.displaysettings.wpicon.ringpt=Rundes Schild +dialog.displaysettings.wpicon.plectrum=Plektrum +dialog.displaysettings.wpicon.ring=Chreis +dialog.displaysettings.wpicon.pin=Sto\u00dfnadel +dialog.displaysettings.size.small=Chli +dialog.displaysettings.size.medium=Mittel +dialog.displaysettings.size.large=Gross dialog.downloadosm.desc=Best\u00e4tige um rohi OSM Date f\u00fcrn Gebiet aba zlade: dialog.searchwikipedianames.search=Sueche na: dialog.weather.location=Ort @@ -591,6 +596,12 @@ dialog.deletebydate.column.keep=Behalte dialog.deletebydate.column.delete=L\u00f6sche dialog.setaltitudetolerance.text.metres=Mindeschtabweichig (Meter) f\u00fcr H\u00f6chiunterschied dialog.setaltitudetolerance.text.feet=Mindeschtabweichig (Feet) f\u00fcr H\u00f6chiunterschied +dialog.settimezone.intro=Do k\u00f6nnet Sie d Ziitzone uusw\u00e4hle, f\u00fcr d'aazeig vo P\u00fcnkt Ziitst\u00e4mple +dialog.settimezone.system=Ziitzone vonem Syschtem +dialog.settimezone.custom=Folgendi Ziitzone: +dialog.settimezone.list.toomany=Viu z'viu +dialog.settimezone.selectedzone=Uusgw\u00e4hlti Ziitzone +dialog.settimezone.offsetfromutc=Unterschied vo UTC dialog.autoplay.duration=Abspieldauer (Sek) dialog.autoplay.usetimestamps=Ziitst\u00e4mple verw\u00e4nde dialog.autoplay.rewind=Zum Aafang @@ -801,7 +812,6 @@ wikipedia.lang=als openweathermap.lang=de webservice.peakfinder=Peakfinder.org \u00f6ffne webservice.geohack=Geohack-Siite \u00f6ffne -webservice.panoramio=Panoramio Karte \u00f6ffne # Cardinals for 3d plots cardinal.n=N @@ -859,7 +869,6 @@ error.jpegload.dialogtitle=F\u00e4hle bim Lade von F\u00f6telis error.jpegload.nofilesfound=Kei Files gfunde error.jpegload.nojpegsfound=Kei Jpegs gfunde error.jpegload.nogpsfound=Kei GPS Information gfunde -error.jpegload.exifreadfailed=Exif Uufruef isch fehlgschlage. Kei Exif Infos k\u00f6nnet gl\u00e4se werde\nohni nen interni oder ext\u00e4rni Bibliothek. error.audioload.nofilesfound=Kei Audiofiles gfunde error.gpsload.unknown=Unbekannts F\u00e4hler error.undofailed.title=Undo isch fehlgschlage worde diff --git a/tim/prune/lang/prune-texts_en.properties b/tim/prune/lang/prune-texts_en.properties index 489b5c6..9e3b7cd 100644 --- a/tim/prune/lang/prune-texts_en.properties +++ b/tim/prune/lang/prune-texts_en.properties @@ -12,7 +12,6 @@ menu.track=Track menu.track.undo=Undo menu.track.clearundo=Clear undo list menu.track.markrectangle=Mark points in rectangle -function.deletemarked=Delete marked points menu.range=Range menu.range.all=Select all menu.range.none=Select none @@ -38,7 +37,6 @@ menu.view.browser.yahoo=Yahoo maps menu.view.browser.bing=Bing maps menu.settings=Settings menu.settings.onlinemode=Load maps from internet -menu.settings.antialias=Use antialiasing menu.settings.autosave=Autosave settings on exit menu.help=Help # Popup menu for map @@ -82,12 +80,12 @@ function.sendtogps=Send data to GPS function.exportkml=Export KML function.exportgpx=Export GPX function.exportpov=Export POV -function.exportsvg=Export SVG function.exportimage=Export image function.editwaypointname=Edit waypoint name function.compress=Compress track function.marklifts=Mark uphill lifts function.deleterange=Delete range +function.deletemarked=Delete marked points function.croptrack=Crop track function.interpolate=Interpolate points function.deletebydate=Delete points by date @@ -108,12 +106,14 @@ function.autoplay=Autoplay track function.selectsegment=Select current segment function.splitsegments=Split track into segments function.sewsegments=Sew track segments together +function.createmarkerwaypoints=Create marker waypoints 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.searchosmpois=Get nearby OSM points function.searchopencachingde=Search OpenCaching.de function.mapillary=Search for photos in Mapillary function.downloadosm=Download OSM data for area @@ -135,7 +135,7 @@ function.stopaudio=Stop audio clip function.setmapbg=Set map background function.setpaths=Set program paths function.setcolours=Set colours -function.setlinewidth=Set line width +function.setdisplaysettings=Set display options function.setlanguage=Set language function.help=Help function.showkeys=Show shortcut keys @@ -146,6 +146,7 @@ function.diskcache=Save maps to disk function.managetilecache=Manage tile cache function.getweatherforecast=Get weather forecast function.setaltitudetolerance=Set altitude tolerance +function.selecttimezone=Set timezone # Dialogs dialog.exit.confirm.title=Exit GpsPrune @@ -174,6 +175,7 @@ dialog.openoptions.deliminfo.norecords=No records dialog.openoptions.altitudeunits=Altitude units dialog.openoptions.speedunits=Speed units dialog.openoptions.vertspeedunits=Vertical speed units +dialog.openoptions.vspeed.intro= dialog.openoptions.vspeed.positiveup=Positive speeds upwards dialog.openoptions.vspeed.positivedown=Positive speeds downwards dialog.open.contentsdoubled=This file contains two copies of each point,\nonce as waypoints and once as track points. @@ -264,10 +266,6 @@ dialog.baseimage.zoom=Zoom level dialog.baseimage.incomplete=Image incomplete dialog.baseimage.tiles=Tiles dialog.baseimage.size=Image size -dialog.exportsvg.text=Select the parameters for the SVG export -dialog.exportsvg.phi=Azimuth angle \u03D5 -dialog.exportsvg.theta=Elevation angle \u03B8 -dialog.exportsvg.gradients=Use gradients for shading dialog.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 @@ -387,6 +385,9 @@ dialog.wikipedia.column.name=Article name dialog.wikipedia.column.distance=Distance dialog.wikipedia.nonefound=No wikipedia entries found dialog.wikipedia.gallery=Gallery +dialog.osmpois.column.name=Name +dialog.osmpois.column.type=Type +dialog.osmpois.nonefound=No points found dialog.geocaching.nonefound=No geocaches found dialog.correlate.notimestamps=There are no timestamps in the data points, so there is nothing to correlate with the photos. dialog.correlate.nouncorrelatedphotos=There are no uncorrelated photos.\nAre you sure you want to continue? @@ -446,12 +447,12 @@ dialog.deletemarked.nonefound=No data points could be removed dialog.pastecoordinates.desc=Enter or paste the coordinates here dialog.pastecoordinates.coords=Coordinates dialog.pastecoordinates.nothingfound=Please check the coordinates and try again -dialog.help.help=Please see\n http://gpsprune.activityworkshop.net/\nfor more information and tips,\nincluding a PDF user guide you can buy. +dialog.help.help=Please see\n https://gpsprune.activityworkshop.net/\nfor more information and tips,\nincluding a PDF user guide you can buy. dialog.about.version=Version dialog.about.build=Build dialog.about.summarytext1=GpsPrune is a program for loading, displaying and editing data from GPS receivers. dialog.about.summarytext2=It is released under the Gnu GPL for free, open, worldwide use and enhancement.
Copying, redistribution and modification are permitted and encouraged
according to the conditions in the included license.txt file. -dialog.about.summarytext3=Please see http://activityworkshop.net/ for more information and tips, including
a PDF user guide you can buy. +dialog.about.summarytext3=Please see https://activityworkshop.net/ for more information and tips, including
a PDF user guide you can buy. dialog.about.languages=Available languages dialog.about.translatedby=English text by activityworkshop. dialog.about.systeminfo=System info @@ -462,11 +463,6 @@ dialog.about.systeminfo.povray=Povray installed dialog.about.systeminfo.exiftool=Exiftool installed dialog.about.systeminfo.gpsbabel=Gpsbabel installed dialog.about.systeminfo.gnuplot=Gnuplot installed -dialog.about.systeminfo.exiflib=Exif library -dialog.about.systeminfo.exiflib.internal=Internal -dialog.about.systeminfo.exiflib.internal.failed=Internal (not found) -dialog.about.systeminfo.exiflib.external=External -dialog.about.systeminfo.exiflib.external.failed=External (not found) dialog.about.yes=Yes dialog.about.no=No dialog.about.credits=Credits @@ -485,7 +481,7 @@ dialog.checkversion.newversion1=A new version of GpsPrune is now available! The dialog.checkversion.newversion2=. dialog.checkversion.releasedate1=This new version was released on dialog.checkversion.releasedate2=. -dialog.checkversion.download=To download the new version, go to http://gpsprune.activityworkshop.net/download.html. +dialog.checkversion.download=To download the new version, go to https://gpsprune.activityworkshop.net/download.html. dialog.keys.intro=You can use the following shortcut keys instead of using the mouse dialog.keys.keylist=
Arrow keysPan map left right, up, down
Ctrl + left, right arrowSelect previous or next point
Ctrl + up, down arrowZoom in or out
Ctrl + PgUp, PgDownSelect previous, next segment
Ctrl + Home, EndSelect first, last point
DelDelete current point
dialog.keys.normalmodifier=Ctrl @@ -564,7 +560,17 @@ dialog.diskcache.deleteall=Delete all tiles 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.displaysettings.linewidth=Thickness of lines for the tracks (1-4) +dialog.displaysettings.antialias=Use antialiasing +dialog.displaysettings.waypointicons=Waypoint icons +dialog.displaysettings.wpicon.default=Default +dialog.displaysettings.wpicon.ringpt=Round marker +dialog.displaysettings.wpicon.plectrum=Plectrum +dialog.displaysettings.wpicon.ring=Ring +dialog.displaysettings.wpicon.pin=Board pin +dialog.displaysettings.size.small=Small +dialog.displaysettings.size.medium=Medium +dialog.displaysettings.size.large=Large dialog.downloadosm.desc=Confirm to download the raw OSM data for the specified area: dialog.searchwikipedianames.search=Search for: dialog.weather.location=Location @@ -596,6 +602,12 @@ dialog.deletebydate.column.keep=Keep dialog.deletebydate.column.delete=Delete dialog.setaltitudetolerance.text.metres=Limit (in metres) below which small climbs and descents will be ignored dialog.setaltitudetolerance.text.feet=Limit (in feet) below which small climbs and descents will be ignored +dialog.settimezone.intro=Here you can select the timezone in which to display point timestamps +dialog.settimezone.system=Use system timezone +dialog.settimezone.custom=Use the following timezone: +dialog.settimezone.list.toomany=Too many to choose +dialog.settimezone.selectedzone=Selected timezone +dialog.settimezone.offsetfromutc=Offset from UTC dialog.autoplay.duration=Duration (secs) dialog.autoplay.usetimestamps=Use point timestamps dialog.autoplay.rewind=Back to beginning @@ -812,7 +824,6 @@ wikipedia.lang=en openweathermap.lang=en webservice.peakfinder=Open Peakfinder.org webservice.geohack=Open Geohack page -webservice.panoramio=Open Panoramio map # Cardinals for 3d plots cardinal.n=N @@ -870,7 +881,6 @@ error.jpegload.dialogtitle=Error loading photos error.jpegload.nofilesfound=No files found error.jpegload.nojpegsfound=No jpeg files found error.jpegload.nogpsfound=No GPS information found -error.jpegload.exifreadfailed=Failed to read Exif information. No Exif information can be read\nwithout either an internal or external library. error.audioload.nofilesfound=No audio clips found error.gpsload.unknown=Unknown error error.undofailed.title=Undo failed diff --git a/tim/prune/lang/prune-texts_es.properties b/tim/prune/lang/prune-texts_es.properties index 53554dd..df1bc28 100644 --- a/tim/prune/lang/prune-texts_es.properties +++ b/tim/prune/lang/prune-texts_es.properties @@ -38,7 +38,7 @@ menu.view.browser.yahoo=Mapas Yahoo menu.view.browser.bing=Mapas Bing menu.settings=Preferencias menu.settings.onlinemode=Cargar mapas de Internet -menu.settings.antialias=Usar antialiasing +dialog.displaysettings.antialias=Usar antialiasing menu.settings.autosave=Auto Guardar menu.help=Ayuda @@ -83,7 +83,6 @@ function.sendtogps=Enviar datos al GPS function.exportkml=Exportar KML function.exportgpx=Exportar GPX function.exportpov=Exportar POV -function.exportsvg=Exportar SVG function.exportimage=Exportar imagen function.editwaypointname=Editar nombre de waypoint function.compress=Comprimir track @@ -122,7 +121,6 @@ function.mapillary=Buscar en Mapillary function.downloadosm=Descargar datos OSM del \u00e1rea function.duplicatepoint=Duplicar punto function.setcolours=Establecer color -function.setlinewidth=Establecer ancho de l\u00ednea function.setlanguage=Establecer lenguaje function.connecttopoint=Conectar con punto function.disconnectfrompoint=Desconectar de punto @@ -175,8 +173,9 @@ dialog.openoptions.deliminfo.norecords=Ningun dato dialog.openoptions.altitudeunits=Unidades altitud dialog.openoptions.speedunits=Unidades velocidad dialog.openoptions.vertspeedunits=Unidades velocidad vertical -dialog.openoptions.vspeed.positiveup=Velocidades positivos significan hacia arriba -dialog.openoptions.vspeed.positivedown=Velocidades positivos significan hacia abajo +dialog.openoptions.vspeed.intro=Velocidades positivos significan: +dialog.openoptions.vspeed.positiveup=hacia arriba +dialog.openoptions.vspeed.positivedown=hacia abajo dialog.open.contentsdoubled=Este archivo contiene dos copias de cada punto,\nuna como "waypoints" y otra como puntos de recorrido. dialog.selecttracks.intro=Seleccionar recorrido/s a cargar dialog.selecttracks.noname=Innominados @@ -269,10 +268,6 @@ dialog.baseimage.zoom=Zoom dialog.baseimage.incomplete=Imagen incompleta dialog.baseimage.tiles=Recuadros dialog.baseimage.size=Tama\u00f1o de la imagen -dialog.exportsvg.text=Seleccione los par\u00e1metros para exportar a SVG -dialog.exportsvg.phi=\u00c1ngulo de azimuth \u03d5 -dialog.exportsvg.theta=\u00c1ngulo de elevaci\u00f3n -dialog.exportsvg.gradients=Usar degradado para sombras dialog.exportimage.drawtrack=Dibujar track dialog.exportimage.drawtrackpoints=Dibujar puntos del track dialog.exportimage.textscalepercent=Agrandamiento del texto (%) @@ -388,6 +383,9 @@ dialog.wikipedia.column.name=Nombre del art\u00edculo dialog.wikipedia.column.distance=Distancia dialog.wikipedia.nonefound=No se encontraron puntos dialog.wikipedia.gallery=Galeria +dialog.osmpois.column.name=Nombre +dialog.osmpois.column.type=Tipo +dialog.osmpois.nonefound=No se encontraron puntos dialog.geocaching.nonefound=No se encontraron tesoros dialog.correlate.notimestamps=No hay informaci\u00f3n de tiempo para los puntos, as\u00ed que no hay nada que correlacionar con las fotos. dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar? @@ -447,12 +445,12 @@ dialog.deletemarked.nonefound=Ning\u00fan punto eliminado dialog.pastecoordinates.desc=Ingresar o pegar las coordenadas aqu\u00ed dialog.pastecoordinates.coords=Coordenadas dialog.pastecoordinates.nothingfound=Por favor verificar las coordenadas e intentar nuevamente -dialog.help.help=Por favor, ver\n http://gpsprune.activityworkshop.net/\npara m\u00e1s informaci\u00f3n y gu\u00edas del usuario. +dialog.help.help=Por favor, ver\n https://gpsprune.activityworkshop.net/\npara m\u00e1s informaci\u00f3n y gu\u00edas del usuario. dialog.about.version=Versi\u00f3n dialog.about.build=Construcci\u00f3n dialog.about.summarytext1=GpsPrune es un programa para cargar, mostrar y editar datos de receptores GPS. dialog.about.summarytext2=Distribuido bajo el GNU GPL para uso libre y gratuito.
Se permite (y se anima) la copia, redistribuci\u00f3n y modificaci\u00f3n de acuerdo
a las condiciones incluidas en el archivo licence.txt. -dialog.about.summarytext3=Por favor, ver http://activityworkshop.net/ para m\u00e1s informaci\u00f3n y gu\u00edas del usuario. +dialog.about.summarytext3=Por favor, ver https://activityworkshop.net/ para m\u00e1s informaci\u00f3n y gu\u00edas del usuario. dialog.about.languages=Idiomas disponibles dialog.about.translatedby=Traducci\u00f3n al espa\u00f1ol realizada por Miguel, In\u00e9s, josatoc y Javier dialog.about.systeminfo=Informacion del sistema @@ -463,11 +461,6 @@ dialog.about.systeminfo.povray=Povray instalado dialog.about.systeminfo.exiftool=Exiftool instalado dialog.about.systeminfo.gpsbabel=Gpsbabel instalado dialog.about.systeminfo.gnuplot=Gnuplot instalado -dialog.about.systeminfo.exiflib=Biblioteca exif -dialog.about.systeminfo.exiflib.internal=Interna -dialog.about.systeminfo.exiflib.internal.failed=Interna (no encontrada) -dialog.about.systeminfo.exiflib.external=Externa -dialog.about.systeminfo.exiflib.external.failed=Externa (no encontrada) dialog.about.yes=Si dialog.about.no=No dialog.about.credits=Cr\u00e9ditos @@ -486,7 +479,7 @@ dialog.checkversion.newversion1=\u00a1Una nueva versi\u00f3n de GpsPrune est\u00 dialog.checkversion.newversion2=. dialog.checkversion.releasedate1=La nueva versi\u00f3n fue lanzada en dialog.checkversion.releasedate2=. -dialog.checkversion.download=Para descargar la nueva versi\u00f3n visite http://gpsprune.activityworkshop.net/download.html. +dialog.checkversion.download=Para descargar la nueva versi\u00f3n visite https://gpsprune.activityworkshop.net/download.html. dialog.keys.intro=Usted puede usar el siguiente atajo en lugar de usar el rat\u00f3n dialog.keys.keylist=
Teclas de cursorDesplazar a la izquierde, derecha, arriba, abajo
Ctrl + cursor izquierda, derechaSeleccionar punto siguiente o anterior
Ctrl + cursor arriba, abajoAmpliar o reducir zoom
Ctrl + Av Pag, Re PagSeleccionar segmento siguiente, anterior
Ctrl + Inicio, FinSeleccionar primer, \u00faltimo punto
SuprEliminar punto actual
dialog.keys.normalmodifier=Ctrl @@ -565,7 +558,13 @@ dialog.diskcache.deleteall=Borrar todos los recuadros 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.displaysettings.linewidth=Anchura de las l\u00edneas para los recorridos (1-4) +dialog.displaysettings.waypointicons=Iconos de los waypoints +dialog.displaysettings.wpicon.plectrum=Plectro +dialog.displaysettings.wpicon.ring=Anillo +dialog.displaysettings.size.small=Peque\u00f1o +dialog.displaysettings.size.medium=Mediano +dialog.displaysettings.size.large=Grande dialog.downloadosm.desc=Confirmar la descarga de datos en bruto de OSM para el \u00e1rea especificada. dialog.searchwikipedianames.search=Buscar: dialog.weather.location=Localidad @@ -594,6 +593,7 @@ dialog.deletebydate.column.keep=Mantener dialog.deletebydate.column.delete=Eliminar dialog.setaltitudetolerance.text.metres=Limite (en metros) por debajo de cual peque\u00f1as subidas o bajadas ser\u00e1n ignoradas dialog.setaltitudetolerance.text.feet=Limite (en pies) por debajo de cual peque\u00f1as subidas o bajadas ser\u00e1n ignoradas +dialog.settimezone.system=Zona horaria del sistema dialog.autoplay.duration=Duraci\u00f3n (seg) dialog.autoplay.usetimestamps=Usar informaci\u00f3n de tiempo dialog.autoplay.rewind=Rebobinar @@ -808,7 +808,6 @@ wikipedia.lang=es openweathermap.lang=sp webservice.peakfinder=Abrir Peakfinder.org webservice.geohack=Abrir Geohack -webservice.panoramio=Abrir mapa Panoramio ## Cardinals for 3d plots cardinal.n=N @@ -866,7 +865,6 @@ error.jpegload.dialogtitle=Error cargando fotos error.jpegload.nofilesfound=No se encuentra ning\u00fan archivo error.jpegload.nojpegsfound=No se encuentra ning\u00fan archivo jpeg error.jpegload.nogpsfound=No se encuentra informaci\u00f3n GPS -error.jpegload.exifreadfailed=Fallo al leer la informaci\u00f3n Exif. No se puede leer ninguna informaci\u00f3n Exif\ncon las librer\u00edas internas ni externas. error.audioload.nofilesfound=No se encontraron archivos de audio error.gpsload.unknown=Error desconocido error.undofailed.title=Fallo al deshacer diff --git a/tim/prune/lang/prune-texts_fi.properties b/tim/prune/lang/prune-texts_fi.properties new file mode 100644 index 0000000..76cdcde --- /dev/null +++ b/tim/prune/lang/prune-texts_fi.properties @@ -0,0 +1,909 @@ +# Text entries for the GpsPrune application +# Finnish entries thanks to Erkki Argillander + +# Menu entries +menu.file=Tiedosto +menu.file.addphotos=Lis\u00e4\u00e4 kuvat... +menu.file.recentfiles=Avaa viimeaikainen +menu.file.save=Tallenna tekstin\u00e4... +menu.file.exit=Lopeta +menu.online=Online +menu.track=Reitti +menu.track.undo=Peru +menu.track.clearundo=Tyhjenn\u00e4 perumislista +menu.track.markrectangle=Merkkaa pisteet suunnikkaasta... +menu.range=Pistealue +menu.range.all=Valitse kaikki +menu.range.none=Valitse 'ei mit\u00e4\u00e4n' +menu.range.start=Aseta alkupiste +menu.range.end=Aseta loppupiste +menu.range.average=Keskiarvovalinta +menu.range.reverse=K\u00e4\u00e4nteinen pistealue +menu.range.mergetracksegments=Yhdist\u00e4 reittilohkot +menu.range.cutandmove=Leikkaa ja siirr\u00e4 valitut +menu.point=Piste +menu.point.editpoint=Muokkaa pistett\u00e4 +menu.point.deletepoint=Poista piste +menu.photo=Kuva +menu.photo.saveexif=Talleta Exif-tietoihin +menu.audio=\u00c4\u00e4net +menu.view=N\u00e4kym\u00e4 +menu.view.showsidebars=N\u00e4yt\u00e4 sivupalkit +menu.view.browser=Selainikkunan karttal\u00e4hde +menu.view.browser.google=Google +menu.view.browser.openstreetmap=Openstreetmap +menu.view.browser.mapquest=Mapquest +menu.view.browser.yahoo=Yahoo +menu.view.browser.bing=Bing +menu.settings=Asetukset +menu.settings.onlinemode=Lataa kartat Internetist\u00e4 +menu.settings.autosave=Tallenna asetukset automaattisesti lopetettaessa +menu.help=Ohje +# Popup menu for map +menu.map.zoomin=L\u00e4henn\u00e4 +menu.map.zoomout=Loitonna +menu.map.zoomfull=Sovita selainikkunaan +menu.map.newpoint=Luo uusi reittipiste +menu.map.drawpoints=Luo reittipisteiden sarja +menu.map.connect=Yhdist\u00e4 reittipisteet +menu.map.autopan=Autopan +menu.map.showmap=N\u00e4yt\u00e4 kartta +menu.map.showscalebar=N\u00e4yt\u00e4 mittakaava-asteikko +menu.map.editmode=Muokkaustila + +# Alt keys for menus +altkey.menu.file=T +altkey.menu.online=O +altkey.menu.range=A +altkey.menu.track=R +altkey.menu.point=P +altkey.menu.view=N +altkey.menu.photo=K +altkey.menu.audio=E +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=Avaa... +function.importwithgpsbabel=Tuo tiedosto GPSBabel'lla... +function.loadfromgps=Lataa data GPS:st\u00e4... +function.sendtogps=L\u00e4het\u00e4 data GPS:\u00e4\u00e4n... +function.exportkml=Vie KML +function.exportgpx=Vie GPX +function.exportpov=Vie POV +function.exportimage=Vie kuva +function.editwaypointname=Muokkaa kohdepisteen nime\u00e4 +function.compress=Tiivist\u00e4 reitti... +function.marklifts=Merkkaa yl\u00e4m\u00e4kinousut +function.deleterange=Poista pistealue +function.deletemarked=Poista merkityt pisteet +function.croptrack=Typist\u00e4 reitti alueseen +function.interpolate=Interpoloi pisteet +function.deletebydate=Poista pisteet p\u00e4iv\u00e4m\u00e4\u00e4r\u00e4n mukaan +function.addtimeoffset=Lis\u00e4\u00e4 aikapoikkeama +function.addaltitudeoffset=Lis\u00e4\u00e4 korkeuspoikkeama +function.findwaypoint=Etsi kohdepiste +function.rearrangewaypoints=J\u00e4rjest\u00e4 kohdepisteet +function.convertnamestotimes=Muunna kohdepisteiden nimet ajoiksi +function.deletefieldvalues=Poista kenttien arvoja... +function.pastecoordinates=Anna uudet koordinaatit... +function.charts=Kaaviot +function.show3d=3D-n\u00e4kym\u00e4 +function.distances=V\u00e4limatkat +function.fullrangedetails=Koko pistealueen tiedot +function.estimatetime=Arvioi aika +function.learnestimationparams=Opi aika-arvion parametrit +function.autoplay=Animoi reitti +function.selectsegment=Valitse nykyinen lohko +function.splitsegments=Pilko reitti lohkoihin +function.sewsegments=Yhdist\u00e4 reittilohkot +function.createmarkerwaypoints=Luo merkityt kohdepisteet +function.getgpsies=Lataa reitit Gpsies.com:sta +function.uploadgpsies=Vie reitti Gpsies.com:iin... +function.lookupsrtm=Lue korkeysk\u00e4yr\u00e4t SRTM:st\u00e4 +function.downloadsrtm=Lataa SRTM-palat +function.getwikipedia=Hae likeiset Wikipedia-artikkelit +function.searchwikipedianames=Etsi nimell\u00e4 Wikipedia:sta... +function.searchosmpois=Etsi l\u00e4heiset OSM-pisteet +function.searchopencachingde=Etsi OpenCaching.de:sta... +function.mapillary=Etsi kuvia Mapillary:sta... +function.downloadosm=Lataa alueen OSM-data... +function.duplicatepoint=Kahdenna piste +function.connecttopoint=Liit\u00e4 pisteeseen +function.disconnectfrompoint=Irroita pisteest\u00e4 +function.removephoto=Poista kuva +function.correlatephotos=Korreloi kuvat +function.rearrangephotos=J\u00e4rjest\u00e4 kuvat... +function.rotatephotoleft=Kierr\u00e4 kuvaa vasemmalle +function.rotatephotoright=Kierr\u00e4 kuvaa oikealle +function.photopopup=N\u00e4yt\u00e4 kuvaikkunassa +function.ignoreexifthumb=Ignore exif thumbnail +function.loadaudio=Lis\u00e4\u00e4 \u00e4\u00e4nileikkeet... +function.removeaudio=Poista \u00e4\u00e4nileike +function.correlateaudios=Korreloi \u00e4\u00e4net +function.playaudio=Toista \u00e4\u00e4nileike +function.stopaudio=Pys\u00e4yt\u00e4 \u00e4\u00e4nileike +function.setmapbg=Aseta taustakartan l\u00e4hde... +function.setpaths=Aseta ohjelmien polut... +function.setcolours=Aseta v\u00e4rit... +function.setdisplaysettings=Aseta n\u00e4kym\u00e4n asetukset +function.setlanguage=Aseta kieli... +function.help=Ohje +function.showkeys=N\u00e4yt\u00e4 n\u00e4pp\u00e4inkomennot +function.about=Tietoja GpsPrune:sta +function.checkversion=Tarkista uusi versio +function.saveconfig=Talleta asetukset +function.diskcache=Talleta kartat hakemistoon... +function.managetilecache=Hallitse karttapalojen v\u00e4limuistia +function.getweatherforecast=Hae s\u00e4\u00e4ennuste.. +function.setaltitudetolerance=Aseta korkeuden herkkyys... +function.selecttimezone=Aseta aikavy\u00f6hyke + +# Dialogs +dialog.exit.confirm.title=Lopeta GpsPrune +dialog.exit.confirm.text=Muokkaamasi data ei ole tallennettu.\nHaluatko varmasti lopettaa? +dialog.openappend.title=Lis\u00e4\u00e4 nykyiseen dataan +dialog.openappend.text=Lis\u00e4t\u00e4\u00e4nk\u00f6 t\u00e4m\u00e4 data jo ladattuun dataan? +dialog.deletepoint.title=Poista reittipiste +dialog.deletepoint.deletephoto=Poistetaanko yhteys pisteeseen liitettyyn kuvaan? +dialog.deletephoto.title=Poista kuva +dialog.deletephoto.deletepoint=Poistetaanko t\u00e4h\u00e4n kuvaan liitetty piste? +dialog.deleteaudio.deletepoint=Poistetaanko t\u00e4h\u00e4n \u00e4\u00e4nileikkeeseen liitetty piste? +dialog.openoptions.title=Avauksen valinnat +dialog.openoptions.filesnippet=Purettava tiedosto +dialog.load.table.field=Kentt\u00e4 +dialog.load.table.datatype=Datan tyyppi +dialog.load.table.description=Kuvaus +dialog.delimiter.label=Kenttien erotin +dialog.delimiter.comma=Pilkku , +dialog.delimiter.tab=Tabulaattori +dialog.delimiter.space=V\u00e4lily\u00f6nti +dialog.delimiter.semicolon=Puolipiste ; +dialog.delimiter.other=Muu +dialog.openoptions.deliminfo.records=tietueet, joissa +dialog.openoptions.deliminfo.fields=kent\u00e4t +dialog.openoptions.deliminfo.norecords=Ei tietueita +dialog.openoptions.altitudeunits=Korkeuden yksik\u00f6t +dialog.openoptions.speedunits=Nopeuden yksik\u00f6t +dialog.openoptions.vertspeedunits=Pystysuuntaisen nopeuden yksik\u00f6t +dialog.openoptions.vspeed.positiveup=Positiiviset nopeudet yl\u00f6sp\u00e4in +dialog.openoptions.vspeed.positivedown=Positiiviset nopeudet alap\u00e4in +dialog.open.contentsdoubled=T\u00e4ss\u00e4 tiedostossa on kaksi kopioita kustakin pisteest\u00e4,\ntoinen kohde- ja toinen reittipisteen\u00e4. +dialog.selecttracks.intro=Valitse ladattava reitti tai reitit +dialog.selecttracks.noname=Nimet\u00f6n +dialog.jpegload.subdirectories=Sis\u00e4llyt\u00e4 alihakemistot +dialog.jpegload.loadjpegswithoutcoords=Sis\u00e4llyt\u00e4 koordinaatittomat kuvat +dialog.jpegload.loadjpegsoutsidearea=Sis\u00e4llyt\u00e4 kuvat nykyisen kartta-alueen ulkopuolelta +dialog.jpegload.progress.title=Ladataan kuvia +dialog.jpegload.progress=Odota, kun kuvia etsit\u00e4\u00e4n +dialog.gpsload.nogpsbabel=gpsbabel-ohjelmaa ei l\u00f6ytynyt. Jatketaanko? +dialog.gpsload.device=Laittee nimi (esim. usb:) +dialog.gpsload.format=Muoto +dialog.gpsload.getwaypoints=Lataa kohdepisteet +dialog.gpsload.gettracks=Lataa reitit +dialog.gpsload.save=Tallenna tiedostoon +dialog.gpssend.sendwaypoints=L\u00e4het\u00e4 kohdepisteet +dialog.gpssend.sendtracks=L\u00e4het\u00e4 reitit +dialog.gpssend.trackname=Reitin nimi +dialog.gpsbabel.filters=Suodattimet +dialog.addfilter.title=Lis\u00e4\u00e4 suodatin +dialog.gpsbabel.filter.discard=Hylk\u00e4\u00e4 +dialog.gpsbabel.filter.simplify=Yksinkertaista +dialog.gpsbabel.filter.distance=V\u00e4limatka +dialog.gpsbabel.filter.interpolate=Interpoloi +dialog.gpsbabel.filter.discard.intro=Hylk\u00e4\u00e4 pisteet, jos +dialog.gpsbabel.filter.discard.hdop=Hdop > +dialog.gpsbabel.filter.discard.vdop=Vdop > +dialog.gpsbabel.filter.discard.numsats=Sateliittien lukum\u00e4\u00e4r\u00e4 < +dialog.gpsbabel.filter.discard.nofix=Point has no fix +dialog.gpsbabel.filter.discard.unknownfix=Point has unknown fix +dialog.gpsbabel.filter.simplify.intro=Poista pisteit\u00e4, kunnes +dialog.gpsbabel.filter.simplify.maxpoints=Pisteiden m\u00e4\u00e4r\u00e4 < +dialog.gpsbabel.filter.simplify.maxerror=tai virhev\u00e4limatka < +dialog.gpsbabel.filter.simplify.crosstrack=cross-track +dialog.gpsbabel.filter.simplify.length=pituusero +dialog.gpsbabel.filter.simplify.relative=suhteessa hdop:iin +dialog.gpsbabel.filter.distance.intro=Poista sellaiset pisteet, jotka l\u00e4hell\u00e4 jotain edellist\u00e4 pistett\u00e4 +dialog.gpsbabel.filter.distance.distance=Jos v\u00e4limatka < +dialog.gpsbabel.filter.distance.time=ja aikaero < +dialog.gpsbabel.filter.interpolate.intro=Lis\u00e4\u00e4 lis\u00e4pisteit\u00e4 reittipisteiden v\u00e4liin +dialog.gpsbabel.filter.interpolate.distance=Jos v\u00e4limatka > +dialog.gpsbabel.filter.interpolate.time=tai aikaero > +dialog.saveoptions.title=Tallenna tiedosto +dialog.save.fieldstosave=Tallennettavat kent\u00e4t +dialog.save.table.field=Kent\u00e4 +dialog.save.table.hasdata=On dataa +dialog.save.table.save=Tallenna +dialog.save.headerrow=Tulosta otsikkorivi +dialog.save.coordinateunits=Koordinaattimuoto +dialog.save.altitudeunits=Korkeuden yksikk\u00f6 +dialog.save.timestampformat=Aikaleiman muoto +dialog.save.overwrite.title=Tiedosto jo olemassa +dialog.save.overwrite.text=T\u00e4m\u00e4 tiedosto on jo olemassa. Haluatko varmasti tallentaa sen p\u00e4\u00e4lle? +dialog.save.notypesselected=Pistetyyppej\u00e4 ei valittuna +dialog.exportkml.text=Otsikko datalle +dialog.exportkml.altitude=Absoluuttiset korkeudet (ilmailulle) +dialog.exportkml.kmz=Tiivist\u00e4 tehd\u00e4ksesi 'kmz'-tiedosto +dialog.exportkml.exportimages=Vie esikatselukuvat 'kmz'-tiedostoon +dialog.exportkml.imagesize=Kuvakoko +dialog.exportkml.trackcolour=Reitin v\u00e4ri +dialog.exportkml.standardkml=Vakio KML +dialog.exportkml.extendedkml=Laajennettu KML aikaleimoin +dialog.exportgpx.name=Nimi +dialog.exportgpx.desc=Kuvaus +dialog.exportgpx.includetimestamps=Sis\u00e4llyt\u00e4 aikaleimat +dialog.exportgpx.copysource=Kopioi l\u00e4hde-XML +dialog.exportgpx.encoding=Koodaus +dialog.exportgpx.encoding.system=J\u00e4rjestelm\u00e4 +dialog.exportgpx.encoding.utf8=UTF-8 +dialog.exportpov.text=Anna parametrit POV-vientiin +dialog.exportpov.font=Kirjasintyyppi +dialog.exportpov.camerax=Kamera X +dialog.exportpov.cameray=Kamera Y +dialog.exportpov.cameraz=Kamera Z +dialog.exportpov.modelstyle=Mallin tyyli +dialog.exportpov.ballsandsticks=Pallot ja kepit +dialog.exportpov.tubesandwalls=Putket ja sein\u00e4t +dialog.3d.warningtracksize=T\u00e4ss\u00e4 reitiss\u00e4 on paljon sellaisia reittipisteit\u00e4, joita Java3D ei mahdollisesti kykene n\u00e4ytt\u00e4m\u00e4\u00e4n.\nHaluatko varmasti jatkaa? +dialog.3d.useterrain=N\u00e4yt\u00e4 maasto +dialog.3d.terraingridsize=Ruudukon koko +dialog.exportpov.baseimage=Peruskuva +dialog.exportpov.cannotmakebaseimage=Peruskuvaa ei voi kirjoittaa +dialog.baseimage.title=Karttakuva +dialog.baseimage.useimage=K\u00e4yt\u00e4 karttakuvaa +dialog.baseimage.mapsource=Karttal\u00e4hde +dialog.baseimage.zoom=Suurennostaso +dialog.baseimage.incomplete=Karttakuva ep\u00e4t\u00e4ydellinen +dialog.baseimage.tiles=Karttapalat +dialog.baseimage.size=Kuvan koko +dialog.exportimage.noimagepossible=Karttakuvat pit\u00e4\u00e4 tallentaa levylle v\u00e4limuistiin, jotta niit\u00e4 voi k\u00e4ytt\u00e4\u00e4 vientiin. +dialog.exportimage.drawtrack=Piirr\u00e4 reitti kartalle +dialog.exportimage.drawtrackpoints=Piirr\u00e4 reittipisteit\u00e4 +dialog.exportimage.textscalepercent=Tekstin skaalauskerroin (%) +dialog.pointtype.desc=Tallenna seuraavan tyyppiset pisteet: +dialog.pointtype.track=Reittipisteet +dialog.pointtype.waypoint=Kohdepisteet +dialog.pointtype.photo=Kuvien pisteet +dialog.pointtype.audio=\u00c4\u00e4nien pisteet +dialog.pointtype.selection=Just selection +dialog.confirmreversetrack.title=Vahvista k\u00e4\u00e4nteisj\u00e4rjest\u00e4minen +dialog.confirmreversetrack.text=T\u00e4m\u00e4 reitti sis\u00e4lt\u00e4\u00e4 aikaleimoja, joiden aikaj\u00e4rjestys ei ole oikea pisteiden k\u00e4\u00e4teisj\u00e4rjestelyn j\u00e4lkeen.\nHaluatko varmasti k\u00e4\u00e4nteisj\u00e4rjest\u00e4\u00e4 t\u00e4m\u00e4n alueen? +dialog.confirmcutandmove.title=Vahvista leikkaa ja siirr\u00e4 +dialog.confirmcutandmove.text=T\u00e4ss\u00e4 reitti sis\u00e4lt\u00e4\u00e4 aikaleimoja, jotka eiv\u00e4t ole j\u00e4rjestyksee\u00e4 siirron j\u00e4lkeen.\nHaluatko varmasti siirt\u00e4\u00e4 t\u00e4m\u00e4n pistelohkon? +dialog.interpolate.parameter.text=Kunkin pisteparin v\u00e4liin luotavien pisteiden m\u00e4\u00e4r\u00e4 +dialog.interpolate.betweenwaypoints=Interpoloi kohdepisteiden v\u00e4linen reitti +dialog.undo.title=Peruttavat toiminnot +dialog.undo.pretext=Valitse yksi tai useampi peruttava toiminto +dialog.undo.none.title=Ei voi perua +dialog.undo.none.text=Ei peruttavia toimintoja! +dialog.clearundo.title=Tyhjenn\u00e4 perumislista +dialog.clearundo.text=Haluatko varmasti tyhjenttiedostottiedostot perumislistan?\nKaikki perumistiedot menetet\u00e4\u00e4n lopullisesti! +dialog.pointedit.title=Muokkaa pistett\u00e4 +dialog.pointedit.intro=Valitse kukin kentt\u00e4 vuorollaan\nn\u00e4hd\u00e4ksesi ja muuttaaksesi sen arvoa +dialog.pointedit.table.field=Kentt\u00e4 +dialog.pointedit.nofield=Kentt\u00e4\u00e4 ei valittuna +dialog.pointedit.table.value=Arvo +dialog.pointnameedit.name=Kohdepisteen nimi +dialog.pointnameedit.uppercase=Suuraakkoset +dialog.pointnameedit.lowercase=Pienaakkoset +dialog.pointnameedit.titlecase=Iso alkukirjain +dialog.addtimeoffset.add=Lis\u00e4tt\u00e4v\u00e4 aika +dialog.addtimeoffset.subtract=V\u00e4hennett\u00e4v\u00e4 aika +dialog.addtimeoffset.days=P\u00e4iv\u00e4t +dialog.addtimeoffset.hours=Tunnit +dialog.addtimeoffset.minutes=Minuutit +dialog.addtimeoffset.notimestamps=Aikapoikkeamaa ei voi lis\u00e4t\u00e4 t\u00e4st\u00e4 valinnasta puuttuvan aikaleiman vuoksi +dialog.findwaypoint.intro=Anna kohdepisteen nimen osa +dialog.findwaypoint.search=Etsi +dialog.saveexif.title=Tallenna Exif +dialog.saveexif.intro=Valitse tallennettavat kuvat ruksaamalla valintaruutuihin +dialog.saveexif.nothingtosave=Koordinaattitieto muuttumaton - ei uutta tallennettavaa +dialog.saveexif.noexiftool='exiftool'-ohjelmaa ei l\u00f6ytynyt. Jatketaanko? +dialog.saveexif.table.photoname=Kuvan nimi +dialog.saveexif.table.status=Tila +dialog.saveexif.table.save=Tallenna +dialog.saveexif.photostatus.connected=Liitetty +dialog.saveexif.photostatus.disconnected=Liitt\u00e4m\u00e4t\u00f6n +dialog.saveexif.photostatus.modified=Muutettu +dialog.saveexif.overwrite=Korvaa tiedostot +dialog.saveexif.force=Pakota huomioimatta pikkuvirheit\u00e4 +dialog.charts.xaxis=X-akseli +dialog.charts.yaxis=Y-akseli +dialog.charts.output=Tulostus +dialog.charts.screen=N\u00e4yt\u00f6lle +dialog.charts.svg=SVG-tiedostoon +dialog.charts.svgwidth=SVG:n leveys kuvapistein\u00e4 +dialog.charts.svgheight=SVG:n korkeus kuvapistein\u00e4 +dialog.charts.needaltitudeortimes=Reitiss\u00e4 pit\u00e4\u00e4 olla joko korkeustiedot tai aikaleimat kaaviokuvien luomiseksi +dialog.charts.gnuplotnotfound='gnuplot'-ohjelmaa ei l\u00f6ytynyt annetusta hakemistopolusta +dialog.distances.intro=Pisteiden v\u00e4limatkat linnuntiet\u00e4 +dialog.distances.column.from=Pisteest\u00e4 +dialog.distances.column.to=Pisteeseen +dialog.distances.currentpoint=Nykypiste +dialog.distances.toofewpoints=T\u00e4m\u00e4 toiminto edellytt\u00e4\u00e4 kohdepisteit\u00e4 niiden v\u00e4limatkojen laskemiseksi +dialog.fullrangedetails.intro=T\u00e4ss\u00e4 tiedot valitulle alueelle +dialog.fullrangedetails.coltotal=Reitti aukkoineen +dialog.fullrangedetails.colsegments=Reitti aukoitta +dialog.estimatetime.details=Tiedot +dialog.estimatetime.gentle=Loiva +dialog.estimatetime.steep=Jyrkk\u00e4 +dialog.estimatetime.climb=Nousu +dialog.estimatetime.descent=Lasku +dialog.estimatetime.parameters=Parametrit +dialog.estimatetime.parameters.timefor=Aika matkalle +dialog.estimatetime.results=Tulokset +dialog.estimatetime.results.estimatedtime=Arvioitu aika +dialog.estimatetime.results.actualtime=Todellinen aika +dialog.estimatetime.error.nodistance=Aika-arviointi tarvitsee yhdistettyj\u00e4 reittipisteit\u00e4 v\u00e4limatkan laskemiseksi. +dialog.estimatetime.error.noaltitudes=Valinta ei sis\u00e4ll\u00e4 yht\u00e4\u00e4n korkeustietoa +dialog.learnestimationparams.intro=N\u00e4m\u00e4 ovat t\u00e4lle reitille lasketut parametrit +dialog.learnestimationparams.averageerror=Keskim\u00e4\u00e4r\u00e4inen virhe +dialog.learnestimationparams.combine=N\u00e4m\u00e4 parametrit voidaan yhdist\u00e4\u00e4 nykyisiin arvoihin +dialog.learnestimationparams.combinedresults=Yhdistetyt tulokset +dialog.learnestimationparams.weight.100pccurrent=Pid\u00e4 nykyiset arvot +dialog.learnestimationparams.weight.current=nykyinen +dialog.learnestimationparams.weight.calculated=laskettu +dialog.learnestimationparams.weight.50pc=Nykyisten ja lasketttujen arvojen keskiarvo +dialog.learnestimationparams.weight.100pccalculated=K\u00e4yt\u00e4 laskettuja arvoja +dialog.setmapbg.intro=Valitse jokin annetuista karttal\u00e4hteist\u00e4\ntai lis\u00e4\u00e4 uusi +dialog.addmapsource.title=Lis\u00e4\u00e4 uusi karttal\u00e4hde +dialog.addmapsource.sourcename=L\u00e4hteen nimi +dialog.addmapsource.layer1url=Ensim\u00e4isen tason URL +dialog.addmapsource.layer2url=Toisen tason valinnainen URL +dialog.addmapsource.maxzoom=Maksimisuurennus +dialog.addmapsource.noname=Nimet\u00f6n +dialog.gpsies.column.name=Reitin nimi +dialog.gpsies.column.length=Pituus +dialog.gpsies.description=Kuvaus +dialog.gpsies.nodescription=Ei kuvausta +dialog.gpsies.nonefound=Reittej\u00e4 ei l\u00f6ytynyt +dialog.gpsies.username=Gpsies.com:n k\u00e4ytt\u00e4j\u00e4nimi +dialog.gpsies.password=Gpsies.com:n salasana +dialog.gpsies.keepprivate=Pid\u00e4 reitti yksityisen\u00e4 +dialog.gpsies.confirmopenpage=Open the web page for the uploaded track? +dialog.gpsies.activities=Aktiviteettityypit +dialog.gpsies.activity.trekking=Patikointi +dialog.gpsies.activity.walking=K\u00e4vely +dialog.gpsies.activity.jogging=Juoksu +dialog.gpsies.activity.biking=Polkupy\u00f6r\u00e4ily +dialog.gpsies.activity.motorbiking=Moottoripy\u00f6r\u00e4ily +dialog.gpsies.activity.snowshoe=Lumikenk\u00e4ily +dialog.gpsies.activity.sailing=Purjehdus +dialog.gpsies.activity.skating=Luistelu +dialog.mapillary.nonefound=Kuvia ei l\u00f6ytynyt +dialog.wikipedia.column.name=Artikkelin nimi +dialog.wikipedia.column.distance=V\u00e4limatka +dialog.wikipedia.nonefound=Wikipedia-artikkeleita ei l\u00f6ytynyt +dialog.wikipedia.gallery=Gallery +dialog.osmpois.column.name=Nimi +dialog.osmpois.column.type=Tyyppi +dialog.osmpois.nonefound=Pisteit\u00e4 ei l\u00f6ytynyt +dialog.geocaching.nonefound=Geok\u00e4tk\u00f6j\u00e4 ei l\u00f6ytynyt +dialog.correlate.notimestamps=Reittipisteiss\u00e4 ei ole aikaleimoja, joten yht\u00e4\u00e4n pistett\u00e4 ei voi aikakorreloida kuvien kanssa. +dialog.correlate.nouncorrelatedphotos=Ei korreloimattomia kuvia.\nHaluatko varmasti jatkaa? +dialog.correlate.nouncorrelatedaudios=Ei korreloimattomia \u00e4\u00e4hileikkeit\u00e4.\nHaluatko varmasti jatkaa? +dialog.correlate.photoselect.intro=Valitse yksi n\u00e4ist\u00e4 korreloiduista kuvista aikapoikkeaman m\u00e4\u00e4ritt\u00e4miseksi +dialog.correlate.select.photoname=Kuvan nimi +dialog.correlate.select.timediff=Aikaero +dialog.correlate.select.photolater=Kuva my\u00f6hemmin +dialog.correlate.options.intro=Valitse vaihtoehtoiset arvot automaattiseen korrelointiin +dialog.correlate.options.offsetpanel=Aikapoikkeama +dialog.correlate.options.offset=Poikkeama +dialog.correlate.options.offset.hours=tunnit, +dialog.correlate.options.offset.minutes=minuutit ja +dialog.correlate.options.offset.seconds=sekunnit +dialog.correlate.options.photolater=Kuva pistett\u00e4 my\u00f6hemmin +dialog.correlate.options.pointlaterphoto=Piste kuvaa my\u00f6hemmin +dialog.correlate.options.audiolater=\u00c4\u00e4nileike pistett\u00e4 my\u00f6hemmin +dialog.correlate.options.pointlateraudio=Piste \u00e4\u00e4nileikett\u00e4 my\u00f6hemmin +dialog.correlate.options.limitspanel=Korreloinnin raja-arvot +dialog.correlate.options.notimelimit=Ei aikarajoja +dialog.correlate.options.timelimit=Aikarajat +dialog.correlate.options.nodistancelimit=Ei et\u00e4isyysrajoja +dialog.correlate.options.distancelimit=Eta\u00e4isyysrajat +dialog.correlate.options.correlate=Korreloi +dialog.correlate.alloutsiderange=Kaikki kohteet reitin aikarajojen ulkopuolella, joten mit\u00e4\u00e4n ei voi korreloida.\nKoita muuttaa poikkeamaa tai korreloida manuaalisesti ainakin yksi kohde. +dialog.correlate.filetimes=Tiedoston aikaleimat osoittavat: +dialog.correlate.filetimes2=\u00e4\u00e4nileikeen (?) +dialog.correlate.correltimes=Korrelointiin k\u00e4yt\u00e4: +dialog.correlate.timestamp.beginning=Aloitusaika +dialog.correlate.timestamp.middle=Keskiaika +dialog.correlate.timestamp.end=Lopetusaika +dialog.correlate.audioselect.intro=Valitse yksi n\u00e4ist\u00e4 korreloiduista \u00e4\u00e4nileikkeist\u00e4 aikapoikkeaman m\u00e4\u00e4ritt\u00e4miseksi +dialog.correlate.select.audioname=\u00c4\u00e4nileikkeen nimi +dialog.correlate.select.audiolater=\u00c4\u00e4nileikke my\u00f6hemmin +dialog.rearrangewaypoints.desc=Valitse kohde ja kohdepisteiden lajittelus\u00e4\u00e4nt\u00f6 +dialog.rearrangephotos.desc=Valitse kohde ja kuvapisteiden lajittelus\u00e4\u00e4nt\u00f6 +dialog.rearrange.tostart=Siirry alkuun +dialog.rearrange.toend=Siirry loppuun +dialog.rearrange.tonearest=Kukin l\u00e4heisimp\u00e4\u00e4n reittipisteeseen +dialog.rearrange.nosort=\u00c4l\u00e4 lajittele +dialog.rearrange.sortbyfilename=Lajittele tiedostonimen suhteen +dialog.rearrange.sortbyname=Lajittele nimen suhteen +dialog.rearrange.sortbytime=Lajittele ajan suhteen +dialog.compress.duplicates.title=Kahdennosten poisto +dialog.compress.closepoints.title=Likipisteen poisto +dialog.compress.closepoints.paramdesc=Aikaj\u00e4nten kerroin +dialog.compress.wackypoints.title=Omituisten pisteiden poisto +dialog.compress.wackypoints.paramdesc=V\u00e4limatkakerroin +dialog.compress.singletons.title=Irtopisteiden poisto +dialog.compress.singletons.paramdesc=V\u00e4limatkakerroin +dialog.compress.douglaspeucker.title=Douglas-Peucker-tiivistys +dialog.compress.douglaspeucker.paramdesc=Aikaj\u00e4nnekerroin +dialog.compress.summarylabel=Poistettavat pisteet +dialog.compress.confirm=%d pistett\u00e4 on merkitty.\nPoista merkityt pisteet nyt? +dialog.compress.confirmnone=yht\u00e4\u00e4n pistett\u00e4 ei ole merkitty +dialog.deletemarked.nonefound=Yht\u00e4\u00e4n datapistett\u00e4 ei voitu poistaa +dialog.pastecoordinates.desc=Kirjoita tai liit\u00e4 koordinaatit t\u00e4h\u00e4n +dialog.pastecoordinates.coords=Koordinaatit +dialog.pastecoordinates.nothingfound=Tarkista koordinaatit ja yrit\u00e4 uudestaan +dialog.help.help=Lis\u00e4tietoja ja vihjeit\u00e4 webbisivulta:\nhttps://gpsprune.activityworkshop.net/\n\nMyyt\u00e4v\u00e4n\u00e4 PDF:n\u00e4 k\u00e4ytt\u00f6ohjekirja. +dialog.about.version=Versio +dialog.about.build=Build +dialog.about.summarytext1=GpsPrune on ohjelma, jolla ladataan, tarkastellaan ja muokataan GPS-tallentimista
luettua paikkatietoa. +dialog.about.summarytext2=Ohjelma on julkaistu GNU GPL-lisenssill\u00e4 vapaana ja avoimena l\u00e4hdekoodina
maailmanlaajuiseen k\u00e4ytt\u00f6\u00f6n ja edelleen kehitt\u00e4miseen.
Kopiointi, jakelu ja muuttelu on sallittua ja kannustettavaa jakelun mukana
tulleen license.txt-tiedoston ehtojen mukaisesti. +dialog.about.summarytext3=Webbisivulta https://activityworkshop.net/ saat lis\u00e4\u00e4 tietoja ja vinkkej\u00e4 sek\u00e4
voit ostaa k\u00e4ytt\u00f6oppaan PDF-julkaisuna. +dialog.about.languages=Kielivaihtoehdot +dialog.about.translatedby=Suomenkielelle k\u00e4\u00e4nt\u00e4nyt Erkki. +dialog.about.systeminfo=J\u00e4rjestelm\u00e4tiedot +dialog.about.systeminfo.os=K\u00e4ytt\u00f6j\u00e4rjestelm\u00e4 +dialog.about.systeminfo.java=Java versio +dialog.about.systeminfo.java3d=Java3d asennettu +dialog.about.systeminfo.povray=Povray asennettu +dialog.about.systeminfo.exiftool=Exiftool asennettu +dialog.about.systeminfo.gpsbabel=Gpsbabel asennettu +dialog.about.systeminfo.gnuplot=Gnuplot asennettu +dialog.about.yes=Kyll\u00e4 +dialog.about.no=Ei +dialog.about.credits=Tekij\u00e4t +dialog.about.credits.code=GpsPrune:n ohjelmoinut +dialog.about.credits.exifcode=Exif:in koodannut +dialog.about.credits.icons=Jotkut kuvakkeet t\u00e4\u00e4lt\u00e4 +dialog.about.credits.translators=K\u00e4\u00e4nt\u00e4j\u00e4t +dialog.about.credits.translations=K\u00e4\u00e4nt\u00e4misess\u00e4 avustaneet +dialog.about.credits.devtools=Kehitysty\u00f6kalut +dialog.about.credits.othertools=Muut ty\u00f6kalut +dialog.about.credits.thanks=Kiitokset +dialog.about.readme=Lueminut +dialog.checkversion.error=Versionumeroa ei voitu tarkistaa.\nTarkista verkkoyhteys. +dialog.checkversion.uptodate=K\u00e4yt\u00f6ss\u00e4si on uusin GpsPrune:n versio. +dialog.checkversion.newversion1=GpsPrune:sta on saatavissa uudempi versio! Uusin versio on nyt +dialog.checkversion.newversion2=. +dialog.checkversion.releasedate1=T\u00e4m\u00e4 uusin versio on julkaistu +dialog.checkversion.releasedate2=. +dialog.checkversion.download=Uusimman version voit ladata webbisivulta https://gpsprune.activityworkshop.net/download.html. +dialog.keys.intro=Voit hiiren asemesta k\u00e4ytt\u00e4\u00e4 seuraavia n\u00e4pp\u00e4inkomentoja: +dialog.keys.keylist=
Nuolin\u00e4pp\u00e4imill\u00e4voit siirt\u00e4\u00e4 kartaa vasemmalle, oikealle, yl\u00f6s ja alas
Ctrl + vasen/oikea nuoli\u00e4pp\u00e4inValitse edellinen/seuraava reittipiste
Ctrl + yl\u00f6s/alas nuolin\u00e4pp\u00e4inL\u00e4henn\u00e4 tai loitonna
Ctrl + PgUp, PgDownValitse edellinen/seuraava pistelohko
Ctrl + Home, EndValitse ensimm\u00e4inen/viimeinen reittipiste
DelPoista valittu reittipiste
+dialog.keys.normalmodifier=Ctrl +dialog.keys.macmodifier=Command +dialog.saveconfig.desc=Seuraava asetukset voidaan tallentaa asetustiedostoon: +dialog.saveconfig.prune.trackdirectory=Reittihakemisto +dialog.saveconfig.prune.photodirectory=Kuvahakemisto +dialog.saveconfig.prune.languagecode=Kielitunnus (FI) +dialog.saveconfig.prune.languagefile=Kielitiedosto +dialog.saveconfig.prune.gpsdevice=GPS-laite +dialog.saveconfig.prune.gpsformat=GPS-formaatti +dialog.saveconfig.prune.povrayfont=Povray fontti +dialog.saveconfig.prune.gnuplotpath=Polku ohjelmaan gnuplot +dialog.saveconfig.prune.gpsbabelpath=Polku ohjelmaan gpsbabel +dialog.saveconfig.prune.exiftoolpath=Polku ohjelmaan exiftool +dialog.saveconfig.prune.mapsource=Valittu karttal\u00e4hde +dialog.saveconfig.prune.mapsourcelist=Karttal\u00e4hteet +dialog.saveconfig.prune.diskcache=Karttojen v\u00e4limuisti +dialog.saveconfig.prune.kmzimagewidth=KMZ:in kuvan leveys +dialog.saveconfig.prune.colourscheme=V\u00e4rij\u00e4rjestelm\u00e4 +dialog.saveconfig.prune.linewidth=Viivan leveys +dialog.saveconfig.prune.kmltrackcolour=KML:n reitin v\u00e4ri +dialog.saveconfig.prune.autosavesettings=Tallenna asetukset automaattisesti +dialog.setpaths.intro=Halutessasi voit valita hakemistopolut ulkoisiin sovelluksiin: +dialog.setpaths.found=Polku l\u00f6ydetty? +dialog.addaltitude.noaltitudes=Valittu alue ei sis\u00e4ll\u00e4 korkeustietoja +dialog.addaltitude.desc=Lis\u00e4tt\u00e4v\u00e4 korkeuspoikkeama +dialog.lookupsrtm.overwritezeros=Korvataanko nolla-arvoiset korkeusarvot? +dialog.setcolours.intro=Klikkaa v\u00e4rilaikua alla vaihtaaksesi kohteen v\u00e4ri\u00e4 +dialog.setcolours.background=Tausta +dialog.setcolours.borders=Reunat +dialog.setcolours.lines=Viivat +dialog.setcolours.primary=Ensisijainen +dialog.setcolours.secondary=Toissijainen +dialog.setcolours.point=Reittipisteet +dialog.setcolours.selection=Valinta +dialog.setcolours.text=Teksti +dialog.colourchooser.title=Valitse v\u00e4ri +dialog.colourchooser.red=Punainen (R) +dialog.colourchooser.green=Vihre\u00e4 (G) +dialog.colourchooser.blue=Sininen (B) +dialog.colourer.intro=Pisteen v\u00e4ritt\u00e4j\u00e4 voi tuottaa reittipisteille eri v\u00e4rej\u00e4 +dialog.colourer.type=V\u00e4ritt\u00e4j\u00e4n tyyppi +dialog.colourer.type.none=Ei mit\u00e4\u00e4n +dialog.colourer.type.byfile=Tiedosto +dialog.colourer.type.bysegment=Lohko +dialog.colourer.type.byaltitude=Korkeus +dialog.colourer.type.byspeed=Nopeus +dialog.colourer.type.byvertspeed=Pystysuuntainen nopeus +dialog.colourer.type.bygradient=Kaltevuus +dialog.colourer.type.bydate=P\u00e4iv\u00e4m\u00e4\u00e4r\u00e4 +dialog.colourer.start=Alkuv\u00e4ri +dialog.colourer.end=P\u00e4\u00e4tev\u00e4ri +dialog.colourer.maxcolours=V\u00e4rien enimm\u00e4ism\u00e4\u00e4r\u00e4 +dialog.setlanguage.firstintro=Voi valita jonkin julkaisun mukana toimitetuista kieliversioista,

tai valita jonkin k\u00e4\u00e4nn\u00f6kset sis\u00e4lt\u00e4v\u00e4n tekstitiedoston. +dialog.setlanguage.secondintro=

Kielen vaihtaaksesi sinun pit\u00e4\u00e4 ensiksi tallentaa asetukset ja sitten

k\u00e4ynnist\u00e4\u00e4 GpsPrune uudestaan. +dialog.setlanguage.language=Kieli +dialog.setlanguage.languagefile=Kielitiedosto +dialog.setlanguage.endmessage=Tallenna nyt asetukset ja k\u00e4ynnist\u00e4 GpsPrune uudelleen\nsaadaksesi vaihtamasi kielen k\u00e4ytt\u00f6\u00f6n. +dialog.setlanguage.endmessagewithautosave=K\u00e4ynnist\u00e4 GpsPrune ottaaksesi k\u00e4ytt\u00f6\u00f6n valitsemasi kielen. +dialog.diskcache.save=Tallenna karttapalat v\u00e4limuistihakemistoon +dialog.diskcache.dir=V\u00e4limuistin hakemisto +dialog.diskcache.createdir=Luodaanko hakemisto +dialog.diskcache.nocreate=V\u00e4limuistin hakemistoa ei luotu +dialog.diskcache.cannotwrite=Karttapaloja ei voida tallentaa valittuun hakemistoon +dialog.diskcache.table.path=Hakemistoolku +dialog.diskcache.table.usedby=Used by +dialog.diskcache.table.zoom=Zoom +dialog.diskcache.table.tiles=Tiles +dialog.diskcache.table.megabytes=Megabytes +dialog.diskcache.tileset=Tileset +dialog.diskcache.tileset.multiple=multiple +dialog.diskcache.deleteold=Poista vanhan karttapalat +dialog.diskcache.maximumage=Enimm\u00e4isik\u00e4 (p\u00e4ivi\u00e4) +dialog.diskcache.deleteall=Poista kaikki kartapalat +dialog.diskcache.deleted=Poistettu %d tiedostoa v\u00e4limuistista +dialog.deletefieldvalues.intro=Valitse nykyvalinnasta poistettavat kent\u00e4t +dialog.deletefieldvalues.nofields=Ei kentti\u00e4 poistettavaksi t\u00e4st\u00e4 valinnasta +dialog.displaysettings.linewidth=Viivanpaksuus reiteille (1-4) +dialog.displaysettings.antialias=K\u00e4yt\u00e4 antialiasointia +dialog.displaysettings.waypointicons=Kohdepistekuvakkeet +dialog.displaysettings.wpicon.default=Oletus +dialog.displaysettings.wpicon.ringpt=Ympyr\u00e4merkkari +dialog.displaysettings.wpicon.plectrum=Plektra +dialog.displaysettings.wpicon.ring=Rengas +dialog.displaysettings.wpicon.pin=Nuppineula +dialog.displaysettings.size.small=Pieni +dialog.displaysettings.size.medium=Keski +dialog.displaysettings.size.large=Iso +dialog.downloadosm.desc=Vahvista ladataksesi muokkaamattoman OSM-datan valitsemaltasi alueelta: +dialog.searchwikipedianames.search=Etsi: +dialog.weather.location=Sijainti +dialog.weather.update=Ennuste p\u00e4ivitetty +dialog.weather.sunrise=Auringonnousu +dialog.weather.sunset=Auringonlasku +dialog.weather.temperatureunits=L\u00e4mp\u00f6tilat +dialog.weather.currentforecast=S\u00e4\u00e4tila nyt +dialog.weather.dailyforecast=Ennuste p\u00e4iv\u00e4ksi +dialog.weather.3hourlyforecast=Ennuste kolmeksi tunniksi +dialog.weather.day.now=S\u00e4\u00e4tila nyt +dialog.weather.day.today=T\u00e4\u00e4n\u00e4\u00e4n +dialog.weather.day.tomorrow=Huomenna +dialog.weather.day.monday=Maanantai +dialog.weather.day.tuesday=Tiistai +dialog.weather.day.wednesday=Keskiviikko +dialog.weather.day.thursday=Torstai +dialog.weather.day.friday=Perjantai +dialog.weather.day.saturday=Lauantai +dialog.weather.day.sunday=Sunnuntai +dialog.weather.wind=Tuuli +dialog.weather.temp=L\u00e4mp\u00f6tila +dialog.weather.humidity=Kosteus +dialog.weather.creditnotice=T\u00e4m\u00e4 s\u00e4\u00e4tieto on openweathermap.org:n tuottama. Lis\u00e4tietoja heid\u00e4n webbisivuiltaans. +dialog.deletebydate.onlyonedate=Kaikki pisteet on tallennettu samana p\u00e4iv\u00e4n\u00e4. +dialog.deletebydate.intro=Reitin kunkin p\u00e4iv\u00e4n osalta voit joko tuhota tai s\u00e4ilytt\u00e4\u00e4 reittipisteet +dialog.deletebydate.nodate=Ei aikaleimaa +dialog.deletebydate.column.keep=S\u00e4ilyt\u00e4 +dialog.deletebydate.column.delete=Poista +dialog.setaltitudetolerance.text.metres=Raja-arvot (metreiss\u00e4), joita pienemm\u00e4t laskeutumisia tai nousuja ei huomioida +dialog.setaltitudetolerance.text.feet=Raja-arvot (jaloissa), joita pienemm\u00e4t laskeutumisia tai nousuja ei huomioida +dialog.settimezone.intro=T\u00e4ss\u00e4 voit valita aikavy\u00f6hykeen, jonka mukaan pisteiden aikaleimat n\u00e4ytet\u00e4\u00e4n +dialog.settimezone.system=K\u00e4yt\u00e4 j\u00e4rjestelm\u00e4n aikavy\u00f6hykett\u00e4 +dialog.settimezone.custom=K\u00e4yt\u00e4 seuraavaa aikavy\u00f6kykett\u00e4: +dialog.settimezone.list.toomany=Liian monta valittavaksi +dialog.settimezone.selectedzone=Valittu aikavy\u00f6hyke +dialog.settimezone.offsetfromutc=Aikaero UTC:n +dialog.autoplay.duration=Kesto (sekuntia) +dialog.autoplay.usetimestamps=K\u00e4yt\u00e4 pisteiden aikaleimoja +dialog.autoplay.rewind=Alkuun +dialog.autoplay.pause=Pys\u00e4yt\u00e4 +dialog.autoplay.play=Toista + +# 3d window +dialog.3d.title=GpsPrune:n kolmiulotteinen n\u00e4kym\u00e4 +dialog.3d.altitudefactor=Korkeuden liioittelukerroin + +# Confirm messages +confirm.loadfile=Paikkatieto ladattu tiedostosta +confirm.save.ok1=Successfully saved +confirm.save.ok2=points to file +confirm.deletepoint.single=data point was removed +confirm.deletepoint.multi=data points were removed +confirm.point.edit=point edited +confirm.mergetracksegments=Track segments merged +confirm.reverserange=Range reversed +confirm.addtimeoffset=Time offset added +confirm.addaltitudeoffset=Altitude offset added +confirm.rearrangewaypoints=Kohdepisteet j\u00e4rjestetty uudelleen +confirm.rearrangephotos=Kuvat j\u00e4rjestetty uudelleen +confirm.splitsegments=%d segment splits were made +confirm.sewsegments=%d segment joins were made +confirm.cutandmove=Selection moved +confirm.interpolate=Points added +confirm.convertnamestotimes=V\u00e4lipisteiden nimet muunnettu +confirm.saveexif.ok=Saved %d photo files +confirm.undo.single=operation undone +confirm.undo.multi=operations undone +confirm.jpegload.single=photo was added +confirm.jpegload.multi=photos were added +confirm.media.connect=media liitetty +confirm.photo.disconnect=kuva liitt\u00e4m\u00e4t\u00e6n +confirm.audio.disconnect=\u00e4\u00e4ni liitt\u00e4m\u00e4t\u00e6n +confirm.media.removed=poistettu +confirm.correlatephotos.single=kuva oli korreloitu +confirm.correlatephotos.multi=kuvat olivat korreloidut +confirm.rotatephoto=kuva kierretty +confirm.createpoint=piste luotu +confirm.running=Running ... +confirm.lookupsrtm=L\u00f6ytyi %d korkeusarvoa +confirm.downloadsrtm=Ladattu %d tiedostoa v\u00e4limuistiin +confirm.downloadsrtm.1=Ladattu %d tiedosto v\u00e4limuistiin +confirm.downloadsrtm.none=Tiedostoja ei ladattu, koska ne olivat jo v\u00e4limuistissa +confirm.deletefieldvalues=Kentt\u00e4arvot poistettu +confirm.audioload=\u00c4\u00e4nitiedostot lis\u00e4tty +confirm.correlateaudios.single=\u00e4\u00e4ni oli korreloitu +confirm.correlateaudios.multi=\u00e4\u00e4net olivat korreloidut + +# Tips, shown just once when appropriate +tip.title=Vihje +tip.useamapcache=Jos tallennutat karttapalat v\u00e4limuistina toimivaan hakemistoon levylle\n Valikko: Asetukset -> Talleta kartat hakemistoon,\nn\u00e4yt\u00f6n toiminta nopeutuu ja verkkoliikenne v\u00e4hentyy. +tip.learntimeparams=Tuloset voivat olla tarkempia, jos suoritutat toiminnon\n Valikko: Reitti -> Opi aika-arvion parametrit\ntallentamillesi reiteille. +tip.downloadsrtm=Voit nopeututtaa t\u00e4t\u00e4 suorituttamalla toiminnon\n Valikko: Online -> Lataa SRTM-palat\nkarttojen v\u00e4limuistihakemistoon. +tip.usesrtmfor3d=T\u00e4ss\u00e4 reitiss\u00e4 ei ole korkeustietoja.\nVoit k\u00e4ytt\u00e4\u00e4 SRTM-toimintoja saadaksesi likim\u00e4\u00e4r\u00e4iset\nkorkeusarvot 3D-n\u00e4kym\u00e4\u00e4 varten. +tip.manuallycorrelateone=Jos korjaat v\u00e4hint\u00e4\u00e4n yhden kohteen aikatiedot, ohjelma voi laskea aikapoikkeamat puolestasi. + +# Buttons +button.ok=OK +button.back=Takaisin +button.next=Seuraava +button.finish=Loppuun +button.cancel=Peru +button.overwrite=Korvaa +button.moveup=Siirry yl\u00f6s +button.movedown=Siirry alas +button.edit=Muokkaa +button.exit=Lopeta +button.close=Sulje +button.continue=Jatka +button.yes=Kyll\u00e4 +button.no=Ei +button.yestoall=Kyll\u00e4 kaikkiin +button.notoall=Ei kaikkiin +button.always=Aina +button.select=Valitse +button.selectall=Valitse kaikki +button.selectnone=Valitse 'ei mit\u00e4\u00e4n' +button.preview=Esikatselu +button.load=Lataa +button.upload=Uppaa +button.guessfields=Arvaa kent\u00e4t +button.showwebpage=N\u00e4yt\u00e4 webbisivu +button.check=Tarkista +button.resettodefaults=Palauta oletusarvot +button.browse=Selaa... +button.addnew=Lis\u00e4\u00e4 uusi +button.delete=Poista +button.manage=Hallinnoi +button.combine=Yhdist\u00e4 + +# File types +filetype.txt=TXT tiedostot +filetype.jpeg=JPG tiedostot +filetype.kmlkmz=KML, KMZ tiedostot +filetype.kml=KML tiedostot +filetype.kmz=KMZ tiedostot +filetype.gpx=GPX tiedostot +filetype.pov=POV tiedostot +filetype.svg=SVG tiedostot +filetype.png=PNG tiedostot +filetype.audio=MP3, OGG, WAV tiedostot + +# Display components +display.nodata=Reittidataa ei ladattuna +display.noaltitudes=Reittidata ei sis\u00e4ll\u00e4 korkeustietoja +display.notimestamps=Reittidata ei sis\u00e4ll\u00e4 aikaleimoja +display.novalues=Reittidata ei sis\u00e4ll\u00e4 tietoja t\u00e4lle kent\u00e4lle +details.trackdetails=Reitin tiedot +details.notrack=Reitti\u00e4 ei ladattuna +details.track.points=Reittipisteit\u00e4 +details.track.file=Tiedosto +details.track.numfiles=Tiedostojen m\u00e4\u00e4r\u00e4 +details.pointdetails=Pisteen tiedot +details.index.selected=Index +details.index.of=... +details.nopointselection=Yht\u00e4\u00e4n pistett\u00e4 ei valittuna +details.photofile=Kuvatiedosto +details.norangeselection=Pistealuetta ei valittuna +details.rangedetails=Pistealueen tiedot +details.range.selected=Valittu +details.range.to=... +details.altitude.to=... +details.range.climb=Nousu +details.range.descent=Lasku +details.coordformat=Koordinaatisto +details.distanceunits=V\u00e4limatkan yksikk\u00f6 +display.range.time.secs=s +display.range.time.mins=m +display.range.time.hours=t +display.range.time.days=pv +details.range.avespeed=Keskinopeus +details.range.maxspeed=Enimm\u00e4isnopeus +details.range.numsegments=Lohkojen m\u00e4\u00e4r\u00e4 +details.range.pace=Vauhti +details.range.gradient=Kaltevuus +details.lists.waypoints=Kohdepisteet +details.lists.photos=Kuvat +details.lists.audio=\u00d4\u00e4net +details.photodetails=Kuvan tiedot +details.nophoto=Kuvaa ei valittuna +details.photo.loading=Lataan +details.photo.bearing=Suunta +details.media.connected=Liitetty +details.media.fullpath=Tiedostopolku +details.audiodetails=Audio details +details.noaudio=\u00c4\u00e4nileikett\u00e4 ei valittuna +details.audio.file=\u00c4\u00e4nitiedosto +details.audio.playing=toistetaan... +map.overzoom=Ei karttoja t\u00e4lle suurennussuhteelle + +# Field names +fieldname.latitude=Leveysaste (Lat.) +fieldname.longitude=Pituusaste (Long.) +fieldname.altitude=Korkeus +fieldname.timestamp=Aikaleima +fieldname.time=Aika +fieldname.date=P\u00e4iv\u00e4m\u00e4\u00e4r\u00e4 +fieldname.waypointname=Nimi +fieldname.waypointtype=Tyyppi +fieldname.newsegment=Reittilohko +fieldname.custom=Mukautettu +fieldname.prefix=Kentt\u00e4 +fieldname.distance=V\u00e4limatka +fieldname.duration=Kesto +fieldname.speed=Nopeus +fieldname.verticalspeed=Pystysuora nopeus +fieldname.description=Kuvaus +fieldname.mediafilename=Median tiedostonimi + +# Measurement units +units.original=Alkuper\u00e4inen +units.default=Oletus +units.metres=Metrit +units.metres.short=m +units.feet=Jalat +units.feet.short=ft +units.kilometres=Kilometrit +units.kilometres.short=km +units.kilometresperhour=kilometri\u00e4 tunnissa +units.kilometresperhour.short=km/t +units.miles=Mailit +units.miles.short=mi +units.milesperhour=mailia tunnissa +units.milesperhour.short=mph +units.nauticalmiles=Merimailit +units.nauticalmiles.short=N.m. +units.nauticalmilesperhour.short=kts +units.metrespersec=metri\u00e4 sekunnissa +units.metrespersec.short=m/s +units.feetpersec=jalkaa sekunnissa +units.feetpersec.short=ft/s +units.hours=tunnit +units.minutes=minuutit +units.seconds=sekunnit +units.degminsec=Asteet-minuutit-sekunnit +units.degmin=Asteet-minuutit +units.deg=Asteet +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=ja +logic.or=tai + +# External urls +url.googlemaps=maps.google.co.uk +wikipedia.lang=en +openweathermap.lang=en +webservice.peakfinder=Avaa Peakfinder.org +webservice.geohack=Avaa Geohack-sivu + +# Cardinals for 3d plots +cardinal.n=P +cardinal.s=E +cardinal.e=I +cardinal.w=L + +# Undo operations +undo.load=lataa data +undo.loadphotos=lataa kuvat +undo.loadaudios=lataa \u00e4\u00e4nileikkeet +undo.editpoint=muokkaa reittipistett\u00e4 +undo.deletepoint=tuhoa reittipiste +undo.removephoto=poista kuva +undo.removeaudio=poista \u00e4\u00e4nileike +undo.deleterange=tuhoa pistealue +undo.croptrack=typist\u00e4 reitti +undo.deletemarked=tuhoa merkityt reittipisteet +undo.insert=liit\u00e4 pisteit\u00e4 +undo.reverse=k\u00e4\u00e4nteinen pistealue +undo.mergetracksegments=yhdist\u00e4 reittilohkot +undo.splitsegments=pilko reittilohkot +undo.sewsegments=yhdist\u00e4 reittilohkot +undo.addtimeoffset=lis\u00e4\u00e4 aikapoikkeama +undo.addaltitudeoffset=lis\u00e4\u00e4 korkeuspoikkeama +undo.rearrangewaypoints=j\u00e4rjest\u00e4 kohdepisteet +undo.cutandmove=siirr\u00e4 section +undo.connect=yhdist\u00e4 +undo.disconnect=irroita +undo.correlatephotos=korreloi kuvat +undo.rearrangephotos=j\u00e4rjest\u00e4 kuvat +undo.rotatephoto=kierr\u00e4 kuvaa +undo.createpoint=luo piste +undo.convertnamestotimes=muunna nimet ajoiksi +undo.lookupsrtm=lookup altitudes from SRTM +undo.deletefieldvalues=poista kentt\u00e4arvot +undo.correlateaudios=korreloi \u00e4\u00e4nileikkeet + +# Error messages +error.save.dialogtitle=Datan tallennuksessa virhe +error.save.nodata=Ei dataa tallennettavaksi +error.save.failed=Data tallennus ep\u00e4onnistui tiedostoon +error.saveexif.filenotfound=Kuvatiedoston etsint\u00e4 ep\u00e4onnistui +error.saveexif.cannotoverwrite1=Kuvatiedosto +error.saveexif.cannotoverwrite2=on kirjoitussuojattu ja sit\u00e4 ei voi korvata.\n\nLuodaanko kopio, johon kirjoitetaan EXIF-tiedot? +error.saveexif.failed=Kuvien %d ei onnistuttu tallentamaan +error.saveexif.forced=Kuvien %d -tietojen muutos pit\u00e4\u00e4 pakottaa +error.load.dialogtitle=Virhe ladattaessa dataa +error.load.noread=En voi lukea tiedostoa +error.load.nopoints=Koordinaattitietoja ei l\u00f6ytynyt tiedostosta +error.load.unknownxml=Tunnistamaton xml-muoto: +error.load.noxmlinzip=ZIP-arkistosta ei l\u00f6ytynyt XML-tiedostoa +error.load.othererror=Virhe luettaessa tiedostoa: +error.jpegload.dialogtitle=Virhe ladattaessa kuvia +error.jpegload.nofilesfound=Tiedostoja ei l\u00f6ytynyt +error.jpegload.nojpegsfound='jpeg'-tiedostoja ei l\u00f6ytnyt +error.jpegload.nogpsfound=GPS-tietoja ei l\u00f6ytnyt +error.audioload.nofilesfound=\u00c4\u00e4nileikkeit\u00e4 ei l\u00f6ytnyt +error.gpsload.unknown=Tuntematon virhe +error.undofailed.title=Peruminen ei onnistunut +error.undofailed.text=Toiminnon peruminen ep\u00e4onnistui +error.function.noop.title=Toiminnolla ei ole vaikutusta +error.rearrange.noop=Pisteiden uudelleen j\u00e4rjest\u00e4misell\u00e4 ei ollut vaikutusta +error.function.notavailable.title=Toiminto ei k\u00e4yt\u00f6ss\u00e4 +error.function.nojava3d=T\u00e4m\u00e4 toiminto vaatii Java3D-kirjaston.\n\nAsenna se tai tarkista Javan hakemistopolkujen m\u00e4\u00e4ritykset. +error.3d=Virhe 3D-n\u00e4kym\u00e4ss\u00e4 +error.readme.notfound=Lueminut-tiedostoa ei l\u00f6ytynyt +error.osmimage.dialogtitle=Virhe ladattaessa karttapaloja +error.osmimage.failed=Karttapalojen lataus ei onnistunut. Tarkista verkkoyhteytesi toimivuus. +error.language.wrongfile=Valittu tiedosto ei n\u00e4yt\u00e4 olevan GpsPrune:n kielitiedosto tai se sis\u00e4lt\u00e4\u00e4 virheit\u00e4. +error.convertnamestotimes.nonames=Kohdepisteiden nimi\u00e4 ei voitu muuntaa ajoiksi +error.lookupsrtm.nonefound=N\u00e4iss\u00e4 pisteiss\u00e4 ei ole korkeustietoja +error.lookupsrtm.nonerequired=Kaikilla pisteill\u00e4 on jo korkeustieto,\njoten ei ole mit\u00e4\u00e4n haettavaa +error.gpsies.uploadnotok='gpsies'-palvelin palautti viestin: +error.gpsies.uploadfailed=Tiedoston uppaus ep\u00e4onnistui virheilmoitukseen: +error.showphoto.failed=Kuvan lataus ei onnistunut +error.playaudiofailed=\u00c4\u00e4nileikkeen toisto ei onnistunut +error.cache.notthere=Karttapalojen v\u00e4limuistihakemistoa ei l\u00f6ytynyt +error.cache.empty=Karttapalojen v\u00e4limuistihakemisto on tyhj\u00e4 +error.cache.cannotdelete=Yht\u00e4\u00e4n karttapalaa ei voitu poistaa +error.learnestimationparams.failed=En voi opetella parametrej\u00e4 t\u00e4st\u00e4 reitist\u00e4.\n\nKokeile ladata useampia reittej\u00e4. +error.tracksplit.nosplit=Reitti\u00e4 ei voitu pilkkoa +error.downloadsrtm.nocache=SRTP-tiedostoja ei voitu tallentaa.\n\nTarkista karttapalojen v\u00e4limuistihakemisto tai sen asetukset. +error.sewsegments.nothingdone=Reittilohkoja ei voitu yhdist\u00e4\u00e4.\n\nReitiss\u00e4 on nyt %d lohkoa. diff --git a/tim/prune/lang/prune-texts_fr.properties b/tim/prune/lang/prune-texts_fr.properties index 064c6e9..ab79667 100644 --- a/tim/prune/lang/prune-texts_fr.properties +++ b/tim/prune/lang/prune-texts_fr.properties @@ -39,7 +39,7 @@ menu.view.browser.yahoo=Cartes Yahoo menu.view.browser.bing=Cartes Bing menu.settings=Pr\u00e9f\u00e9rences menu.settings.onlinemode=Charger cartes depuis internet -menu.settings.antialias=Anticr\u00e9nelage +dialog.displaysettings.antialias=Anticr\u00e9nelage menu.settings.autosave=Sauver automatiquement en quittant menu.help=Aide # Popup menu for map @@ -83,7 +83,6 @@ function.sendtogps=Envoyer donn\u00e9es au GPS function.exportkml=Exporter en KML function.exportgpx=Exporter en GPX function.exportpov=Exporter en POV -function.exportsvg=Exporter en SVG function.exportimage=Exporter une image function.editwaypointname=\u00c9diter le nom du point function.compress=Compresser la trace @@ -121,7 +120,6 @@ function.mapillary=Rechercher dans Mapillary function.downloadosm=T\u00e9l\u00e9charger les donn\u00e9es OSM de la zone function.duplicatepoint=Dupliquer le point function.setcolours=Choisir les couleurs -function.setlinewidth=Choisir la largeur de ligne function.setlanguage=Choisir la langue function.connecttopoint=Relier au point function.disconnectfrompoint=D\u00e9tacher du point @@ -261,10 +259,6 @@ dialog.baseimage.zoom=Zoom dialog.baseimage.incomplete=Image incompl\u00e8te dialog.baseimage.tiles=Dalles dialog.baseimage.size=Taille de l'image -dialog.exportsvg.text=S\u00e9lectionner les param\u00e8tres de l'export SVG -dialog.exportsvg.phi=Angle d'azimuth \u03d5 -dialog.exportsvg.theta=Angle d'\u00e9l\u00e9vation \u03b8 -dialog.exportsvg.gradients=Utiliser des d\u00e9grad\u00e9s pour l'ombrage dialog.exportimage.drawtrack=Dessiner la trace sur la carte dialog.exportimage.drawtrackpoints=Dessiner les points de trace dialog.exportimage.textscalepercent=Facteur d'echelle du texte (%) @@ -439,12 +433,12 @@ dialog.deletemarked.nonefound=Pas de donn\u00e9es \u00e0 effacer dialog.pastecoordinates.desc=Entrez ou collez les coordonn\u00e9es ici dialog.pastecoordinates.coords=Coordonn\u00e9es dialog.pastecoordinates.nothingfound=V\u00e9rifier les coordonn\u00e9es et essayez \u00e0 nouveau -dialog.help.help=Consultez la page\n http://gpsprune.activityworkshop.net/\npour plus de d\u00e9tails et des manuels utilisateur. +dialog.help.help=Consultez la page\n https://gpsprune.activityworkshop.net/\npour plus de d\u00e9tails et des manuels utilisateur. dialog.about.version=Version dialog.about.build=Build dialog.about.summarytext1=GpsPrune est un programme pour charger, afficher et \u00e9diter des donn\u00e9es de r\u00e9cepteurs GPS. dialog.about.summarytext2=Distribu\u00e9 sous license Gnu GPL pour un usage et une am\u00e9lioration libres, ouverts et mondiaux.
La copie, la redistribution et la modification sont autoris\u00e9es et encourag\u00e9es
selon les conditions d\u00e9taill\u00e9es dans le fichier license.txt inclus. -dialog.about.summarytext3=Consultez la page http://activityworkshop.net/ pour plus de d\u00e9tails et des manuels utilisateur. +dialog.about.summarytext3=Consultez la page https://activityworkshop.net/ pour plus de d\u00e9tails et des manuels utilisateur. dialog.about.languages=Langues disponibles dialog.about.translatedby=Texte en fran\u00e7ais par Petrovsk, theYinYeti, R\u00e9mi et jmr. dialog.about.systeminfo=Info Syst\u00e8me @@ -455,11 +449,6 @@ dialog.about.systeminfo.povray=Povray install\u00e9 dialog.about.systeminfo.exiftool=Exiftool install\u00e9 dialog.about.systeminfo.gpsbabel=Gpsbabel install\u00e9 dialog.about.systeminfo.gnuplot=Gnuplot install\u00e9 -dialog.about.systeminfo.exiflib=Librairie Exif -dialog.about.systeminfo.exiflib.internal=Interne -dialog.about.systeminfo.exiflib.internal.failed=Interne (non-trouv\u00e9) -dialog.about.systeminfo.exiflib.external=Externe -dialog.about.systeminfo.exiflib.external.failed=Externe (non-trouv\u00e9) dialog.about.yes=Oui dialog.about.no=Non dialog.about.credits=Cr\u00e9dits @@ -478,7 +467,7 @@ dialog.checkversion.newversion1=La version actuelle est maintenant la version dialog.checkversion.newversion2=. dialog.checkversion.releasedate1=La nouvelle version est sortie le dialog.checkversion.releasedate2=. -dialog.checkversion.download=Pour t\u00e9l\u00e9charger la nouvelle version, aller \u00e0 http://gpsprune.activityworkshop.net/download.html. +dialog.checkversion.download=Pour t\u00e9l\u00e9charger la nouvelle version, aller \u00e0 https://gpsprune.activityworkshop.net/download.html. dialog.keys.intro=Vous pouvez utiliser ces raccourcis clavier \u00e0 la place de la souris dialog.keys.keylist=
Touches-fl\u00e8chesFaire d\u00e9filer la carte horizontalement et verticalement
Ctrl + gauche, Ctrl + droiteChoisir le point pr\u00e9c\u00e9dent ou suivant
Ctrl + haut, Ctrl + basZoomer, s'\u00e9loigner
Ctrl + PgUp, PgDownChoisir le segment pr\u00e9c\u00e9dent ou suivant
Ctrl + Home, EndChoisir le point premier, dernier
SupprEffacer le point courant
dialog.keys.normalmodifier=Ctrl @@ -556,7 +545,7 @@ dialog.diskcache.deleteall=Efface toutes les dalles 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.displaysettings.linewidth=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 @@ -610,8 +599,6 @@ confirm.addtimeoffset=D\u00e9calage ajout\u00e9 confirm.addaltitudeoffset=D\u00e9calage d'altitude ajout\u00e9 confirm.rearrangewaypoints=Waypoints r\u00e9arrang\u00e9s confirm.rearrangephotos=Photos r\u00e9arrang\u00e9es -confirm.splitsegments=%d s\u00e9parations ont \u00e9t\u00e9 effectu\u00e9es -confirm.sewsegments=%d r\u00e9unifications ont \u00e9t\u00e9 effectu\u00e9es confirm.cutandmove=S\u00e9lection d\u00e9plac\u00e9e confirm.interpolate=Points ajout\u00e9s confirm.convertnamestotimes=Noms de waypoints convertis @@ -802,7 +789,6 @@ wikipedia.lang=fr openweathermap.lang=fr webservice.peakfinder=Ouvrir Peakfinder.org webservice.geohack=Ouvrir la page Geohack -webservice.panoramio=Ouvrir la carte Panoramio # Cardinals for 3d plots cardinal.n=N @@ -860,7 +846,6 @@ error.jpegload.dialogtitle=Erreur au chargement des photos error.jpegload.nofilesfound=Aucun fichier trouv\u00e9 error.jpegload.nojpegsfound=Aucun fichier jpeg trouv\u00e9 error.jpegload.nogpsfound=Aucune information GPS trouv\u00e9e -error.jpegload.exifreadfailed=Information Exif illisible. Aucune information Exif ne peut \u00eatre lue\nsans une librairie interne ou externe. error.audioload.nofilesfound=Aucun fichier audio trouv\u00e9 error.gpsload.unknown=Erreur inconnue error.undofailed.title=\u00c9chec de l'annulation diff --git a/tim/prune/lang/prune-texts_hu.properties b/tim/prune/lang/prune-texts_hu.properties index b949500..9b61e87 100644 --- a/tim/prune/lang/prune-texts_hu.properties +++ b/tim/prune/lang/prune-texts_hu.properties @@ -38,7 +38,7 @@ menu.view.browser.yahoo=Yahoo! Maps menu.view.browser.bing=Bing Maps menu.settings=Be\u00e1ll\u00edt\u00e1sok menu.settings.onlinemode=T\u00e9rk\u00e9pek bet\u00f6lt\u00e9se internetr\u0151l -menu.settings.antialias=\u00c9lsim\u00edt\u00e1s haszn\u00e1lata +dialog.displaysettings.antialias=\u00c9lsim\u00edt\u00e1s haszn\u00e1lata menu.settings.autosave=Be\u00e1ll\u00edt\u00e1sok automatikus ment\u00e9se kil\u00e9p\u00e9skor menu.help=S\u00fag\u00f3 @@ -83,7 +83,6 @@ function.sendtogps=Adatok felt\u00f6lt\u00e9se GPS-re 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 @@ -122,7 +121,6 @@ function.mapillary=F\u00e9nyk\u00e9pek keres\u00e9se Mapillary-n function.downloadosm=OSM adatok let\u00f6lt\u00e9se a ter\u00fcletr\u0151l function.duplicatepoint=Pont kett\u0151z\u00e9se function.setcolours=Sz\u00ednek be\u00e1ll\u00edt\u00e1sa -function.setlinewidth=Vonalsz\u00e9less\u00e9g be\u00e1ll\u00edt\u00e1sa function.setlanguage=Nyelv be\u00e1ll\u00edt\u00e1sa function.connecttopoint=Kapcsol\u00e1s ponthoz function.disconnectfrompoint=Lev\u00e1laszt\u00e1s pontr\u00f3l @@ -265,10 +263,6 @@ 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 @@ -388,6 +382,7 @@ dialog.wikipedia.column.name=Sz\u00f3cikk neve dialog.wikipedia.column.distance=T\u00e1vols\u00e1g dialog.wikipedia.nonefound=Nem tal\u00e1lhat\u00f3 Wikip\u00e9dia sz\u00f3cikk dialog.wikipedia.gallery=Gal\u00e9ria +dialog.geocaching.nonefound=Nem tal\u00e1lhat\u00f3 geol\u00e1da dialog.correlate.notimestamps=Nincsenek id\u0151b\u00e9lyegek az adatpontokon, \u00edgy nem feleltethet\u0151 meg semmi a f\u00e9nyk\u00e9pekkel. dialog.correlate.nouncorrelatedphotos=Nincsenek megfeleltetlen f\u00e9nyk\u00e9pek.\nBiztos benne, hogy folytatja? dialog.correlate.nouncorrelatedaudios=Nincsenek megfeleltetlen hangok.\nBiztos benne, hogy folytatja? @@ -446,12 +441,12 @@ 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://gpsprune.activityworkshop.net/\nwebhelyet. +dialog.help.help=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a \n https://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.summarytext3=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a https://activityworkshop.net/ webhelyet. dialog.about.languages=El\u00e9rhet\u0151 nyelvek dialog.about.translatedby=Magyar sz\u00f6veg: Ball\u00f3 Gy\u00f6rgy \u00e9s B\u00e1thory P\u00e9ter dialog.about.systeminfo=Rendszerinform\u00e1ci\u00f3 @@ -462,11 +457,6 @@ dialog.about.systeminfo.povray=Povray telep\u00edtve dialog.about.systeminfo.exiftool=Exiftool telep\u00edtve dialog.about.systeminfo.gpsbabel=Gpsbabel telep\u00edtve dialog.about.systeminfo.gnuplot=Gnuplot telep\u00edtve -dialog.about.systeminfo.exiflib=Exif f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r -dialog.about.systeminfo.exiflib.internal=Be\u00e9p\u00edtett -dialog.about.systeminfo.exiflib.internal.failed=Be\u00e9p\u00edtett (nem tal\u00e1lhat\u00f3) -dialog.about.systeminfo.exiflib.external=K\u00fcls\u0151 -dialog.about.systeminfo.exiflib.external.failed=K\u00fcls\u0151 (nem tal\u00e1lhat\u00f3) dialog.about.yes=Igen dialog.about.no=Nem dialog.about.credits=K\u00e9sz\u00edt\u0151k @@ -485,7 +475,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://gpsprune.activityworkshop.net/download.html webhelyet. +dialog.checkversion.download=Az \u00faj verzi\u00f3 let\u00f6lt\u00e9s\u00e9hez keresse fel a https://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 @@ -564,7 +554,7 @@ dialog.diskcache.deleteall=Az \u00f6sszes csempe t\u00f6rl\u00e9se 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.displaysettings.linewidth=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 @@ -812,7 +802,6 @@ wikipedia.lang=hu openweathermap.lang=en webservice.peakfinder=Peakfinder.org megnyit\u00e1sa webservice.geohack=A geohack lap nyit\u00e1sa -webservice.panoramio=Panoramio t\u00e9rk\u00e9p nyit\u00e1sa # Cardinals for 3d plots cardinal.n=\u00c9 @@ -870,7 +859,6 @@ 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 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 diff --git a/tim/prune/lang/prune-texts_it.properties b/tim/prune/lang/prune-texts_it.properties index d8e6895..892d88d 100644 --- a/tim/prune/lang/prune-texts_it.properties +++ b/tim/prune/lang/prune-texts_it.properties @@ -39,7 +39,6 @@ menu.view.browser.yahoo=Mappe Yahoo menu.view.browser.bing=Mappe Bing menu.settings=Preferenze menu.settings.onlinemode=Carica mappa da internet -menu.settings.antialias=Usa antialiasing menu.settings.autosave=Salva settaggi con chiusura del programma menu.help=Aiuto # Popup menu for map @@ -83,7 +82,6 @@ function.sendtogps=Invia dati al GPS function.exportkml=Esporta in KML function.exportgpx=Esporta in GPX function.exportpov=Esporta in POV -function.exportsvg=Esporta in SVG function.exportimage=Esporta come immagine function.editwaypointname=Modifica nome waypoint function.compress=Comprimi la traccia @@ -110,18 +108,20 @@ function.setpaths=Configura percorsi programmi function.selectsegment=Seleziona segmento corrente function.splitsegments=Dividi traccia in segmenti function.sewsegments=Riorganizza segmenti insieme +function.createmarkerwaypoints=Crea marcatori function.getgpsies=Ottieni tracce 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.getwikipedia=Ottieni i punti Wikipedia nelle vicinanze function.searchwikipedianames=Cerca il nome in Wikipedia +function.searchosmpois=Ottieni i punti OSM nelle vicinanze function.searchopencachingde=Cerca OpenCaching.de function.mapillary=Cerca Mapillary.com function.downloadosm=Scarica dati OSM dell'area function.duplicatepoint=Duplica punto corrente in coda function.setcolours=Scegli colori -function.setlinewidth=Scegli la lo spessore +function.setdisplaysettings=Opzioni di visualizzazione function.setlanguage=Scegli la lingua function.connecttopoint=Collega al punto function.disconnectfrompoint=Scollega dal punto @@ -146,6 +146,7 @@ function.diskcache=Salva mappe su disco function.managetilecache=Gestione del cache di tasselli function.getweatherforecast=Ottieni previsioni del tempo function.setaltitudetolerance=Configura tolleranza di altitudini +function.selecttimezone=Seleziona fuso orario # Dialogs dialog.exit.confirm.title=Esci da GpsPrune @@ -264,10 +265,6 @@ dialog.baseimage.zoom=Livello di zoom dialog.baseimage.incomplete=Immagine incompleta dialog.baseimage.tiles=Tiles dialog.baseimage.size=Dimensione immagine -dialog.exportsvg.text=Seleziona i parametri per esportare in SVG -dialog.exportsvg.phi=Angolo orizzontale \u03d5 -dialog.exportsvg.theta=Angolo di elevazione \u03b8 -dialog.exportsvg.gradients=Usa il gradiente per le ombre dialog.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 @@ -387,6 +384,9 @@ dialog.wikipedia.column.name=Titolo articolo dialog.wikipedia.column.distance=Distanza dialog.wikipedia.nonefound=Nessuna punti trovata dialog.wikipedia.gallery=Immagine +dialog.osmpois.column.name=Nome +dialog.osmpois.column.type=Tipo +dialog.osmpois.nonefound=Nessuna punti trovata dialog.geocaching.nonefound=Nessuna punti trovata dialog.correlate.notimestamps=Non ci sono informazioni temporali tra i dati dei punti, non c'\u00e8 niente per collegarli con le foto. dialog.correlate.nouncorrelatedphotos=Non ci sono foto non correlate.\nSei sicuro di voler continuare? @@ -446,12 +446,12 @@ dialog.deletemarked.nonefound=Nessun punto rimosso dialog.pastecoordinates.desc=Inserisci o incolla qui le coordinate dialog.pastecoordinates.coords=Coordinate dialog.pastecoordinates.nothingfound=Per favore, controlla le coordinate e riprova -dialog.help.help=Per favore vedi\n http://gpsprune.activityworkshop.net/\nper maggiori informazioni e per la guida utente. +dialog.help.help=Per favore vedi\n https://gpsprune.activityworkshop.net/\nper maggiori informazioni e per la guida utente. dialog.about.version=Versione dialog.about.build=Build dialog.about.summarytext1=GpsPrune \u00e8 un programma per il caricamento, la visione e l'edit di dati provenienti da un GPS. dialog.about.summarytext2=\u00c8 rilasciato sotto la licenza Gnu GPL per l'uso gratuito e aperto ed il suo miglioramento, con validit\u00e0 mondiale.
La copia, la ridistribuzione sono permesse e incoraggiate
in accordo con i termini inclusi nel file license.txt. -dialog.about.summarytext3=Per favore vedi http://activityworkshop.net/ per maggiori informazioni e per la guida utente. +dialog.about.summarytext3=Per favore vedi https://activityworkshop.net/ per maggiori informazioni e per la guida utente. dialog.about.languages=Lingue disponibili dialog.about.translatedby=Testo italiano di Giovanni Sartor + altro dialog.about.systeminfo=Info di sistema @@ -462,11 +462,6 @@ dialog.about.systeminfo.povray=Povray installato dialog.about.systeminfo.exiftool=Exiftool installato dialog.about.systeminfo.gpsbabel=Gpsbabel installato dialog.about.systeminfo.gnuplot=Gnuplot installato -dialog.about.systeminfo.exiflib=Libreria exif -dialog.about.systeminfo.exiflib.internal=Interna -dialog.about.systeminfo.exiflib.internal.failed=Interna (non trovata) -dialog.about.systeminfo.exiflib.external=Esterna -dialog.about.systeminfo.exiflib.external.failed=Esterna (non trovata) dialog.about.yes=S\u00ec dialog.about.no=No dialog.about.credits=Crediti @@ -485,7 +480,7 @@ dialog.checkversion.newversion1=Una nuova versione di GpsPrune \u00e8 disponibil dialog.checkversion.newversion2=. dialog.checkversion.releasedate1=Questa nuova versione \u00e8 stata rilasciata il dialog.checkversion.releasedate2=. -dialog.checkversion.download=Per scaricare la nuova versione vai a http://gpsprune.activityworkshop.net/download.html. +dialog.checkversion.download=Per scaricare la nuova versione vai a https://gpsprune.activityworkshop.net/download.html. dialog.keys.intro=Puoi utilizzare i seguenti tast di scelta rapida al posto del mouse dialog.keys.keylist=
Tasti frecciaMuovi mappa destra, sinistra, su, giu'
Ctrl + freccia destra, sinistraSelezione punto successivo o precedente
Ctrl + freccia su, giu'Zoom in o out
Ctrl + pagina su, giu'Segmento successivo o precedente
Ctrl + Home, EndPunto primo o ultimo
DelCancella punto attuale
dialog.keys.normalmodifier=Ctrl @@ -564,7 +559,17 @@ dialog.diskcache.deleteall=Cancellare tutti tasselli 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.displaysettings.linewidth=Tratteggio delle linee per disegnare la traccia (1-4) +dialog.displaysettings.antialias=Usa antialiasing +dialog.displaysettings.waypointicons=Icone waypoint +dialog.displaysettings.wpicon.default=Default +dialog.displaysettings.wpicon.ringpt=Etichetta rotonda +dialog.displaysettings.wpicon.plectrum=Plettro +dialog.displaysettings.wpicon.ring=Anello +dialog.displaysettings.wpicon.pin=Spillo da lavagna +dialog.displaysettings.size.small=Piccole +dialog.displaysettings.size.medium=Medie +dialog.displaysettings.size.large=Grandi dialog.downloadosm.desc=Conferma lo scarico dei dati raw OSM per l'area specificata: dialog.searchwikipedianames.search=Cerca per: dialog.weather.location=Localit\u00e0 @@ -596,6 +601,12 @@ dialog.deletebydate.column.keep=Tieni dialog.deletebydate.column.delete=Cancella dialog.setaltitudetolerance.text.metres=Tolleranza di altitudini (metri) dialog.setaltitudetolerance.text.feet=Tolleranza di altitudini (feet) +dialog.settimezone.intro=Qui puoi selezionare il fuso orario con cui visualizzare il timestamp dei punti +dialog.settimezone.system=Utilizza il fuso orario di sistema +dialog.settimezone.custom=Utilizza il seguente fuso orario: +dialog.settimezone.list.toomany=Troppe zone +dialog.settimezone.selectedzone=Fuso orario selezionato +dialog.settimezone.offsetfromutc=Offset rispetto a UTC dialog.autoplay.duration=Durata (sec) dialog.autoplay.usetimestamps=Usa dati temporali dialog.autoplay.rewind=Riavvolga @@ -812,7 +823,6 @@ wikipedia.lang=it openweathermap.lang=it webservice.peakfinder=Apri Peakfinder.org webservice.geohack=Apri la pagina Geohack -webservice.panoramio=Apri la mappa Panoramio # Cardinals for 3d plots cardinal.n=N @@ -870,7 +880,6 @@ error.jpegload.dialogtitle=Errore nel caricamento delle foto error.jpegload.nofilesfound=File non trovato error.jpegload.nojpegsfound=File jpeg non trovato error.jpegload.nogpsfound=Informazioni GPS non trovate -error.jpegload.exifreadfailed=Lettera dei dati Exif fallita. I dati Exif non possono\n essere letti senza una libreria interna o esterna. error.audioload.nofilesfound=Riprese audio non trovate error.gpsload.unknown=Errore sconosciuto error.undofailed.title=Impossibile annullare diff --git a/tim/prune/lang/prune-texts_ja.properties b/tim/prune/lang/prune-texts_ja.properties index 087e8f7..4f9d7ee 100644 --- a/tim/prune/lang/prune-texts_ja.properties +++ b/tim/prune/lang/prune-texts_ja.properties @@ -79,7 +79,6 @@ function.sendtogps=GPS\u3078\u4fdd\u5b58 function.exportkml=KML\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 function.exportgpx=GPX\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 function.exportpov=POV\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 -function.exportsvg=SVG\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 function.exportimage=\u30a4\u30e1\u30fc\u30b8\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 function.editwaypointname=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u306e\u540d\u524d\u3092\u7de8\u96c6 function.compress=\u30c8\u30e9\u30c3\u30af\u3092\u5727\u7e2e @@ -105,7 +104,6 @@ function.searchwikipedianames=\u540d\u524d\u3067Wikipedia\u3092\u691c\u7d22 function.downloadosm=\u30a8\u30ea\u30a2\u306eOSM\u30c7\u30fc\u30bf\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9 function.duplicatepoint=\u91cd\u8907\u70b9 function.setcolours=\u8272\u3092\u8a2d\u5b9a -function.setlinewidth=\u884c\u306e\u5e45\u3092\u8a2d\u5b9a function.setlanguage=\u8a00\u8a9e\u8a2d\u5b9a function.connecttopoint=\u70b9\u306b\u63a5\u7d9a function.disconnectfrompoint=\u70b9\u304b\u3089\u63a5\u7d9a\u89e3\u9664 @@ -332,12 +330,12 @@ 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://gpsprune.activityworkshop.net/ \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\nhttps://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 dialog.about.summarytext2=\u3053\u308c\u306f\u3001Gnu GPL\u306e\u4e0b\u306b\u516c\u958b\u3055\u308c\u3001\u81ea\u7531\u3067\u3001\u30aa\u30fc\u30d7\u30f3\u3067\u3001\u4e16\u754c\u4e2d\u3067\u3064\u304b\u3048\u3001\u62e1\u5f35\u53ef\u80fd\u3067\u3059\u3002
"license.txt"\u306b\u304b\u304b\u308c\u3066\u3044\u308b\u6761\u4ef6\u306b\u3088\u308b\u3068\u3001
\u8907\u88fd\u30fb\u518d\u914d\u5e03\u30fb\u6539\u5909\u306f\u3001\u8a31\u3055\u308c\u3066\u304a\u308a\u3001\u304b\u3064\u5968\u52b1\u3055\u308c\u3066\u3044\u307e\u3059\u3002 -dialog.about.summarytext3=\u3082\u3063\u3068\u8a73\u3057\u3044\u60c5\u5831\u3084\u30e6\u30fc\u30b6\u30fc\u30ac\u30a4\u30c9\u306a\u3069\u306f\u3001http://activityworkshop.net/\u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002 +dialog.about.summarytext3=\u3082\u3063\u3068\u8a73\u3057\u3044\u60c5\u5831\u3084\u30e6\u30fc\u30b6\u30fc\u30ac\u30a4\u30c9\u306a\u3069\u306f\u3001https://activityworkshop.net/\u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002 dialog.about.languages=\u5229\u7528\u53ef\u80fd\u306a\u8a00\u8a9e dialog.about.translatedby=\u65e5\u672c\u8a9e\u7ffb\u8a33\uff1aOpenStreetMap \u8ca2\u732e\u8005\u306enazotoko dialog.about.systeminfo=\u30b7\u30b9\u30c6\u30e0\u60c5\u5831 @@ -348,7 +346,6 @@ dialog.about.systeminfo.povray=Povray \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08 dialog.about.systeminfo.exiftool=Exiftool \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08 dialog.about.systeminfo.gpsbabel=Gpsbabel \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08 dialog.about.systeminfo.gnuplot=Gnuplot \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08 -dialog.about.systeminfo.exiflib=Exif\u30e9\u30a4\u30d6\u30e9\u30ea dialog.about.yes=\u306f\u3044 dialog.about.no=\u3044\u3044\u3048 dialog.about.credits=\u30af\u30ec\u30b8\u30c3\u30c8 @@ -367,7 +364,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://gpsprune.activityworkshop.net/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 https://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 @@ -415,7 +412,7 @@ dialog.diskcache.save=\u30c7\u30a3\u30b9\u30af\u306b\u30a4\u30e1\u30fc\u30b8\u30 dialog.diskcache.dir=\u30ad\u30e3\u30c3\u30b7\u30e5\u30c7\u30a3\u30ec\u30af\u30c8\u30ea dialog.diskcache.createdir=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210 dialog.diskcache.nocreate=\u30ad\u30e3\u30c3\u30b7\u30e5\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c\u4f5c\u6210\u3055\u308c\u306a\u304b\u3063\u305f\u3002 -dialog.setlinewidth.text=\u30c8\u30e9\u30c3\u30af\u63cf\u753b\u306e\u7dda\u5e45\u30921-4\u306e\u7bc4\u56f2\u3067\u5165\u529b +dialog.displaysettings.linewidth=\u30c8\u30e9\u30c3\u30af\u63cf\u753b\u306e\u7dda\u5e45\u30921-4\u306e\u7bc4\u56f2\u3067\u5165\u529b dialog.searchwikipedianames.search=\u53f3\u8a18\u3092\u691c\u7d22: # 3d window diff --git a/tim/prune/lang/prune-texts_ko.properties b/tim/prune/lang/prune-texts_ko.properties index e1f1945..21c0c60 100644 --- a/tim/prune/lang/prune-texts_ko.properties +++ b/tim/prune/lang/prune-texts_ko.properties @@ -77,7 +77,6 @@ function.sendtogps=GPS\ub85c \uc790\ub8cc \ubcf4\ub0b4\uae30 function.exportkml=KML \ub0b4\ubcf4\ub0b4\uae30 function.exportgpx=GPX \ub0b4\ubcf4\ub0b4\uae30 function.exportpov=POV \ub0b4\ubcf4\ub0b4\uae30 -function.exportsvg=SVG \ub0b4\ubcf4\ub0b4\uae30 function.editwaypointname=\uacbd\uc720\uc9c0 \uc774\ub984 \uc218\uc815 function.compress=\uacbd\ub85c \uc555\ucd95\ud558\uae30 function.addtimeoffset=\uc624\ud504\uc14b \uc2dc\uac04 \ucd94\uac00 @@ -100,7 +99,6 @@ function.searchwikipedianames=\uc774\ub984\uc73c\ub85c \uc704\ud0a4\ud53c\ub514\ function.downloadosm=OSM \ub370\uc774\ud0c0 \ub2e4\uc6b4\ub85c\ub4dc\ud558\uae30 function.duplicatepoint=\uc9c0\uc810 \ubcf5\uc0ac\ud558\uae30 function.setcolours=\uc0c9\uc0c1 \uc9c0\uc815 -function.setlinewidth=\uc120 \ub113\uc774 \uc9c0\uc815 function.setlanguage=\uc5b8\uc5b4 \uc9c0\uc815 function.connecttopoint=\uc9c0\uc810\uc73c\ub85c \uc5f0\uacb0 function.disconnectfrompoint=\uc9c0\uc810\uc5d0\uc11c \uc5f0\uacb0 \ub04a\uae30 @@ -194,10 +192,6 @@ dialog.exportpov.modelstyle=\ubaa8\ub378 \uc2a4\ud0c0\uc77c dialog.exportpov.ballsandsticks=\ub9c9\ub300\uae30\uc640 \uacf5 dialog.exportpov.tubesandwalls=\ubcbd\uacfc \ud29c\ube0c dialog.3d.warningtracksize=\uc774 \ud2b8\ub799\uc740 \uc9c0\uc810\uc774 \ub108\ubb34 \ub9ce\uc544 Java3D\uac00 \ud45c\ud604 \ubabb\ud560 \uc218\ub3c4 \uc788\uc2b5\ub2c8\ub2e4. /n \uadf8\ub798\ub3c4 \uacc4\uc18d \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? -dialog.exportsvg.text=SVG\ub85c \ub0b4\ubcf4\ub0bc \ud30c\ub77c\ubbf8\ud130\ub97c \uc120\ud0dd\ud558\uc138\uc694 -dialog.exportsvg.phi=\ubc29\uc704\uac01(\u03d5) -dialog.exportsvg.theta=\uace0\ub3c4(\u03b8) -dialog.exportsvg.gradients=\uc250\uc774\ub529\uc5d0 \uadf8\ub798\ub514\uc5b8\ud2b8 \uc0ac\uc6a9\ud558\uae30 dialog.pointtype.desc=\uc9c0\uc810 \ud615\uc2dd \uc800\uc7a5\ud558\uae30 dialog.pointtype.track=\ud2b8\ub799 \uc9c0\uc810 dialog.pointtype.waypoint=\uacbd\uc720 \uc9c0\uc810 @@ -335,12 +329,12 @@ 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://gpsprune.activityworkshop.net/ +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 https://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. dialog.about.summarytext2=\uc774 \ud504\ub85c\uadf8\ub7a8\uc740 \uc790\uc720\ub86d\uac8c, \uac1c\ubc29\uc801\uc73c\ub85c, \uadf8\ub9ac\uace0 \uc804\uc138\uacc4\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\uace0 \uac1c\uc120\ud558\uae30 \uc704\ud574 Gnu GPL \uc5d0 \ub530\ub77c\uc11c \ubc30\ud3ec\ub429\ub2c8\ub2e4.
\uc774 \ud504\ub85c\uadf8\ub7a8\uc5d0 \ud3ec\ud568\ub41c license.txt \ud30c\uc77c\uc758 \uc870\uac74\uc5d0 \ub530\ub77c \ubcf5\uc81c, \uc7ac\ubc30\ud3ec, \uc218\uc815\uc774 \ud5c8\uac00\ub418\uace0, \uc7a5\ub824\ub429\ub2c8\ub2e4. -dialog.about.summarytext3=\ub354 \uc790\uc138\ud55c \uc815\ubcf4\ub098 \uc0ac\uc6a9\uc790\uc124\uba85\uc740 \uc774\uacf3\uc744 \ucc38\uace0\ud574\uc8fc\uc138\uc694.http://activityworkshop.net/ +dialog.about.summarytext3=\ub354 \uc790\uc138\ud55c \uc815\ubcf4\ub098 \uc0ac\uc6a9\uc790\uc124\uba85\uc740 \uc774\uacf3\uc744 \ucc38\uace0\ud574\uc8fc\uc138\uc694.https://activityworkshop.net/ dialog.about.languages=\uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc5b8\uc5b4\ub4e4 dialog.about.translatedby=\ud55c\uad6d\uc5b4 Hooau dialog.about.systeminfo=\uc2dc\uc2a4\ud15c \uc815\ubcf4 @@ -351,11 +345,6 @@ dialog.about.systeminfo.povray=Povray\uac00 \uc124\uce58\ub418\uc5c8\uc74c dialog.about.systeminfo.exiftool=Exiftool\uc774 \uc124\uce58\ub418\uc5c8\uc74c dialog.about.systeminfo.gpsbabel=Gpsbabel\uc774 \uc124\uce58\ub418\uc5c8\uc74c dialog.about.systeminfo.gnuplot=Gnuplot\uc774 \uc124\uce58\ub418\uc5c8\uc74c -dialog.about.systeminfo.exiflib=Exif \ub77c\uc774\ube0c\ub7ec\ub9ac -dialog.about.systeminfo.exiflib.internal=\ub0b4\uc7a5 -dialog.about.systeminfo.exiflib.internal.failed=\ub0b4\uc7a5(\ucc3e\uc9c0\ubabb\ud568) -dialog.about.systeminfo.exiflib.external=\uc678\uc7a5 -dialog.about.systeminfo.exiflib.external.failed=\uc678\uc7a5(\ucc3e\uc9c0\ubabb\ud568) dialog.about.yes=\uc608 dialog.about.no=\uc544\ub2c8\uc624 dialog.about.credits=\uc5d0\uac8c \uac10\uc0ac\ub97c @@ -374,7 +363,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://gpsprune.activityworkshop.net/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 https://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 @@ -425,7 +414,7 @@ dialog.diskcache.dir=\uce90\uc2dc \ub514\ub809\ud1a0\ub9ac dialog.diskcache.createdir=\ub514\ub809\ud1a0\ub9ac \ub9cc\ub4e4\uae30 dialog.diskcache.nocreate=\uce90\uc2dc \ub514\ub809\ud1a0\ub9ac\uac00 \ub9cc\ub4e4\uc5b4\uc9c0\uc9c0 \uc54a\uc558\uc5b4\uc694. dialog.deletefieldvalues.intro=\ud604\uc7ac \ubc94\uc704\uc5d0\uc11c \uc0ad\uc81c\ud560 \ud544\ub4dc\ub97c \uc120\ud0dd -dialog.setlinewidth.text=\ud2b8\ub799\uc744 \uadf8\ub9b4 \uc120\uc758 \ub450\uaed8\ub97c \uc801\uc73c\uc138\uc694(1-4) +dialog.displaysettings.linewidth=\ud2b8\ub799\uc744 \uadf8\ub9b4 \uc120\uc758 \ub450\uaed8\ub97c \uc801\uc73c\uc138\uc694(1-4) dialog.downloadosm.desc=\ud2b9\uc815\uc9c0\uc5ed\uc758 raw OSM \ub370\uc774\ud130\ub97c \ub2e4\uc6b4\ub85c\ub4dc \ud558\uc2dc\uaca0\uc5b4\uc694? dialog.searchwikipedianames.search=\ucc3e\uae30 @@ -653,7 +642,6 @@ error.jpegload.dialogtitle=\uc0ac\uc9c4 \ubd88\ub7ec\uc624\ub294 \uc911 \uc5d0\u error.jpegload.nofilesfound=\ucc3e\uc740 \ud30c\uc77c \uc5c6\uc74c. error.jpegload.nojpegsfound=\ucc3e\uc740 jpeg\ud30c\uc77c \uc5c6\uc74c. error.jpegload.nogpsfound=GPS \uc815\ubcf4\ub97c \ucc3e\uc9c0 \ubabb\ud568. -error.jpegload.exifreadfailed=Exif\uc815\ubcf4 \uc77d\uae30 \uc2e4\ud328./n\ub0b4\uc7a5\uc774\ub098 \uc678\uc7a5 \ub77c\uc774\ube0c\ub7ec\uc774\uac00 \uc5c6\uc73c\uba74/nExif\uc815\ubcf4\ub97c \uc77d\uc744 \uc218 \uc5c6\uc5b4\uc694. error.audioload.nofilesfound=\ucc3e\uc740 \uc18c\ub9ac\ud30c\uc77c \uc5c6\uc74c. error.gpsload.unknown=\uc54c\ub824\uc9c0\uc9c0 \uc54a\uc740 \uc624\ub958. error.undofailed.title=\ub418\ub3cc\ub9ac\uae30 \uc2e4\ud328. diff --git a/tim/prune/lang/prune-texts_nl.properties b/tim/prune/lang/prune-texts_nl.properties index 47b9601..fb5006d 100644 --- a/tim/prune/lang/prune-texts_nl.properties +++ b/tim/prune/lang/prune-texts_nl.properties @@ -1,5 +1,5 @@ # Text entries for the GpsPrune application -# Dutch entries +# Dutch entries thanks to Jeroen # Menu entries menu.file=Bestand @@ -38,9 +38,9 @@ menu.view.browser.yahoo=Yahoo maps menu.view.browser.bing=Bing maps menu.settings=Instellingen menu.settings.onlinemode=Kaarten van internet ophalen -menu.settings.antialias=Gebruik anti-aliasering menu.settings.autosave=Automatisch opslaan bij afsluiten menu.help=Help + # Popup menu for map menu.map.zoomin=Zoom + menu.map.zoomout=Zoom - @@ -82,7 +82,6 @@ function.sendtogps=Gegevens verzenden naar GPS function.exportkml=Export KML function.exportgpx=Export GPX function.exportpov=Export POV -function.exportsvg=Export SVG function.exportimage=Bestand exporteren function.editwaypointname=Hernoem waypoint function.compress=Route comprimeren @@ -110,18 +109,20 @@ function.setpaths=Instellen programmapaden function.selectsegment=Selecteer huidige segment function.splitsegments=Splits route in segmenten function.sewsegments=Voeg segmenten samen +function.createmarkerwaypoints=aak waypoints voor markering 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.searchosmpois=Haal nabije OSM punten op function.searchopencachingde=Doorzoek OpenCaching.de function.mapillary=Zoek naar foto's op Mapillary function.downloadosm=Downloaden OSM data voor gebied function.duplicatepoint=Dupliceer punt function.setcolours=Instellen kleuren -function.setlinewidth=Instellen lijndikte +function.setdisplaysettings=Instellen afbeeldingopties function.setlanguage=Instellen taal function.connecttopoint=Aan punt vastzetten function.disconnectfrompoint=Van punt losmaken @@ -146,6 +147,7 @@ function.diskcache=Kaart opslaan op schijf function.managetilecache=Beheer tegelcache function.getweatherforecast=Ophalen weersvoorspelling function.setaltitudetolerance=Instellen hoogtetolerantie +function.selecttimezone=Instellen tijdzone # Dialogs dialog.exit.confirm.title=GpsPrune afsluiten @@ -264,10 +266,6 @@ dialog.baseimage.zoom=Zoom niveau dialog.baseimage.incomplete=Afbeelding onvolledig dialog.baseimage.tiles=Tegels dialog.baseimage.size=Afbeeldinggrootte -dialog.exportsvg.text=Selecteer de camera hoeken voor SVG export -dialog.exportsvg.phi=Azimut hoek \u03d5 -dialog.exportsvg.theta=Stijgingshoek \u03b8 -dialog.exportsvg.gradients=Gebruik gradaties voor schaduw dialog.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 @@ -387,6 +385,9 @@ dialog.wikipedia.column.name=Artikelnaam dialog.wikipedia.column.distance=Afstand dialog.wikipedia.nonefound=Geen punten gevonden dialog.wikipedia.gallery=Galerie +dialog.osmpois.column.name=Naam +dialog.osmpois.column.type=Type +dialog.osmpois.nonefound=Geen punten gevonden dialog.geocaching.nonefound=Geen punten gevonden dialog.correlate.notimestamps=Er zit geen tijdinformatie in de punten, dus kunnen ze niet aan foto's gekoppeld worden. dialog.correlate.nouncorrelatedphotos=Er zijn geen ongekoppelde foto's.\nWeet u zeker dat u wilt doorgaan? @@ -446,12 +447,12 @@ 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://gpsprune.activityworkshop.net/\nvoor meer informatie en handleidingen. +dialog.help.help=Ga naar\n https://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. dialog.about.summarytext2=Uitgebracht onder de Gnu GPL voor gratis, open, wereldwijd gebruik en verbetering
Kopieren, verspreiding en aanpassing is toegestaan en aangemoedigd
volgens de voorwaarden in het bestand license.txt. -dialog.about.summarytext3=Ga naar http://activityworkshop.net/ voor meer informatie en handleidingen. +dialog.about.summarytext3=Ga naar https://activityworkshop.net/ voor meer informatie en handleidingen. dialog.about.languages=Beschikbare talen dialog.about.translatedby=Nederlandse vertaling door JFO dialog.about.systeminfo=Systeeminformatie @@ -462,11 +463,6 @@ dialog.about.systeminfo.povray=Povray ge\u00efnstalleerd dialog.about.systeminfo.exiftool=Exiftool ge\u00efnstalleer dialog.about.systeminfo.gpsbabel=Gpsbabel ge\u00efnstalleer dialog.about.systeminfo.gnuplot=Gnuplot ge\u00efnstalleer -dialog.about.systeminfo.exiflib=Exif bibliotheek -dialog.about.systeminfo.exiflib.internal=Intern -dialog.about.systeminfo.exiflib.internal.failed=Intern (niet gevonden) -dialog.about.systeminfo.exiflib.external=Extern -dialog.about.systeminfo.exiflib.external.failed=Extern (niet gevonden) dialog.about.yes=Ja dialog.about.no=Nee dialog.about.credits=Credits @@ -485,7 +481,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://gpsprune.activityworkshop.net/download.html. +dialog.checkversion.download=Om de nieuwst versie te downloaden, ga naar https://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 @@ -564,7 +560,17 @@ dialog.diskcache.deleteall=Verwijder alle tegels 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.displaysettings.linewidth=Geef lijndikte voor routes (1-4) +dialog.displaysettings.antialias=Gebruik anti-aliasering +dialog.displaysettings.waypointicons=Waypoint iconen +dialog.displaysettings.wpicon.default=Standaard +dialog.displaysettings.wpicon.ringpt=Rond markeerpunt +dialog.displaysettings.wpicon.plectrum=Plectrum +dialog.displaysettings.wpicon.ring=Ring +dialog.displaysettings.wpicon.pin=Punaise +dialog.displaysettings.size.small=Klein +dialog.displaysettings.size.medium=Middel +dialog.displaysettings.size.large=Groot dialog.downloadosm.desc=Bevestig het downloaden van ruwe OSM data voor dit gebied: dialog.searchwikipedianames.search=Zoeken naar: dialog.weather.location=Locatie @@ -596,11 +602,18 @@ dialog.deletebydate.column.keep=Behouden dialog.deletebydate.column.delete=Verwijderen dialog.setaltitudetolerance.text.metres=Grens (in meters) waaronder kleine klimmen en afdalingen worden genegeerd dialog.setaltitudetolerance.text.feet=Grens (in feet) waaronder kleine klimmen en afdalingen worden genegeerd +dialog.settimezone.intro=Selecteer de tijdzone waarin tijdinformatie van punten wordt afgebeeld +dialog.settimezone.system=Gebruik tijdzone vna het systeem +dialog.settimezone.custom=Gebruik de volgende tijdzone +dialog.settimezone.list.toomany=Te veel keuze +dialog.settimezone.selectedzone=Geselecteerde tijdzone +dialog.settimezone.offsetfromutc=Verschil ten opzichte van UTC dialog.autoplay.duration=Duur (sec.) dialog.autoplay.usetimestamps=Gebruik tijdinfo van punt dialog.autoplay.rewind=Terug naar begin dialog.autoplay.pause=Pauze dialog.autoplay.play=Afspelen + # 3d window dialog.3d.title=GpsPrune in 3D dialog.3d.altitudefactor=Hoogte overdrijvingsfactor @@ -805,13 +818,12 @@ units.degreesfahrenheit.short=\u00baF logic.and=en logic.or=of -# External urls +# External urls and services url.googlemaps=maps.google.nl wikipedia.lang=nl openweathermap.lang=nl webservice.peakfinder=Open Peakfinder.org webservice.geohack=Open Geohack pagina -webservice.panoramio=Open Panoramio kaart # Cardinals for 3d plots cardinal.n=N @@ -869,7 +881,6 @@ error.jpegload.dialogtitle=Fout bij inlezen foto's error.jpegload.nofilesfound=Bestanden niet gevonden error.jpegload.nojpegsfound=Geen jpeg-bestanden gevonden error.jpegload.nogpsfound=Geen GPS informatie gevonden -error.jpegload.exifreadfailed=Kon geen Exif informatie inlezen. Exif informatie kan niet worden gelezen\n zonder interne of externe bibliotheek. error.audioload.nofilesfound=Geen audiobestanden gevonden error.gpsload.unknown=Onbekende fout error.undofailed.title=Terugdraaien mislukt diff --git a/tim/prune/lang/prune-texts_no.properties b/tim/prune/lang/prune-texts_no.properties new file mode 100644 index 0000000..e5a89f6 --- /dev/null +++ b/tim/prune/lang/prune-texts_no.properties @@ -0,0 +1,140 @@ +# Text entries for the GpsPrune application +# Norwegian entries + +# Menu entries +menu.file=Fil +menu.file.addphotos=Legg til bilder +menu.file.recentfiles=Nyeste filer +menu.file.save=Lagre som tekst +menu.file.exit=Avslutt +menu.online= +menu.track= +menu.track.undo=Angre +menu.track.clearundo=Nullstill angreliste +menu.track.markrectangle=Marker punkter i rektangel +function.deletemarked=Slett markerte punkter +menu.range=Intervall +menu.range.all=Velg alle +menu.range.none=Velg ingen +menu.range.start=Velg intervallets startpunkt +menu.range.end=Velg intervallets slutpunkt +menu.range.average=Nytt punkt midt i intervallet +menu.range.reverse=Reverser intervallet +menu.range.mergetracksegments=Sl\u00e5 sammen delintervaller +menu.range.cutandmove=Kutt av og flytt valgt intervall +menu.point=Punkt +menu.point.editpoint=Rediger punkt +menu.point.deletepoint=Slett punkt +menu.photo=Bilde +menu.photo.saveexif=Lagre Exif-data +menu.audio=Lyd +menu.view=Visning +menu.view.showsidebars=Vis sidefelter +menu.view.browser=Vis kart i nettleser +menu.view.browser.google=Google kart +menu.view.browser.openstreetmap=Openstreetmap +menu.view.browser.mapquest=Mapquest +menu.view.browser.yahoo=Yahoo maps +menu.view.browser.bing=Bing maps +menu.settings=Innstillinger +menu.settings.onlinemode=Last kart fra Internet +menu.settings.autosave=Lagre innstillinger automatisk ved avslutning +menu.help=Hjelp +# Popup menu for map +menu.map.zoomin=Zoom inn +menu.map.zoomout=Zoom ut +menu.map.zoomfull=Zoom til hele sporet +menu.map.newpoint=Lag nytt punkt +menu.map.drawpoints=Lag en serie punkter +menu.map.connect=Knyt sporpunkter til linje +menu.map.autopan=Autosentrering +menu.map.showmap=Vis kart +menu.map.showscalebar=Vis m\u00e5lestokk +menu.map.editmode=Redigeringsmodus + +# Alt keys for menus +altkey.menu.file=F +altkey.menu.online= +altkey.menu.track=S +altkey.menu.range=I +altkey.menu.point=P +altkey.menu.view=V +altkey.menu.photo=B +altkey.menu.audio=L +altkey.menu.settings=n +altkey.menu.help=H + +# Ctrl shortcuts for menu items +shortcut.menu.file.open=\u00c5 +shortcut.menu.file.load= +shortcut.menu.file.save=L +shortcut.menu.track.undo=g +shortcut.menu.edit.compress=K +shortcut.menu.range.all=A +shortcut.menu.help.help=H + +# Functions +function.open=\u00c5pne fil +function.importwithgpsbabel=Importer fil med GPSBabel +function.loadfromgps=Hent data fra GPS +function.sendtogps=Overf\u00f8r data til GPS +function.exportkml=Eksporter KML +function.exportgpx=Eksporter GPX +function.exportpov=Eksporter POV +function.exportimage= +function.editwaypointname=Endre waypoint-navn +function.compress=Komprimer spor +function.deleterange=Fjern valgt intervall +function.croptrack=Beskj\u00e6r sporet +function.interpolate=Interpoler punkter +function.deletebydate= +function.addtimeoffset=Legg til tidsinkrement +function.addaltitudeoffset=Legg til h\u00f8ideinkrement +function.rearrangewaypoints= +function.convertnamestotimes=Les waypoint-navn som tidspunkter +function.deletefieldvalues=Slett feltets verdier +function.findwaypoint=Finn waypoint +function.pastecoordinates=Legg til nye koordinater +function.charts=Grafer +function.show3d=3-D visning +function.distances=Avstander +function.fullrangedetails=Vis alle detaljer +function.estimatetime= +function.learnestimationparams= +function.setmapbg=Velg grunnlagskart +function.setpaths=Angi plassering av programmer +function.selectsegment= +function.splitsegments= +function.sewsegments= +function.getgpsies=Vis spor fra gpsies.com +function.uploadgpsies=Last opp spor til gpsies.com +function.lookupsrtm=Hent h\u00f8yde fra SRTM +function.downloadsrtm= +function.getwikipedia=Vis Wikipedia info for omegn +function.searchwikipedianames=S\u00f8k Wikipedia +function.searchopencachingde= +function.downloadosm=Last ned OSM data for omr\u00e5det +function.duplicatepoint=Dupliser punkt +function.setcolours=Velg farger +function.setlanguage=Velg spr\u00e5k +function.connecttopoint=Knytt til punkt +function.disconnectfrompoint=Koble fra punkt +function.removephoto=Fjern foto +function.correlatephotos=Relater alle bilder +function.rearrangephotos=Omarranger bilder +function.rotatephotoleft=Roter bilde mot venstre +function.rotatephotoright=Roter bilde mot h\u00f8yre +function.photopopup=Vis bilde popup +function.ignoreexifthumb=Ignorer exif miniatyrbilder +function.loadaudio=Legg til lydklipp +function.removeaudio=Fjern lydklipp +function.correlateaudios=Relater alle lydklipp +function.playaudio=Spill lydklipp +function.stopaudio=Avbryt lydklipp +function.help=Hjelp +function.showkeys=Vis hurtigtaster +function.about=Om GpsPrune +function.checkversion=Sjekk etter nye versjoner +function.saveconfig=Lagre innstillinger +function.diskcache=Om lagring av kart +function.managetilecache=H\u00e5ndter kart-flis-lager diff --git a/tim/prune/lang/prune-texts_pl.properties b/tim/prune/lang/prune-texts_pl.properties index 405c594..fb73385 100644 --- a/tim/prune/lang/prune-texts_pl.properties +++ b/tim/prune/lang/prune-texts_pl.properties @@ -38,7 +38,7 @@ menu.view.browser.yahoo=Mapy Yahoo menu.view.browser.bing=Mapy Bing menu.settings=Ustawienia menu.settings.onlinemode=\u0141aduj mapy z sieci -menu.settings.antialias=U\u017Cyj antyaliasingu +dialog.displaysettings.antialias=U\u017Cyj antyaliasingu menu.settings.autosave=Autozapis ustawie\u0144 przy wyj\u015bciu menu.help=Pomoc # Popup menu for map @@ -82,7 +82,6 @@ function.sendtogps=Wy\u015blij dane do urz\u0105dzenia GPS function.exportkml=Eksportuj jako KML function.exportgpx=Eksportuj jako GPX function.exportpov=Eksportuj jako POV -function.exportsvg=Eksportuj jako SVG function.exportimage=Eksportuj jako obraz function.editwaypointname=Zmie\u0144 nazw\u0119 punktu po\u015bredniego function.compress=Kompresuj \u015bcie\u017ck\u0119 @@ -104,12 +103,13 @@ function.distances=Odleg\u0142o\u015bci function.fullrangedetails=Wszystkie detale function.estimatetime=Przewidywany czas function.learnestimationparams=Skoryguj wsp\u00f3\u0142czynniki szacowania czasu -function.autoplay=Gra\u0107 \u015bcie\u017ck\u0119 +function.autoplay=Gra\u0107 \u015bcie\u017ck\u0119 function.setmapbg=Wybierz map\u0119 t\u0142a function.setpaths=Ustaw \u015bcie\u017cki do program\u00f3w function.selectsegment=Wybierz bie\u017c\u0105cy fragment function.splitsegments=Podziel \u015bcie\u017ck\u0119 na fragmenty function.sewsegments=Po\u0142\u0105cz fragmenty +function.createmarkerwaypoints=Stw\u00f3rz markery podzia\u0142u function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies function.uploadgpsies=Wy\u015blij \u015bcie\u017cki do Gpsies function.lookupsrtm=Pobierz wysoko\u015bci z SRTM @@ -121,7 +121,6 @@ function.mapillary=Szukaj zdj\u0119cia w Mapillary function.downloadosm=Za\u0142aduj dane obszaru z OSM function.duplicatepoint=Duplikuj plik function.setcolours=Ustaw kolory -function.setlinewidth=Ustaw szeroko\u015b\u0107 linii function.setlanguage=Zmie\u0144 j\u0119zyk function.connecttopoint=Przy\u0142\u0105cz do punktu function.disconnectfrompoint=Od\u0142\u0105cz od punktu @@ -264,10 +263,6 @@ dialog.baseimage.zoom=Poziom zbli\u017cenia dialog.baseimage.incomplete=Obraz niekompletny dialog.baseimage.tiles=Kafelki dialog.baseimage.size=Rozmiar obrazu -dialog.exportsvg.text=Wybierz parametry eksportu do pliku SVG -dialog.exportsvg.phi=azymut \u03d5 -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 @@ -398,8 +393,8 @@ dialog.correlate.select.photolater=P\u00f3\u017aniejsze zdj\u0119cie dialog.correlate.options.intro=Wybierz opcje dla automatycznej korelacji dialog.correlate.options.offsetpanel=Przesuni\u0119cie czasowe dialog.correlate.options.offset=Przesuni\u0119cie -dialog.correlate.options.offset.hours=godzin, -dialog.correlate.options.offset.minutes=minut i +dialog.correlate.options.offset.hours=godziny, +dialog.correlate.options.offset.minutes=minuty i dialog.correlate.options.offset.seconds=sekund dialog.correlate.options.photolater=Zdj\u0119cie p\u00f3\u017aniejsze ni\u017c punkt dialog.correlate.options.pointlaterphoto=Punkt p\u00f3\u017aniejszy ni\u017c zdj\u0119cie @@ -446,12 +441,12 @@ dialog.deletemarked.nonefound=Nie mo\u017cna usun\u0105\u0107 \u017cadnych punkt dialog.pastecoordinates.desc=Wprowad\u017a lub wklej wsp\u00f3\u0142rz\u0119dne dialog.pastecoordinates.coords=Wsp\u00f3\u0142rz\u0119dne dialog.pastecoordinates.nothingfound=Sprawd\u017a wsp\u00f3\u0142rz\u0119dne i spr\u00f3buj jeszcze raz -dialog.help.help=Na stronie\nhttp://gpsprune.activityworkshop.net/\nznajdziesz wi\u0119cej informacji i porad\noraz mo\u017cliwo\u015b\u0107 kupna nowego podr\u0119cznika u\u017cytkownika w formacie PDF. +dialog.help.help=Na stronie\nhttp://gpsprune.activityworkshop.net/\nznajdziesz wi\u0119cej informacji i porad\noraz mo\u017cliwo\u015b\u0107 kupna podr\u0119cznika u\u017cytkownika w formacie PDF. dialog.about.version=Wersja dialog.about.build=Build dialog.about.summarytext1=GpsPrune s\u0142u\u017cy do pobierania, wy\u015bwietlania i edycji danych z odbiornik\u00f3w GPS. dialog.about.summarytext2=Ten program zosta\u0142 udost\u0119pniony na podstawie licencji GNU pozwalaj\u0105cej
na jego wolne, nieograniczone i og\u00f3lno\u015bwiatowe u\u017cytkowanie i rozszerzanie.
Kopiowanie, rozprowadzanie i modyfikowanie s\u0105 dozwolone i zalecane
zgodnie z warunkami zawartymi w do\u0142\u0105czonym plikulicense.txt -dialog.about.summarytext3=Na stronie http://activityworkshop.net/ znajdziesz wi\u0119cej informacji i porad\noraz mo\u017cliwo\u015b\u0107 kupna nowego podr\u0119cznika u\u017cytkownika w formacie PDF. +dialog.about.summarytext3=Na stronie https://activityworkshop.net/ znajdziesz wi\u0119cej informacji i porad
oraz mo\u017cliwo\u015b\u0107 kupna podr\u0119cznika u\u017cytkownika w formacie PDF. dialog.about.languages=Dost\u0119pne j\u0119zyki dialog.about.translatedby=Tekst po polsku: Piotr, Weehal dialog.about.systeminfo=Informacje o systemie @@ -462,11 +457,6 @@ dialog.about.systeminfo.povray=Povray zainstalowany dialog.about.systeminfo.exiftool=Exiftool zainstalowany dialog.about.systeminfo.gpsbabel=Gpsbabel zainstalowany dialog.about.systeminfo.gnuplot=Gnuplot zainstalowany -dialog.about.systeminfo.exiflib=Biblioteka Exif -dialog.about.systeminfo.exiflib.internal=Wewn\u0119trzny -dialog.about.systeminfo.exiflib.internal.failed=Wewn\u0119trzny (nie znaleziony) -dialog.about.systeminfo.exiflib.external=Zewn\u0119trzny -dialog.about.systeminfo.exiflib.external.failed=Zewn\u0119trzny (nie znaleziony) dialog.about.yes=Tak dialog.about.no=Nie dialog.about.credits=Podzi\u0119kowania @@ -564,7 +554,7 @@ dialog.diskcache.deleteall=Usu\u0144 wszystkie p\u0142ytki 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.displaysettings.linewidth=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM: dialog.searchwikipedianames.search=Szukaj dialog.weather.location=Pozycja @@ -597,7 +587,7 @@ dialog.deletebydate.column.delete=Usu\u0144 dialog.setaltitudetolerance.text.metres=Limit (w metrach) poni\u017cej kt\u00f3rego, ma\u0142e spadki wzniosy b\u0119d\u0105 ignorowane dialog.setaltitudetolerance.text.feet=Limit (w stopach) poni\u017cej kt\u00f3rego, ma\u0142e spadki wzniosy b\u0119d\u0105 ignorowane dialog.autoplay.duration=Czas trwania (sek) -dialog.autoplay.usetimestamps=U\u017Cyj znaczników czasowych +dialog.autoplay.usetimestamps=U\u017Cyj znacznik\u00f3w czasowych dialog.autoplay.rewind=Przewin\u0105\u0107 dialog.autoplay.pause=Pauza dialog.autoplay.play=Graj @@ -812,7 +802,6 @@ wikipedia.lang=pl openweathermap.lang=pl webservice.peakfinder=Otw\u00f3rz w Peakfinder.org webservice.geohack=Otw\u00f3rz w Geohack -webservice.panoramio=Otw\u00f3rz w Panoramio.com # Cardinals for 3d plots cardinal.n=N @@ -870,7 +859,6 @@ error.jpegload.dialogtitle=B\u0142\u0105d \u0142adowania zdj\u0119cia error.jpegload.nofilesfound=Nie znaleziono plik\u00f3w error.jpegload.nojpegsfound=Nie znaleziono plik\u00f3w jpeg error.jpegload.nogpsfound=Nie znaleziono informacji GPS -error.jpegload.exifreadfailed=Nie powiod\u0142o si\u0119 odczytanie informacji Exif\nInformacji tych nie mo\u017cna przeczyta\u0107 bez wewn\u0119trznej lub zewn\u0119trznej biblioteki. error.audioload.nofilesfound=Nie znaleziono plik\u00f3w audio error.gpsload.unknown=Nieznany b\u0142\u0105d error.undofailed.title=Cofnij nie powiod\u0142o si\u0119 diff --git a/tim/prune/lang/prune-texts_pt.properties b/tim/prune/lang/prune-texts_pt.properties index a3875d6..f12eaf6 100644 --- a/tim/prune/lang/prune-texts_pt.properties +++ b/tim/prune/lang/prune-texts_pt.properties @@ -82,7 +82,6 @@ function.sendtogps=Enviar dados para o GPS 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 @@ -118,7 +117,6 @@ function.mapillary=Procurar na Mapillary.com function.downloadosm=Baixar dados OSM para a \u00e1rea function.duplicatepoint=Duplicar ponto function.setcolours=Definir cores -function.setlinewidth=Definir espessura da linha function.setlanguage=Definir idioma function.connecttopoint=Conectar ao ponto function.disconnectfrompoint=Desconectar do ponto @@ -260,10 +258,6 @@ 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 @@ -378,8 +372,10 @@ dialog.gpsies.activity.motorbiking=Motocross dialog.gpsies.activity.snowshoe=Snowshoeing dialog.gpsies.activity.sailing=Sailing dialog.gpsies.activity.skating=Patina\u00e7\u00e3o +dialog.mapillary.nonefound=Nenhuma foto encontrada dialog.wikipedia.column.name=Nome do artigo dialog.wikipedia.column.distance=Dist\u00e2ncia +dialog.wikipedia.nonefound=Nenhum artigo encontrado 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 de que deseja continuar? dialog.correlate.nouncorrelatedaudios=Existem \u00e1udios n\u00e3o correlacionados.\nVoc\u00ea tem certeza de que deseja continuar? @@ -453,11 +449,6 @@ dialog.about.systeminfo.povray=Povray instalado dialog.about.systeminfo.exiftool=Exittool instalado dialog.about.systeminfo.gpsbabel=Gpsbabel instalado dialog.about.systeminfo.gnuplot=Gnuplot instalado -dialog.about.systeminfo.exiflib=Biblioteca do Exif -dialog.about.systeminfo.exiflib.internal=Interna -dialog.about.systeminfo.exiflib.internal.failed=Interna (n\u00e3o encontrada) -dialog.about.systeminfo.exiflib.external=Externa -dialog.about.systeminfo.exiflib.external.failed=Externa (n\u00e3o encontrada) dialog.about.yes=Sim dialog.about.no=N\u00e3o dialog.about.credits=Cr\u00e9ditos @@ -542,7 +533,7 @@ dialog.diskcache.deleteall=Apagar todos os fundos 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.displaysettings.linewidth=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 @@ -785,7 +776,6 @@ wikipedia.lang=pt openweathermap.lang=pt webservice.peakfinder=Abrir Peakfinder.org webservice.geohack=Abrir p\u00e1gina Geohack -webservice.panoramio=Abrir mapa do Panoramio # Cardinals for 3d plots cardinal.n=N @@ -843,7 +833,6 @@ error.jpegload.dialogtitle=Erro ao carregar fotos error.jpegload.nofilesfound=Nenhum arquivo encontrado error.jpegload.nojpegsfound=Nenhum arquivo jpeg encontrado error.jpegload.nogpsfound=Nenhuma informa\u00e7\u00e3o de GPS encontrada -error.jpegload.exifreadfailed=Falha ao ler informa\u00e7\u00f5es do Exif. Nenhuma informa\u00e7\u00e3o do Exif pode ser lida\nseja na biblioteca interna, seja na externa. error.audioload.nofilesfound=Nenhum arquivo de \u00e1udio encontrado error.gpsload.unknown=Erro desconhecido error.undofailed.title=Falha ao desfazer diff --git a/tim/prune/lang/prune-texts_ro.properties b/tim/prune/lang/prune-texts_ro.properties index 46e0c58..b920ed2 100644 --- a/tim/prune/lang/prune-texts_ro.properties +++ b/tim/prune/lang/prune-texts_ro.properties @@ -38,7 +38,7 @@ menu.view.browser.yahoo=Harta Yahoo menu.view.browser.bing=Harta Bing menu.settings=Set\u0103ri menu.settings.onlinemode=\u00cencarc\u0103 h\u0103r\u021bi -menu.settings.antialias=Folose\u0219te antialiasing +dialog.displaysettings.antialias=Folose\u0219te antialiasing menu.settings.autosave=Salveaz\u0103 set\u0103rile automat la ie\u0219ire menu.help=Ajutor @@ -83,7 +83,6 @@ function.sendtogps=Trimite date spre GPS function.exportkml=Export\u0103 \u00een fi\u015fier KML function.exportgpx=Export\u0103 \u00een fi\u015fier GPX function.exportpov=Export\u0103 \u00een fi\u015fier POV -function.exportsvg=Export\u0103 \u00een fi\u015fier SVG function.exportimage=Export\u0103 imagine function.editwaypointname=Editeaz\u0103 nume waypoint function.compress=Comprim\u0103 traseu @@ -122,7 +121,6 @@ function.mapillary=Caut\u0103 fotografii \u00een Mapillary function.downloadosm=Descarc\u0103 date OSM pentru zona traseului function.duplicatepoint=Duplic\u0103 punctul function.setcolours=Seteaz\u0103 culori -function.setlinewidth=Seteaz\u0103 grosime linie function.setlanguage=Seteaz\u0103 limb\u0103 function.connecttopoint=Conecteaz\u0103 la punct function.disconnectfrompoint=Deconecteaz\u0103 de la punct @@ -265,10 +263,6 @@ dialog.baseimage.zoom=Nivel de zoom dialog.baseimage.incomplete=Imagine incomplet\u0103 dialog.baseimage.tiles=\u021aigle dialog.baseimage.size=Dimensiunea imaginii -dialog.exportsvg.text=Alege\u021bi parametrii pentru export SVG -dialog.exportsvg.phi=Azimut \u03d5 -dialog.exportsvg.theta=\u00cenclina\u0163ie \u03b8 -dialog.exportsvg.gradients=Folose\u0219te gradien\u021bi pentru umbrire dialog.exportimage.noimagepossible=Imaginile hart\u0103 trebuie sa fie salvate \u00een cache pe disc pentru a putea fi folosite la export. dialog.exportimage.drawtrack=Deseneaz\u0103 traseu pe hart\u0103 dialog.exportimage.drawtrackpoints=Deseneaz\u0103 punctele traseului @@ -463,11 +457,6 @@ dialog.about.systeminfo.povray=Povray instalat dialog.about.systeminfo.exiftool=Exiftool instalat dialog.about.systeminfo.gpsbabel=Gpsbabel instalat dialog.about.systeminfo.gnuplot=Gnuplot instalat -dialog.about.systeminfo.exiflib=Bibliotec\u0103 Exif -dialog.about.systeminfo.exiflib.internal=Intern -dialog.about.systeminfo.exiflib.internal.failed=Intern (absent) -dialog.about.systeminfo.exiflib.external=Extern -dialog.about.systeminfo.exiflib.external.failed=Extern (absent) dialog.about.yes=Da dialog.about.no=Nu dialog.about.credits=Mul\u021bumiri @@ -565,7 +554,7 @@ dialog.diskcache.deleteall=\u015eterge toate imaginile dialog.diskcache.deleted=Au fost \u0219terse %d fi\u0219iere din cache dialog.deletefieldvalues.intro=Alege\u021bi c\u00e2mpurile ce urmeaz\u0103 a fi \u0219terse din intervalul curent dialog.deletefieldvalues.nofields=Nu poate fi \u0219ters nici un c\u00e2mp pentru intervalul dat -dialog.setlinewidth.text=Introduce\u021bi grosimea liniilor ce vor fi desenate pentru trasee (1-3) +dialog.displaysettings.linewidth=Introduce\u021bi grosimea liniilor ce vor fi desenate pentru trasee (1-3) dialog.downloadosm.desc=Confirma\u021bi desc\u0103rcarea datelor brute OSM pentru zona specificat\u0103: dialog.searchwikipedianames.search=Caut\u0103: dialog.weather.location=Loca\u0163ie @@ -813,7 +802,6 @@ wikipedia.lang=ro openweathermap.lang=ro webservice.peakfinder=Deschide Peakfinder.org webservice.geohack=Deschide pagina Geohack -webservice.panoramio=Deschide harta Panoramio # Cardinals for 3d plots cardinal.n=N @@ -871,7 +859,6 @@ error.jpegload.dialogtitle=Eroare la \u00eenc\u0103rcarea fotografiilor error.jpegload.nofilesfound=Nu a fost g\u0103sit niciun fi\u0219ier error.jpegload.nojpegsfound=Nu a fost g\u0103sit niciun fi\u0219ier jpeg error.jpegload.nogpsfound=\u00cen EXIF-ul fi\u0219ierelor JPEG nu a fost g\u0103sit\u0103 informa\u021bie GPS -error.jpegload.exifreadfailed=Citirea informa\u021biei EXIF a e\u0219uat. Informa\u021bia EXIF nu poate fi citit\u0103 far\u0103 o bibliotec\u0103 extern\u0103 sau intern\u0103. error.audioload.nofilesfound=Nu a fost g\u0103sit niciun clip audio. error.gpsload.unknown=Eroare cu cauz\u0103 necunoscut\u0103 error.undofailed.title=Anularea a e\u0219uat diff --git a/tim/prune/lang/prune-texts_ru.properties b/tim/prune/lang/prune-texts_ru.properties index 6e6e8d7..e72acfa 100644 --- a/tim/prune/lang/prune-texts_ru.properties +++ b/tim/prune/lang/prune-texts_ru.properties @@ -1,17 +1,17 @@ # Text entries for the GpsPrune application -# Russian entries thanks to Sergey, Roman +# Russian entries thanks to Sergey, Roman, Valerij # Menu entries menu.file=\u0424\u0430\u0439\u043b menu.file.addphotos=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0444\u043e\u0442\u043e -menu.file.recentfiles=\u041f\u0440\u0438\u043d\u044f\u0442\u044b\u0435 \u0444\u0430\u0439\u043b\u044b +menu.file.recentfiles=\u041d\u0435\u0434\u0430\u0432\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u044b menu.file.save=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u043a \u0442\u0435\u043a\u0441\u0442 -menu.file.exit=\u0412\u044b\u0445\u043e\u0434 +menu.file.exit=\u0412\u044b\u0439\u0442\u0438 menu.online=\u041e\u043d\u043b\u0430\u0439\u043d menu.track=\u0422\u0440\u0435\u043a menu.track.undo=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 -menu.track.markrectangle=\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 \u0432 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0435 +menu.track.markrectangle=\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 \u0432 \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0435 function.deletemarked=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438 menu.range=\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b menu.range.all=\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435 @@ -20,7 +20,7 @@ menu.range.start=\u041d\u0430\u0447\u0430\u043b\u043e \u0438\u043d\u0442\u0435\u menu.range.end=\u041a\u043e\u043d\u0435\u0446 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 menu.range.average=\u0422\u043e\u0447\u043a\u0430 \u043f\u043e \u0441\u0440\u0435\u0434\u043d\u0435\u043c\u0443 menu.range.reverse=\u041f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b -menu.range.mergetracksegments=\u0421\u043b\u0438\u0442\u044c \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u044b \u0442\u0440\u0435\u043a\u0430 +menu.range.mergetracksegments=\u0421\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u044b \u0442\u0440\u0435\u043a\u0430 menu.range.cutandmove=\u0412\u044b\u0440\u0435\u0437\u0430\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u044b\u0431\u043e\u0440\u043a\u0443 menu.point=\u0422\u043e\u0447\u043a\u0430 menu.point.editpoint=\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u0447\u043a\u0443 @@ -36,19 +36,19 @@ menu.view.browser.openstreetmap=Openstreetmap menu.view.browser.mapquest=Mapquest menu.view.browser.yahoo=Yahoo maps menu.view.browser.bing=Bing maps -menu.settings=\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 +menu.settings=\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 menu.settings.onlinemode=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0443 \u0438\u0437 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430 -menu.settings.antialias=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0433\u043b\u0430\u0436\u0438\u0432\u0430\u043d\u0438\u0435 menu.settings.autosave=\u0410\u0432\u0442\u043e\u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435 menu.help=\u041f\u043e\u043c\u043e\u0449\u044c + # Popup menu for map menu.map.zoomin=\u0423\u0432\u0435\u043b\u0438\u0447\u0438\u0442\u044c menu.map.zoomout=\u0423\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c menu.map.zoomfull=\u0423\u0432\u0435\u043b\u0447\u0438\u0442\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e -menu.map.newpoint=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043e\u0434\u043d\u0443 \u0442\u043e\u0447\u043a\u0443 +menu.map.newpoint=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0442\u043e\u0447\u043a\u0443 menu.map.drawpoints=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u043e\u0447\u0435\u043a menu.map.connect=\u0421\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 \u0442\u0440\u0435\u043a\u0430 \u0441 \u043b\u0438\u043d\u0438\u0435\u0439 -menu.map.autopan=\u041e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 +menu.map.autopan=\u0410\u0432\u0442\u043e\u0444\u043e\u043a\u0443\u0441 \u043d\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 menu.map.showmap=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u041e\u0421\u041c-\u043a\u0430\u0440\u0442\u0443 menu.map.showscalebar=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0448\u043a\u0430\u043b\u0443 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0430 menu.map.editmode=\u0420\u0435\u0436\u0438\u043c \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f @@ -82,19 +82,18 @@ function.sendtogps=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0432 function.exportkml=\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 KML function.exportgpx=\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 GPX function.exportpov=\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 POV -function.exportsvg=\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 SVG function.exportimage=\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u0301\u043d\u0438\u0435 function.editwaypointname=\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u044f \u043f\u0443\u0442\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 function.compress=\u0421\u0436\u0430\u0442\u044c \u0442\u0440\u0435\u043a -function.marklifts=\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043f\u043e\u0434\u044a\u0451\u043c\u0438\u043d\u0438\u043a \u0432 \u0433\u043e\u0440\u0443 +function.marklifts=\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043f\u043e\u0434\u044a\u0451\u043c\u043d\u0438\u043a\u0438 \u0432 \u0433\u043e\u0440\u0443 function.deleterange=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b function.croptrack=\u041e\u0431\u0440\u0435\u0437\u0430\u0442\u044c \u0442\u0440\u0435\u043a -function.interpolate=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u0447\u0435\u043a\u0438 \u0432 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b +function.interpolate=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 \u0432 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b function.deletebydate=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 \u043f\u043e \u0434\u0430\u0442\u0435 -function.addtimeoffset=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0442\u043a\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 -function.addaltitudeoffset=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0442\u043a\u0443 \u0432\u044b\u0441\u043e\u0442\u044b -function.rearrangewaypoints=\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u044b -function.convertnamestotimes=\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u044f \u043f\u0443\u0442\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u0432\u043e \u0432\u0440\u0435\u043c\u044f +function.addtimeoffset=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 +function.addaltitudeoffset=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b +function.rearrangewaypoints=\u041f\u0435\u0440\u0435\u0443\u043f\u043e\u0440\u044f\u0434\u043e\u0447\u0438\u0442\u044c \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0442\u043e\u0447\u0435\u043a \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 +function.convertnamestotimes=\u041a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u044f \u043f\u0443\u0442\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u0432 \u043c\u0435\u0442\u043a\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 function.deletefieldvalues=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f function.findwaypoint=\u041d\u0430\u0439\u0442\u0438 \u043f\u0443\u0442\u0435\u0432\u0443\u044e \u0442\u043e\u0447\u043a\u0443 function.pastecoordinates=\u0412\u0432\u043e\u0434 \u043d\u043e\u0432\u044b\u0445 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 @@ -102,32 +101,34 @@ function.charts=\u0413\u0440\u0430\u0444\u0438\u043a\u0438 function.show3d=3D-\u0432\u0438\u0434 function.distances=\u0420\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u044f function.fullrangedetails=\u0414\u0435\u0442\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0443 -function.estimatetime=\u041f\u0440\u043e\u0433\u043d\u043e\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u044f +function.estimatetime=\u0421\u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u044f function.learnestimationparams=\u0417\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 -function.autoplay=\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 +function.autoplay=\u0410\u0432\u0442\u043e\u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0440\u0435\u043a\u0430 function.setmapbg=\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u043a\u0430\u0440\u0442\u0443-\u043f\u043e\u0434\u043b\u043e\u0436\u043a\u0443 function.setpaths=\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0443\u0442\u0438 \u043a \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430\u043c function.selectsegment=\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0441\u0435\u0433\u043c\u0435\u043d\u0442 function.splitsegments=\u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u0440\u0435\u043a \u043d\u0430 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u044b function.sewsegments=\u0421\u043a\u043b\u0435\u0438\u0442\u044c \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u044b \u0442\u0440\u0435\u043a\u0430 \u0432\u043e\u0435\u0434\u0438\u043d\u043e +function.createmarkerwaypoints=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0430\u0440\u043a\u0435\u0440\u044b \u043f\u0443\u0442\u0435\u0432\u044b\u0445 \u0442\u043e\u0447\u0435\u043a function.getgpsies=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0442\u0440\u0435\u043a\u0438 function.uploadgpsies=\u0412\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0442\u0440\u0435\u043a \u043d\u0430 gpsies.com -function.lookupsrtm=\u0412\u044b\u0441\u043e\u0442\u044b \u0432 SRTM -function.downloadsrtm=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c STRM +function.lookupsrtm=\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u044b\u0441\u043e\u0442\u0443 \u0438\u0437 SRTM +function.downloadsrtm=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c SRTM-\u0442\u0430\u0439\u043b\u044b function.getwikipedia=\u0421\u0442\u0430\u0442\u044c\u044f \u043e \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0432 \u0412\u0438\u043a\u0438 function.searchwikipedianames=\u041f\u043e\u0438\u0441\u043a \u0441\u0442\u0430\u0442\u0435\u0439 \u0432 \u0412\u0438\u043a\u0438 \u043f\u043e \u0438\u043c\u0435\u043d\u0438 -function.searchopencachingde=\u041f\u043e\u0438\u0441\u043a OpenCaching.de +function.searchosmpois=\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0435 \u0442\u043e\u0447\u043a\u0438 OSM +function.searchopencachingde=\u041f\u043e\u0438\u0441\u043a \u0442\u0430\u0439\u043d\u0438\u043a\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 OpenCaching.de function.mapillary=\u041f\u043e\u0438\u0441\u043a \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0445 \u0444\u043e\u0442\u043e -function.downloadosm=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c OSM \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u0442\u0435\u0440\u0440\u0438\u0442\u043e\u0440\u0438\u044e +function.downloadosm=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c OSM \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u043e\u0431\u043b\u0430\u0441\u0442\u044c function.duplicatepoint=\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u0447\u043a\u0443 \u0432 \u043a\u043e\u043d\u0435\u0446 \u0442\u0440\u0435\u043a\u0430 function.setcolours=\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0446\u0432\u0435\u0442\u0430 -function.setlinewidth=\u0417\u0430\u0434\u0430\u0442\u044c \u0448\u0438\u0440\u0438\u043d\u0443 \u043b\u0438\u043d\u0438\u0438 +function.setdisplaysettings=\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f function.setlanguage=\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u044f\u0437\u044b\u043a function.connecttopoint=\u041f\u0440\u0438\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u043a \u0442\u043e\u0447\u043a\u0435 function.disconnectfrompoint=\u041e\u0442\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u043e\u0442 \u0442\u043e\u0447\u043a\u0438 function.removephoto=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0444\u043e\u0442\u043e -function.correlatephotos=\u0421\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0444\u043e\u0442\u043e \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 -function.rearrangephotos=\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0444\u043e\u0442\u043e \u043f\u043e \u0442\u0440\u0435\u043a\u0443 +function.correlatephotos=\u0421\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0444\u043e\u0442\u043e \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 +function.rearrangephotos=\u041f\u0435\u0440\u0435\u0443\u043f\u043e\u0440\u044f\u0434\u043e\u0447\u0438\u0442\u044c \u0444\u043e\u0442\u043e \u043f\u043e \u0442\u0440\u0435\u043a\u0443 function.rotatephotoleft=\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0444\u043e\u0442\u043e \u043d\u0430 90 \u0432\u043b\u0435\u0432\u043e function.rotatephotoright=\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0444\u043e\u0442\u043e \u043d\u0430 90 \u0432\u043f\u0440\u0430\u0432\u043e function.photopopup=\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0444\u043e\u0442\u043e \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043e\u043a\u043d\u0435 @@ -141,19 +142,20 @@ function.help=\u041f\u043e\u043c\u043e\u0449\u044c function.showkeys=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0433\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 function.about=\u041e GpsPrune function.checkversion=\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f -function.saveconfig=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 +function.saveconfig=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 function.diskcache=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u044b \u043d\u0430 \u0434\u0438\u0441\u043a -function.managetilecache=\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0435\u0448\u0435\u043c +function.managetilecache=\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u044d\u0448\u0435\u043c function.getweatherforecast=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b -function.setaltitudetolerance=\u0417\u0430\u0434\u0430\u0442\u044c \u043e\u0433\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b +function.setaltitudetolerance=\u0417\u0430\u0434\u0430\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b +function.selecttimezone=\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u044f\u0441 # Dialogs -dialog.exit.confirm.title=\u0412\u044b\u0445\u043e\u0434 +dialog.exit.confirm.title=\u0412\u044b\u0439\u0442\u0438 dialog.exit.confirm.text=\u0414\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b! \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c? dialog.openappend.title=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u043b\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0445. dialog.openappend.text=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a \u0442\u0435\u043a\u0443\u0449\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u043c? dialog.deletepoint.title=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0443 -dialog.deletepoint.deletephoto=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0444\u043e\u0442\u043e \u0443 \u044d\u0442\u043e\u0439 \u0442\u043e\u0447\u043a\u0438? +dialog.deletepoint.deletephoto=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0444\u043e\u0442\u043e \u0441 \u044d\u0442\u043e\u0439 \u0442\u043e\u0447\u043a\u0438? dialog.deletephoto.title=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0444\u043e\u0442\u043e dialog.deletephoto.deletepoint=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0443 \u0443 \u044d\u0442\u043e\u0433\u043e \u0444\u043e\u0442\u043e? dialog.deleteaudio.deletepoint=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0443 \u0443 \u044d\u0442\u043e\u0433\u043e \u0430\u0443\u0434\u0438\u043e \u0444\u0430\u0439\u043b\u0430? @@ -174,6 +176,9 @@ dialog.openoptions.deliminfo.norecords=\u041d\u0435\u0442 \u0437\u0430\u043f\u04 dialog.openoptions.altitudeunits=\u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0432\u044b\u0441\u043e\u0442\u044b dialog.openoptions.speedunits=\u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 dialog.openoptions.vertspeedunits=\u0415\u0434\u0438\u043d\u0438\u0446\u044b +dialog.openoptions.vspeed.intro=\u041f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438: +dialog.openoptions.vspeed.positiveup=\u041f\u043e\u0434\u044a\u0451\u043c\u0430 +dialog.openoptions.vspeed.positivedown=\u0421\u043d\u0438\u0436\u0435\u043d\u0438\u044f dialog.open.contentsdoubled=\u042d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u043a\u0430\u0436\u0434\u043e\u0439 \u0442\u043e\u0447\u043a\u0435,\n\u043e\u0434\u043d\u0430 \u043a\u0430\u043a \u043f\u0443\u0442\u0435\u0432\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0438 \u043e\u0434\u043d\u0430 \u043a\u0430\u043a \u0442\u043e\u0447\u043a\u0430 \u0442\u0440\u0435\u043a\u0430 dialog.selecttracks.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0440\u0435\u043a(-\u0438) \u0434\u043b\u044f \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f dialog.selecttracks.noname=\u0411\u0435\u0437\u044b\u043c\u044f\u043d\u043d\u044b\u0439 @@ -181,7 +186,7 @@ dialog.jpegload.subdirectories=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c dialog.jpegload.loadjpegswithoutcoords=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0444\u043e\u0442\u043e \u0431\u0435\u0437 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 dialog.jpegload.loadjpegsoutsidearea=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0444\u043e\u0442\u043e \u0437\u0430 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u043c\u0438 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 dialog.jpegload.progress.title=\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0444\u043e\u0442\u043e -dialog.jpegload.progress=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u043f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u0438\u0434\u0435\u0442 \u043f\u043e\u0438\u0441\u043a \u0444\u043e\u0442\u043e +dialog.jpegload.progress=\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u0438\u0434\u0435\u0442 \u043f\u043e\u0438\u0441\u043a \u0444\u043e\u0442\u043e dialog.gpsload.nogpsbabel=Gpsbabel \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d. \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c? dialog.gpsload.device=\u0418\u043c\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 dialog.gpsload.format=\u0424\u043e\u0440\u043c\u0430\u0442 @@ -193,10 +198,28 @@ dialog.gpssend.sendtracks=\u041f\u043e\u0441\u043b\u0430\u0442\u044c \u0442\u044 dialog.gpssend.trackname=\u0418\u043c\u044f \u0442\u0440\u0435\u043a\u0430 dialog.gpsbabel.filters=\u0424\u0438\u043b\u044c\u0442\u0440\u044b dialog.addfilter.title=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0444\u0438\u043b\u044c\u0442\u0440 +dialog.gpsbabel.filter.discard=\u041e\u0442\u0431\u0440\u043e\u0441\u0438\u0442\u044c dialog.gpsbabel.filter.simplify=\u0421\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u044c dialog.gpsbabel.filter.distance=\u0420\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 dialog.gpsbabel.filter.interpolate=\u0418\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u044f +dialog.gpsbabel.filter.discard.intro=\u041e\u0442\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438, \u0435\u0441\u043b\u0438 +dialog.gpsbabel.filter.discard.hdop=\u0423\u0445\u0443\u0434\u0448\u0435\u043d\u0438\u0435 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0438 > +dialog.gpsbabel.filter.discard.vdop=\u0423\u0445\u0443\u0434\u0448\u0435\u043d\u0438\u0435 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0438 > dialog.gpsbabel.filter.discard.numsats=\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u043f\u0443\u0442\u043d\u0438\u043a\u043e\u0432 < +dialog.gpsbabel.filter.discard.nofix=\u0422\u043e\u0447\u043a\u0430 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0439 +dialog.gpsbabel.filter.discard.unknownfix=\u0422\u043e\u0447\u043a\u0430 \u0438\u043c\u0435\u0435\u0442 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e\u0435 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 +dialog.gpsbabel.filter.simplify.intro=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 \u0434\u043e +dialog.gpsbabel.filter.simplify.maxpoints=\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0447\u0435\u043a < +dialog.gpsbabel.filter.simplify.maxerror=\u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u044f < +dialog.gpsbabel.filter.simplify.crosstrack=\u043f\u0435\u0440\u0435\u043a\u0440\u0435\u0441\u0442\u043e\u043a +dialog.gpsbabel.filter.simplify.length=\u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u0432 \u0434\u043b\u0438\u043d\u0435 +dialog.gpsbabel.filter.simplify.relative=\u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e hdop +dialog.gpsbabel.filter.distance.intro=\u0423\u0434\u0430\u043b\u044f\u0442\u044c \u0442\u043e\u0447\u043a\u0438, \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u0431\u043b\u0438\u0437\u043a\u0438 \u043a \u043b\u044e\u0431\u043e\u0439 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0442\u043e\u0447\u043a\u0435 +dialog.gpsbabel.filter.distance.distance=\u0415\u0441\u043b\u0438 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 < +dialog.gpsbabel.filter.distance.time=\u0438 \u0432\u0440\u0435\u043c\u044f \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f < +dialog.gpsbabel.filter.interpolate.intro=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438 \u043c\u0435\u0436\u0434\u0443 \u0442\u043e\u0447\u043a\u0430\u043c\u0438 \u0442\u0440\u0435\u043a\u0430 +dialog.gpsbabel.filter.interpolate.distance=\u0415\u0441\u043b\u0438 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 > +dialog.gpsbabel.filter.interpolate.time=\u0438\u043b\u0438 \u0432\u0440\u0435\u043c\u044f \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f > dialog.saveoptions.title=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0444\u0430\u0439\u043b dialog.save.fieldstosave=\u041f\u043e\u043b\u044f \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 dialog.save.table.field=\u041f\u043e\u043b\u0435 @@ -207,7 +230,7 @@ dialog.save.coordinateunits=\u0415\u0434\u0438\u043d\u0438\u0446\u044b \u043a\u0 dialog.save.altitudeunits=\u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0432\u044b\u0441\u043e\u0442\u044b dialog.save.timestampformat=\u0424\u043e\u0440\u043c\u0430\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 dialog.save.overwrite.title=\u0424\u0430\u0439\u043b \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 -dialog.save.overwrite.text=\u0424\u0430\u0439\u043b \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442. \u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c? +dialog.save.overwrite.text=\u0424\u0430\u0439\u043b \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.\u0425\u043e\u0442\u0438\u0442\u0435 \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c? dialog.save.notypesselected=\u041d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d \u0442\u0438\u043f \u0442\u043e\u0447\u0435\u043a dialog.exportkml.text=\u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043a \u0434\u0430\u043d\u043d\u044b\u043c dialog.exportkml.altitude=\u0410\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u044b\u0435 \u0432\u044b\u0441\u043e\u0442\u044b (\u0434\u043b\u044f \u0430\u0432\u0438\u0430\u0446\u0438\u0438) @@ -215,9 +238,11 @@ dialog.exportkml.kmz=\u0421\u0436\u0430\u0442\u0438\u0435 \u0434\u043b\u044f kmz dialog.exportkml.exportimages=\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u044d\u0441\u043a\u0438\u0437\u0430 \u0432 kmz dialog.exportkml.imagesize=\u0420\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f dialog.exportkml.trackcolour=\u0426\u0432\u0435\u0442 \u0442\u0440\u0435\u043a\u0430 +dialog.exportkml.standardkml=\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 KML +dialog.exportkml.extendedkml=\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0439 KML \u0441 \u043c\u0435\u0442\u043a\u0430\u043c\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 dialog.exportgpx.name=\u0418\u043c\u044f dialog.exportgpx.desc=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 -dialog.exportgpx.includetimestamps=\u0412\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043e\u0442\u043c\u0435\u0442\u043a\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 +dialog.exportgpx.includetimestamps=\u0412\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043c\u0435\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 dialog.exportgpx.copysource=\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a xml dialog.exportgpx.encoding=\u041a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430 dialog.exportgpx.encoding.system=\u0421\u0438\u0441\u0442\u0435\u043c\u043d\u0430\u044f @@ -233,7 +258,7 @@ dialog.exportpov.tubesandwalls=\u0422\u0440\u0443\u0431\u044b \u0438 \u0441\u044 dialog.3d.warningtracksize=\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0432 \u0442\u0440\u0435\u043a\u0435 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0447\u0435\u043a - Java3D \u043c\u043e\u0436\u0435\u0442 \u0435\u0433\u043e \u043d\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c!\n\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c? dialog.3d.useterrain=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0440\u0435\u043b\u044c\u0435\u0444 dialog.3d.terraingridsize=\u0420\u0430\u0437\u043c\u0435\u0440 \u0441\u0435\u0442\u043a\u0438 -dialog.exportpov.baseimage=\u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u043e\u0441\u043d\u043e\u0432\u044b(\u043f\u043e\u0434\u043b\u043e\u0436\u043a\u0438) +dialog.exportpov.baseimage=\u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u043e\u0441\u043d\u043e\u0432\u044b (\u043f\u043e\u0434\u043b\u043e\u0436\u043a\u0430) dialog.exportpov.cannotmakebaseimage=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043e\u0441\u043d\u043e\u0432\u044b dialog.baseimage.title=\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u044b dialog.baseimage.useimage=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 @@ -242,10 +267,6 @@ dialog.baseimage.zoom=\u041c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u04 dialog.baseimage.incomplete=\u041d\u0435 \u0432\u0441\u0435 \u0442\u0430\u0439\u043b\u044b \u0431\u044b\u043b\u0438 \u043d\u0430\u0439\u0434\u0435\u043d\u044b dialog.baseimage.tiles=\u0422\u0430\u0439\u043b\u044b dialog.baseimage.size=\u0420\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f -dialog.exportsvg.text=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430 SVG -dialog.exportsvg.phi=\u0410\u0437\u0438\u043c\u0443\u0442 \u03d5 -dialog.exportsvg.theta=\u0423\u0433\u043e\u043b \u03b8 -dialog.exportsvg.gradients=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0437\u0430\u0442\u0435\u043d\u0435\u043d\u0438\u044f dialog.exportimage.noimagepossible=\u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043a\u0430\u0440\u0442\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u043d\u0430 \u0434\u0438\u0441\u043a \u0434\u043b\u044f \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430 dialog.exportimage.drawtrack=\u0420\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0442\u0440\u0435\u043a \u043d\u0430 \u043a\u0430\u0440\u0442\u0435 dialog.exportimage.drawtrackpoints=\u041e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 \u0442\u0440\u0435\u043a\u0430 @@ -255,15 +276,15 @@ dialog.pointtype.track=\u0422\u043e\u0447\u043a\u0438 \u0442\u0440\u0435\u043a\u dialog.pointtype.waypoint=\u041f\u0443\u0442\u0435\u0432\u044b\u0435 \u0442\u043e\u0447\u043a\u0438 dialog.pointtype.photo=\u0422\u043e\u0447\u043a\u0438 \u0441 \u0444\u043e\u0442\u043e dialog.pointtype.audio=\u0422\u043e\u0447\u043a\u0438 \u0441\u043e \u0437\u0432\u0443\u043a\u043e\u043c -dialog.pointtype.selection=\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 +dialog.pointtype.selection=\u0422\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 dialog.confirmreversetrack.title=\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0440\u0430\u0437\u0432\u043e\u0440\u043e\u0442 -dialog.confirmreversetrack.text=\u042d\u0442\u043e\u0442 \u0442\u0440\u0435\u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0440\u0443\u0448\u0435\u043d\u0430 \u043f\u043e\u0441\u043b\u0435 \u0440\u0430\u0437\u0432\u043e\u0440\u043e\u0442\u0430.\n\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435? +dialog.confirmreversetrack.text=\u042d\u0442\u043e\u0442 \u0442\u0440\u0435\u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043c\u0435\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0440\u0443\u0448\u0435\u043d\u0430 \u043f\u043e\u0441\u043b\u0435 \u0440\u0430\u0437\u0432\u043e\u0440\u043e\u0442\u0430.\n\u0423\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435? dialog.confirmcutandmove.title=\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 "\u0432\u044b\u0440\u0435\u0437\u0430\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438" -dialog.confirmcutandmove.text=\u042d\u0442\u043e\u0442 \u0442\u0440\u0435\u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0440\u0443\u0448\u0435\u043d\u0430 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f.\n\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435? +dialog.confirmcutandmove.text=\u042d\u0442\u043e\u0442 \u0442\u0440\u0435\u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043c\u0435\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0440\u0443\u0448\u0435\u043d\u0430 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f.\n\u0423\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435? dialog.interpolate.parameter.text=\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0447\u0435\u043a \u0434\u043b\u044f \u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u043c\u0435\u0436\u0434\u0443 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c\u0438 dialog.interpolate.betweenwaypoints=\u0418\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u0442\u043e\u0447\u043a\u0430\u043c\u0438 ? dialog.undo.title=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435(\u044f) -dialog.undo.pretext=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u043c\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435(\u044f) +dialog.undo.pretext=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u043c\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435(\u044f) dialog.undo.none.title=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c dialog.undo.none.text=\u041d\u0435\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0434\u043b\u044f \u043e\u0442\u043c\u0435\u043d\u044b dialog.clearundo.title=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0434\u043b\u044f \u043e\u0442\u043c\u0435\u043d\u044b @@ -282,7 +303,7 @@ dialog.addtimeoffset.subtract=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \ dialog.addtimeoffset.days=\u0414\u043d\u0438 dialog.addtimeoffset.hours=\u0427\u0430\u0441\u044b dialog.addtimeoffset.minutes=\u041c\u0438\u043d\u0443\u0442\u044b -dialog.addtimeoffset.notimestamps=\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f, \u043a\u0430\u043a \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0430\u043d\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 +dialog.addtimeoffset.notimestamps=\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f, \u0442\u0430\u043a \u043a\u0430\u043a \u0432\u044b\u0431\u043e\u0440\u043a\u0430 \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0430\u043d\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 dialog.findwaypoint.intro=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0443\u0442\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u0438\u043b\u0438 \u0435\u0433\u043e \u0447\u0430\u0441\u0442\u044c dialog.findwaypoint.search=\u041f\u043e\u0438\u0441\u043a dialog.saveexif.title=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c Exif @@ -306,7 +327,7 @@ dialog.charts.svgwidth=\u0448\u0438\u0440\u0438\u043d\u0430 SVG dialog.charts.svgheight=\u0432\u044b\u0441\u043e\u0442\u0430 SVG dialog.charts.needaltitudeortimes=\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u0442\u0440\u0435\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438\u043b\u0438 \u0432\u044b\u0441\u043e\u0442\u0435 dialog.charts.gnuplotnotfound=\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 gnuplot \u043f\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 -dialog.distances.intro=\u041a\u0440\u043e\u0442\u0447\u0430\u0439\u0448\u0435\u0435 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u0442\u043e\u0447\u043a\u0430\u043c\u0438 +dialog.distances.intro=\u041a\u0440\u0430\u0442\u0447\u0430\u0439\u0448\u0435\u0435 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u0442\u043e\u0447\u043a\u0430\u043c\u0438 dialog.distances.column.from=\u041e\u0442 \u0442\u043e\u0447\u043a\u0438 dialog.distances.column.to=\u0414\u043e \u0442\u043e\u0447\u043a\u0438 dialog.distances.currentpoint=\u0422\u0435\u043a\u0443\u0449\u0430\u044f \u0442\u043e\u0447\u043a\u0430 @@ -324,8 +345,13 @@ dialog.estimatetime.parameters.timefor=\u0412\u0440\u0435\u043c\u044f \u0434\u04 dialog.estimatetime.results=\u0412\u043e\u0442: dialog.estimatetime.results.estimatedtime=\u041f\u0440\u043e\u0433\u043d\u043e\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f dialog.estimatetime.results.actualtime=\u0420\u0435\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f +dialog.estimatetime.error.nodistance=\u0420\u0430\u0441\u0447\u0435\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u0442\u0440\u0435\u043a\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 dialog.estimatetime.error.noaltitudes=\u0412\u044b\u0431\u043e\u0440\u043a\u0430 \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u0432\u044b\u0441\u043e\u0442\u0435 -dialog.learnestimationparams.averageerror=\u0421\u0440\u0435\u0434\u043d\u044f\u044f \u043e\u0448\u0438\u0431\u043a\u0430 (%) +dialog.learnestimationparams.intro=\u042d\u0442\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u0442\u0440\u0435\u043a\u0443 +dialog.learnestimationparams.averageerror=\u0421\u0440\u0435\u0434\u043d\u044f\u044f \u043f\u043e\u0433\u0440\u0435\u0448\u043d\u043e\u0441\u0442\u044c (%) +dialog.learnestimationparams.combine=\u042d\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043c\u043e\u0436\u043d\u043e \u043a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441 \u0442\u0435\u043a\u0443\u0449\u0438\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 +dialog.learnestimationparams.combinedresults=\u041a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b +dialog.learnestimationparams.weight.100pccurrent=\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f dialog.learnestimationparams.weight.current=\u0442\u0435\u043a\u0443\u0449\u0435\u0435 dialog.learnestimationparams.weight.calculated=\u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u043d\u043e\u0435 dialog.learnestimationparams.weight.50pc=\u0421\u0440\u0435\u0434\u043d\u0435\u0435 \u043e\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0438 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u043d\u043e\u0433\u043e @@ -355,9 +381,16 @@ dialog.gpsies.activity.motorbiking=\u041d\u0430 \u043c\u043e\u0442\u043e\u0446\u dialog.gpsies.activity.snowshoe=\u041d\u0430 \u0441\u043d\u0435\u0433\u043e\u0441\u0442\u0443\u043f\u0430\u0445 dialog.gpsies.activity.sailing=\u041f\u0430\u0440\u0443\u0441\u043d\u044b\u0439 \u0441\u043f\u043e\u0440\u0442 dialog.gpsies.activity.skating=\u041d\u0430 \u043a\u043e\u043d\u044c\u043a\u0430\u0445 +dialog.mapillary.nonefound=\u0424\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b dialog.wikipedia.column.name=\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438 dialog.wikipedia.column.distance=\u0420\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 -dialog.correlate.notimestamps=\u041d\u0435\u0442 \u043e\u0442\u043c\u0435\u0442\u043e\u043a \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0434\u043b\u044f \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0435\u0432\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u0441 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044f\u043c\u0438! +dialog.wikipedia.nonefound=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u0441\u0442\u0430\u0442\u0435\u0439 \u0432 \u0412\u0438\u043a\u0438\u043f\u0435\u0434\u0438\u0438 +dialog.wikipedia.gallery=\u0413\u0430\u043b\u0435\u0440\u0435\u044f +dialog.osmpois.column.name=\u0418\u043c\u044f +dialog.osmpois.column.type=\u0422\u0438\u043f +dialog.osmpois.nonefound=\u0422\u043e\u0447\u0435\u043a \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e +dialog.geocaching.nonefound=\u0422\u0430\u0439\u043d\u0438\u043a\u043e\u0432 \u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e +dialog.correlate.notimestamps=\u041d\u0435\u0442 \u043c\u0435\u0442\u043e\u043a \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0434\u043b\u044f \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0435\u0432\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u0441 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044f\u043c\u0438! dialog.correlate.nouncorrelatedphotos=\u041d\u0435\u0442 \u043d\u0435\u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0439.\n\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c? dialog.correlate.nouncorrelatedaudios=\u041d\u0435\u0442 \u043d\u0435\u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0435\u0439.\n\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c? dialog.correlate.photoselect.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0434\u043d\u0443 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0439 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0435\u0451 \u043a\u0430\u043a \u043c\u0435\u0442\u043a\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 @@ -390,6 +423,7 @@ dialog.correlate.timestamp.end=\u041a\u043e\u043d\u0435\u0446 dialog.correlate.audioselect.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0434\u043d\u0443 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043c\u0435\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438. dialog.correlate.select.audioname=\u0418\u043c\u044f \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 dialog.correlate.select.audiolater=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u0437\u0434\u043d\u0435\u0435 +dialog.rearrangewaypoints.desc=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438 \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u043f\u0443\u0442\u0435\u0432\u044b\u0445 \u0442\u043e\u0447\u0435\u043a dialog.rearrangephotos.desc=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438 \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u0444\u043e\u0442\u043e dialog.rearrange.tostart=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043d\u0430\u0447\u0430\u043b\u043e dialog.rearrange.toend=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0435\u0446 @@ -400,12 +434,12 @@ dialog.rearrange.sortbyname=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u04 dialog.rearrange.sortbytime=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 dialog.compress.closepoints.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0431\u043b\u0438\u0436\u0435\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a dialog.compress.closepoints.paramdesc=\u0420\u0430\u0437\u043c\u0430\u0445 -dialog.compress.wackypoints.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 "\u0448\u0430\u043b\u044c\u043d\u044b\u0445"(\u043d\u0435\u043e\u0431\u044b\u0447\u043d\u044b\u0445) \u0442\u043e\u0447\u0435\u043a +dialog.compress.wackypoints.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 "\u0448\u0430\u043b\u044c\u043d\u044b\u0445" (\u043d\u0435\u043e\u0431\u044b\u0447\u043d\u044b\u0445) \u0442\u043e\u0447\u0435\u043a dialog.compress.wackypoints.paramdesc=\u0420\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 dialog.compress.singletons.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 Singleton-\u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 dialog.compress.singletons.paramdesc=\u0420\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 dialog.compress.duplicates.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432 -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.title=\u0421\u0436\u0430\u0442\u0438\u0435 \u043f\u043e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0443 \u0414\u0443\u0433\u043b\u0430\u0441\u0430-\u041f\u0435\u043a\u0435\u0440\u0430 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.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 @@ -413,15 +447,15 @@ dialog.compress.confirmnone=\u043d\u0435\u0442 \u043f\u043e\u043c\u0435\u0447\u0 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 dialog.pastecoordinates.coords=\u041a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b -dialog.pastecoordinates.nothingfound=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437 +dialog.pastecoordinates.nothingfound=\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437 dialog.help.help=\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443\nhttp://gpsprune.activityworkshop.net/ dialog.about.version=\u0412\u0435\u0440\u0441\u0438\u044f dialog.about.build=\u0420\u0435\u0432\u0438\u0437\u0438\u044f dialog.about.summarytext1=GpsPrune \u044d\u0442\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 GPS \u043f\u0440\u0438\u0435\u043c\u043d\u0438\u043a\u043e\u0432. dialog.about.summarytext2=\u042d\u0442\u043e \u0432\u044b\u043f\u0443\u0449\u0435\u043d\u043e \u043f\u043e\u0434 \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0435\u0439 Gnu GPL \u0434\u043b\u044f \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0433\u043e, \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e, \u0432\u0441\u0435\u043c\u0438\u0440\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u0438\u044f.
\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442\u0441\u044f \u0438 \u043f\u043e\u043e\u0449\u0440\u044f\u0435\u0442\u0441\u044f
\u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u043c\u0438, \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c\u0438 \u0432 \u0444\u0430\u0439\u043b\u0435 license.txt. -dialog.about.summarytext3=\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 http://activityworkshop.net/ +dialog.about.summarytext3=\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 https://activityworkshop.net/ dialog.about.languages=\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u044f\u0437\u044b\u043a\u0438 -dialog.about.translatedby=\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u0438\u0439: \u0421\u0435\u0440\u0433\u0435\u0439 \u0428\u0438\u043b\u043e\u0432 +dialog.about.translatedby=\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u0438\u0439: \u0421\u0435\u0440\u0433\u0435\u0439 \u0428\u0438\u043b\u043e\u0432, \u0412\u0430\u043b\u0435\u0440\u0438\u0439 \u0420\u043e\u043c\u0430\u043d\u043e\u0432\u0441\u043a\u0438\u0439 dialog.about.systeminfo=\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0435 dialog.about.systeminfo.os=\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 dialog.about.systeminfo.java=Java Runtime @@ -430,11 +464,6 @@ dialog.about.systeminfo.povray=Povray \u0443\u0441\u0442\u0430\u043d\u043e\u0432 dialog.about.systeminfo.exiftool=ExifTool \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d dialog.about.systeminfo.gpsbabel=GPSBabel \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d dialog.about.systeminfo.gnuplot=Gnuplot \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d -dialog.about.systeminfo.exiflib=Exif \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 -dialog.about.systeminfo.exiflib.internal=\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 -dialog.about.systeminfo.exiflib.internal.failed=\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 (\u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d) -dialog.about.systeminfo.exiflib.external=\u0412\u043d\u0435\u0448\u043d\u0438\u0439 -dialog.about.systeminfo.exiflib.external.failed=\u0412\u043d\u0435\u0448\u043d\u0438\u0439 (\u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d) dialog.about.yes=\u0414\u0430 dialog.about.no=\u041d\u0435\u0442 dialog.about.credits=\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 @@ -454,7 +483,7 @@ dialog.checkversion.newversion2= dialog.checkversion.releasedate1=\u042d\u0442\u0430 \u043d\u043e\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0431\u044b\u043b\u0430 \u0432\u044b\u043f\u0443\u0449\u0435\u043d\u0430 dialog.checkversion.releasedate2= dialog.checkversion.download=\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 http://gpsprune.activityworkshop.net/download.html -dialog.keys.intro=\u0412\u043c\u0435\u0441\u0442\u043e \u043c\u044b\u0448\u0438 \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0433\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 +dialog.keys.intro=\u0412\u043c\u0435\u0441\u0442\u043e \u043c\u044b\u0448\u0438 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0433\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 dialog.keys.keylist=
\u0421\u0434\u0432\u0438\u0433 \u043a\u0430\u0440\u0442\u044b \u0432\u043b\u0435\u0432\u043e, \u0432\u043f\u0440\u0430\u0432\u043e, \u0432\u0432\u0435\u0440\u0445, \u0432\u043d\u0438\u0437
Ctrl + \u043b\u0435\u0432\u0430\u044f, \u043f\u0440\u0430\u0432\u0430\u044f \u0441\u0442\u0440\u0435\u043b\u043a\u0430\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0443\u044e \u0438\u043b\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0442\u043e\u0447\u043a\u0443
Ctrl + \u0441\u0442\u0440\u0435\u043b\u043a\u0438 \u0432\u0432\u0435\u0440\u0445, \u0432\u043d\u0438\u0437 \u0423\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u0435 \u0438\u043b\u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u0435 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0430
Ctrl + PgUp, PgDown\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439, \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0441\u0435\u0433\u043c\u0435\u043d\u0442
Ctrl + Home, End\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0435\u0440\u0432\u0443\u044e, \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0442\u043e\u0447\u043a\u0443
Del\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0442\u043e\u0447\u043a\u0443
dialog.keys.normalmodifier=Ctrl dialog.keys.macmodifier=Command @@ -495,16 +524,29 @@ dialog.colourchooser.title=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u04 dialog.colourchooser.red=\u041a\u0440\u0430\u0441\u043d\u044b\u0439 dialog.colourchooser.green=\u0417\u0435\u043b\u0435\u043d\u044b\u0439 dialog.colourchooser.blue=\u0421\u0438\u043d\u0438\u0439 +dialog.colourer.intro=\u0422\u043e\u0447\u0435\u0447\u043d\u0430\u044f \u0440\u0430\u0441\u043a\u0440\u0430\u0441\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u0434\u0430\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 \u0446\u0432\u0435\u0442\u0430 \u0442\u0440\u0435\u043a\u043e\u0432 +dialog.colourer.type=\u0422\u0438\u043f \u043e\u043a\u0440\u0430\u0441\u043a\u0438 +dialog.colourer.type.none=\u041d\u0435\u0442 +dialog.colourer.type.byfile=\u041f\u043e \u0444\u0430\u0439\u043b\u0443 +dialog.colourer.type.bysegment=\u041f\u043e \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0443 +dialog.colourer.type.byaltitude=\u041f\u043e \u0432\u044b\u0441\u043e\u0442\u0435 +dialog.colourer.type.byspeed=\u041f\u043e \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 +dialog.colourer.type.byvertspeed=\u041f\u043e \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 +dialog.colourer.type.bygradient=\u041f\u043e \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442\u0443 +dialog.colourer.type.bydate=\u041f\u043e \u0434\u0430\u0442\u0435 +dialog.colourer.start=\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u0446\u0432\u0435\u0442 +dialog.colourer.end=\u041a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u0446\u0432\u0435\u0442 +dialog.colourer.maxcolours=\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0446\u0432\u0435\u0442\u043e\u0432 dialog.setlanguage.firstintro=\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u044f\u0437\u044b\u043a\u043e\u0432,

\u0438\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u044f\u0437\u044b\u043a\u043e\u0432\u043e\u0439 \u0444\u0430\u0439\u043b. -dialog.setlanguage.secondintro=\u0414\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u044f\u0437\u044b\u043a\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438

\u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c GpsPrune . +dialog.setlanguage.secondintro=\u0414\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u044f\u0437\u044b\u043a\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438

\u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c GpsPrune . dialog.setlanguage.language=\u042f\u0437\u044b\u043a dialog.setlanguage.languagefile=\u042f\u0437\u044b\u043a\u043e\u0432\u043e\u0439 \u0444\u0430\u0439\u043b dialog.setlanguage.endmessage=\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 GpsPrune,\n\u0447\u0442\u043e\u0431\u044b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u044f\u0437\u044b\u043a\u0430 \u0432\u0441\u0442\u0443\u043f\u0438\u043b\u043e \u0432 \u0441\u0438\u043b\u0443. -dialog.setlanguage.endmessagewithautosave=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 GpsPrune,\n\u0447\u0442\u043e\u0431\u044b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u044f\u0437\u044b\u043a\u0430 \u0432\u0441\u0442\u0443\u043f\u0438\u043b\u043e \u0432 \u0441\u0438\u043b\u0443. +dialog.setlanguage.endmessagewithautosave=\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 GpsPrune,\n\u0447\u0442\u043e\u0431\u044b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u044f\u0437\u044b\u043a\u0430 \u0432\u0441\u0442\u0443\u043f\u0438\u043b\u043e \u0432 \u0441\u0438\u043b\u0443. dialog.diskcache.save=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u044b \u043d\u0430 \u0434\u0438\u0441\u043a -dialog.diskcache.dir=\u041f\u0430\u043f\u043a\u0430 \u043a\u0435\u0448\u0430 +dialog.diskcache.dir=\u041f\u0430\u043f\u043a\u0430 \u043a\u044d\u0448\u0430 dialog.diskcache.createdir=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0430\u043f\u043a\u0443 -dialog.diskcache.nocreate=\u041f\u0430\u043f\u043a\u0430 \u0434\u043b\u044f \u043a\u0435\u0448\u0430 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0430 +dialog.diskcache.nocreate=\u041f\u0430\u043f\u043a\u0430 \u0434\u043b\u044f \u043a\u044d\u0448\u0430 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0430 dialog.diskcache.cannotwrite=\u0422\u0430\u0439\u043b\u044b \u043a\u0430\u0440\u0442\u044b \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b \u0432 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u043c \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435 dialog.diskcache.table.path=\u041f\u0443\u0442\u044c dialog.diskcache.table.usedby=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f @@ -519,7 +561,17 @@ dialog.diskcache.deleteall=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u04 dialog.diskcache.deleted=\u0423\u0434\u0430\u043b\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u043e\u0432 \u0438\u0437 \u043a\u044d\u0448\u0430 dialog.deletefieldvalues.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 dialog.deletefieldvalues.nofields=\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 \u043d\u0435\u0442 \u043f\u043e\u043b\u0435\u0439 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f -dialog.setlinewidth.text=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u043e\u043b\u0449\u0438\u043d\u0443 \u043b\u0438\u043d\u0438\u0439 \u0434\u043b\u044f \u0442\u0440\u0435\u043a\u043e\u0432 (1-4) +dialog.displaysettings.linewidth=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u043e\u043b\u0449\u0438\u043d\u0443 \u043b\u0438\u043d\u0438\u0439 \u0434\u043b\u044f \u0442\u0440\u0435\u043a\u043e\u0432 (1-4) +dialog.displaysettings.antialias=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0433\u043b\u0430\u0436\u0438\u0432\u0430\u043d\u0438\u0435 +dialog.displaysettings.waypointicons=\u0417\u043d\u0430\u0447\u043a\u0438 \u043f\u0443\u0442\u0435\u0432\u044b\u0445 \u0442\u043e\u0447\u0435\u043a +dialog.displaysettings.wpicon.default=\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e +dialog.displaysettings.wpicon.ringpt=\u041a\u0440\u0443\u0433\u043b\u044b\u0439 \u043c\u0430\u0440\u043a\u0435\u0440 +dialog.displaysettings.wpicon.plectrum=\u041c\u0435\u0434\u0438\u0430\u0442\u043e\u0440 +dialog.displaysettings.wpicon.ring=\u041a\u0440\u0443\u0433 +dialog.displaysettings.wpicon.pin=\u0417\u0430\u043a\u043e\u043b\u043a\u0430 +dialog.displaysettings.size.small=\u041c\u0430\u043b\u044b\u0439 +dialog.displaysettings.size.medium=\u0421\u0440\u0435\u0434\u043d\u0438\u0439 +dialog.displaysettings.size.large=\u0411\u043e\u043b\u044c\u0448\u043e\u0439 dialog.downloadosm.desc=\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 OSM \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438: dialog.searchwikipedianames.search=\u041f\u043e\u0438\u0441\u043a \u0434\u043b\u044f: dialog.weather.location=\u041c\u0435\u0441\u0442\u043e @@ -544,6 +596,21 @@ dialog.weather.wind=\u0412\u0435\u0442\u0435\u0440 dialog.weather.temp=\u0422\u00b0 dialog.weather.humidity=\u0412\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c dialog.weather.creditnotice=\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c \u043f\u043e openweathermap.org. +dialog.deletebydate.onlyonedate=\u0423 \u0432\u0441\u0435\u0445 \u0442\u043e\u0447\u0435\u043a \u043e\u0434\u043d\u0430 \u0438 \u0442\u0430 \u0436\u0435 \u0434\u0430\u0442\u0430. +dialog.deletebydate.intro=\u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0434\u0430\u0442\u044b \u0432 \u0442\u0440\u0435\u043a\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u043b\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 +dialog.deletebydate.nodate=\u041d\u0435\u0442 \u043c\u0435\u0442\u043e\u043a \u0432\u0440\u0435\u043c\u0435\u043d\u0438 +dialog.deletebydate.column.keep=\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c +dialog.deletebydate.column.delete=\u0423\u0434\u0430\u043b\u0438\u0442\u044c +dialog.setaltitudetolerance.text.metres=\u041f\u0440\u0435\u0434\u0435\u043b (\u0432 \u043c\u0435\u0442\u0440\u0430\u0445), \u043d\u0438\u0436\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0432\u043e\u0441\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u0438 \u0441\u043f\u0443\u0441\u043a\u0438 \u0431\u0443\u0434\u0443\u0442 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f +dialog.setaltitudetolerance.text.feet=\u041f\u0440\u0435\u0434\u0435\u043b (\u0432 \u0444\u0443\u0442\u0430\u0445), \u043d\u0438\u0436\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0432\u043e\u0441\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u0438 \u0441\u043f\u0443\u0441\u043a\u0438 \u0431\u0443\u0434\u0443\u0442 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f +dialog.settimezone.intro=\u0417\u0434\u0435\u0441\u044c \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u044f\u0441 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u043a \u0442\u043e\u0447\u0435\u043a +dialog.settimezone.system=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0439 \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u044f\u0441 +dialog.settimezone.custom=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u044f\u0441: +dialog.settimezone.list.toomany=\u0421\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0434\u043b\u044f \u0432\u044b\u0431\u043e\u0440\u0430 +dialog.settimezone.selectedzone=\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u044f\u0441 +dialog.settimezone.offsetfromutc=\u0421\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u043f\u043e UTC +dialog.autoplay.duration=\u0414\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c (\u0441\u0435\u043a.) +dialog.autoplay.usetimestamps=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043a\u0438 \u0442\u043e\u0447\u043a\u0438 dialog.autoplay.rewind=\u041d\u0430\u0437\u0430\u0434 dialog.autoplay.pause=\u0414\u0435\u0301\u043b\u0430\u0442\u044c \u043f\u0430\u0301\u0443\u0437\u0443 dialog.autoplay.play=\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 @@ -590,11 +657,15 @@ confirm.downloadsrtm.1=\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e %d confirm.downloadsrtm.none=\u0412\u0441\u0435 \u0444\u0430\u0439\u043b\u044b \u0443\u0436\u0435 \u0432 \u043a\u044d\u0448\u0435 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.single=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u044c \u0431\u044b\u043b\u0430 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 confirm.correlateaudios.multi=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u0431\u044b\u043b\u0438 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b # Tips, shown just once when appropriate tip.title=\u0421\u043e\u0432\u0435\u0442 +tip.useamapcache=\u041f\u0443\u0442\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043a\u044d\u0448\u0430 \u0434\u0438\u0441\u043a\u0430 (\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 -> \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u044b \u043d\u0430 \u0434\u0438\u0441\u043a\u00bb)\n\u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0442\u0440\u0430\u0444\u0438\u043a. +tip.learntimeparams=\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0431\u0443\u0434\u0443\u0442 \u0431\u043e\u043b\u0435\u0435 \u0442\u043e\u0447\u043d\u044b\u043c\u0438, \u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435\n\u0422\u0440\u0435\u043a->\u0417\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438\n\u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0432\u0430\u043c\u0438 \u0442\u0440\u0435\u043a\u0438. +tip.downloadsrtm=\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u044d\u0442\u043e, \u0432\u044b\u0437\u043e\u0432\u043e\u043c\n\u041e\u043d\u043b\u0430\u0439\u043d-> \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c SRTM-\u0442\u0430\u0439\u043b\u044b\n\u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0432\u0430\u0448\u0435\u043c \u043a\u044d\u0448\u0435 \u043a\u0430\u0440\u0442\u044b. +tip.usesrtmfor3d=\u042d\u0442\u043e\u0442 \u0442\u0440\u0435\u043a \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0432\u044b\u0441\u043e\u0442.\n\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 SRTM, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u0440\u0438\u0431\u043b\u0438\u0437\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432\u044b\u0441\u043e\u0442 \u0434\u043b\u044f \u0432\u0438\u0434\u0430 3D. 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 @@ -607,7 +678,7 @@ button.overwrite=\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u0 button.moveup=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u0432\u0435\u0440\u0445 button.movedown=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u0437 button.edit=\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c -button.exit=\u0412\u044b\u0445\u043e\u0434 +button.exit=\u0412\u044b\u0439\u0442\u0438 button.close=\u0417\u0430\u043a\u0440\u044b\u0442\u044c button.continue=\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c button.yes=\u0414\u0430 @@ -622,7 +693,7 @@ button.preview=\u041f\u0440\u0435\u0434\u043f\u0440\u043e\u0441\u043c\u043e\u044 button.load=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c button.upload=\u0412\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c button.guessfields=\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0435 \u043f\u043e\u043b\u044f -button.showwebpage=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0435\u0431\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 +button.showwebpage=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 button.check=\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 button.resettodefaults=\u0421\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0435 button.browse=\u041e\u0431\u0437\u043e\u0440... @@ -754,7 +825,6 @@ wikipedia.lang=ru openweathermap.lang=ru webservice.peakfinder=\u041e\u0442\u043a\u0440\u044b\u0442\u044c Peakfinder.org webservice.geohack=\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 Geohack -webservice.panoramio=\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u043a\u0430\u0440\u0442\u0443 Panoramio # Cardinals for 3d plots cardinal.n=\u0421\u0435\u0432\u0435\u0440 @@ -812,7 +882,6 @@ error.jpegload.dialogtitle=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u04 error.jpegload.nofilesfound=\u0424\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b error.jpegload.nojpegsfound=JPEG-\u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b error.jpegload.nogpsfound=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 GPS-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f -error.jpegload.exifreadfailed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c Exif-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u041d\u0435\u0442 Exif-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u200b\u200b\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c\n\u0431\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u043a\u0438\u0445-\u043b\u0438\u0431\u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0445 \u0438\u043b\u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a. error.audioload.nofilesfound=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b error.gpsload.unknown=\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 error.undofailed.title=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c @@ -822,9 +891,9 @@ error.rearrange.noop=\u041f\u0435\u0440\u0435\u0440\u0430\u0441\u043f\u0440\u043 error.function.notavailable.title=\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 error.function.nojava3d=\u042d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a Java3D. error.3d=\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f 3D-\u0432\u0438\u0434\u0430 -error.readme.notfound=Readme \u0444\u0430\u0439\u043b\u043d\u0435\u043d\u0430\u0439\u0434\u0435\u043d +error.readme.notfound=Readme \u0444\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d error.osmimage.dialogtitle=\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043a\u0430\u0440\u0442\u044b -error.osmimage.failed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0443. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442. +error.osmimage.failed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0443. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442. error.language.wrongfile=\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043d\u0435 \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u044f\u0437\u044b\u043a\u043e\u0432\u043e\u0439 \u0444\u0430\u0439\u043b \u0434\u043b\u044f GpsPrune error.convertnamestotimes.nonames=\u041d\u0435\u0442 \u0438\u043c\u0435\u043d, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0432\u043e \u0432\u0440\u0435\u043c\u044f error.lookupsrtm.nonefound=\u041d\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043e\u0442\u043c\u0435\u0442\u043e\u043a \u0432\u044b\u0441\u043e\u0442 \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0442\u043e\u0447\u0435\u043a. @@ -836,4 +905,7 @@ error.playaudiofailed=\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u043e\u0441\u0 error.cache.notthere=\u041f\u0430\u043f\u043a\u0430 \u043a\u044d\u0448\u0430 \u0441 \u0442\u0430\u0439\u043b\u0430\u043c\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 error.cache.empty=\u041f\u0430\u043f\u043a\u0430 \u043a\u044d\u0448\u0430 \u0441 \u0442\u0430\u0439\u043b\u0430\u043c\u0438 \u043f\u0443\u0441\u0442\u0430 error.cache.cannotdelete=\u041d\u0435\u0442 \u0442\u0430\u0439\u043b\u043e\u0432, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f +error.learnestimationparams.failed=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441 \u044d\u0442\u043e\u0433\u043e \u0442\u0440\u0435\u043a\u0430.\n\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u0442\u0440\u0435\u043a\u0438. error.tracksplit.nosplit=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u0440\u0435\u043a +error.downloadsrtm.nocache=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b.\n\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043a\u044d\u0448 \u0434\u0438\u0441\u043a\u0430. +error.sewsegments.nothingdone=\u041d\u0435\u0442 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0441\u0448\u0438\u0442\u044b \u0432\u043c\u0435\u0441\u0442\u0435.\n\u0412 \u0442\u0440\u0435\u043a\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 %d \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u043e\u0432. diff --git a/tim/prune/lang/prune-texts_sv.properties b/tim/prune/lang/prune-texts_sv.properties index 59a84af..7b227d2 100644 --- a/tim/prune/lang/prune-texts_sv.properties +++ b/tim/prune/lang/prune-texts_sv.properties @@ -82,7 +82,6 @@ function.sendtogps=Skicka till GPS function.exportkml=Exportera KML function.exportgpx=Exportera GPX function.exportpov=Exportera POV -function.exportsvg=Exportera SVG function.exportimage=Exportera bildfil function.editwaypointname=Redigera namn p\u00e5 waypoint function.compress=Komprimera sp\u00e5r diff --git a/tim/prune/lang/prune-texts_uk.properties b/tim/prune/lang/prune-texts_uk.properties index ddfb0ac..212edf4 100644 --- a/tim/prune/lang/prune-texts_uk.properties +++ b/tim/prune/lang/prune-texts_uk.properties @@ -81,7 +81,6 @@ function.sendtogps=\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\ 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 @@ -116,7 +115,6 @@ function.searchwikipedianames=\u041f\u043e\u0448\u0443\u043a \u0441\u0442\u0430\ 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 diff --git a/tim/prune/lang/prune-texts_zh.properties b/tim/prune/lang/prune-texts_zh.properties index a2344e4..d1c1282 100644 --- a/tim/prune/lang/prune-texts_zh.properties +++ b/tim/prune/lang/prune-texts_zh.properties @@ -81,7 +81,6 @@ function.sendtogps=\u53d1\u9001\u81f3GPS function.exportkml=\u8f93\u51faKML\u6587\u4ef6 function.exportgpx=\u8f93\u51faGPX\u6587\u4ef6 function.exportpov=\u8f93\u51faPOV\u6587\u4ef6 -function.exportsvg=\u8f93\u51faSVG\u6587\u4ef6 function.exportimage=\u8f93\u51fa\u56fe\u50cf function.editwaypointname=\u7f16\u8f91\u822a\u70b9\u540d function.compress=\u538b\u7f29\u8f68\u8ff9(\u6807\u8bb0\u8981\u5220\u9664\u822a\u70b9) @@ -116,7 +115,6 @@ function.searchwikipedianames=\u6309\u540d\u5b57\u4ece\u7ef4\u57fa\u767e\u79d1\u function.downloadosm=\u4e0b\u8f7d\u6b64\u5730OSM\u6570\u636e function.duplicatepoint=\u590d\u5236\u70b9 function.setcolours=\u8bbe\u7f6e\u989c\u8272 -function.setlinewidth=\u8bbe\u7f6e\u7ebf\u4f53\u5bbd\u5ea6 function.setlanguage=\u8bbe\u7f6e\u8bed\u8a00 function.connecttopoint=\u94fe\u63a5\u5230\u5f53\u524d\u70b9 function.disconnectfrompoint=\u64a4\u9500\u94fe\u63a5 @@ -259,10 +257,6 @@ dialog.baseimage.zoom=\u7f29\u653e\u7ea7\u522b dialog.baseimage.incomplete=\u56fe\u50cf\u4e0d\u5b8c\u6574 dialog.baseimage.tiles=\u5730\u56fe\u5757 dialog.baseimage.size=\u56fe\u50cf\u5c3a\u5bf8 -dialog.exportsvg.text=\u9009\u62e9\u8f93\u51faSVG\u6587\u4ef6\u7684\u53c2\u6570 -dialog.exportsvg.phi=\u65b9\u4f4d\u89d2 -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 @@ -442,8 +436,8 @@ dialog.help.help=\u66f4\u591a\u4fe1\u606f\u548c\u7528\u6cd5\uff0c\u8bf7\u53c2\u8 dialog.about.version=\u7248\u672c dialog.about.build=Build dialog.about.summarytext1=GpsPrune\u662f\u4e00\u4e2a\u4eceGPS\u4e2d\u5bfc\u5165\u6570\u636e\uff0c\u663e\u793a\u6570\u636e\u548c\u7f16\u8f91\u6570\u636e\u7684\u8f6f\u4ef6 -dialog.about.summarytext2=\u5b83\u7684\u53d1\u884c\u662f\u57fa\u4e8eGnu GPL\u89c4\u5219\uff0c\u662f\u514d\u8d39\u7684\uff0c\u5f00\u653e\u5f0f\u7684\uff0c\u5168\u4e16\u754c\u5171\u7528\u5e76\u6539\u5584\u589e\u5f3a\u5176\u529f\u80fd\n\u5728\u7b26\u5408"license.txt"\u6761\u4ef6\u4e0b\uff0c\u5bb9\u8bb8\u5e76\u9f13\u52b1\u590d\u5236\uff0c\u5206\u53d1\u53ca\u4fee\u6539\u3002 -dialog.about.summarytext3=\u66f4\u591a\u4fe1\u606f\u53ca\u7528\u6cd5\u6307\u5357\uff0c\u8bf7\u53c2\u8003\u7f51\u7ad9\uff1a\nhttp:activityworkshop.net/ +dialog.about.summarytext2=\u5b83\u7684\u53d1\u884c\u662f\u57fa\u4e8eGnu GPL\u89c4\u5219\uff0c\u662f\u514d\u8d39\u7684\uff0c\u5f00\u653e\u5f0f\u7684\uff0c\u5168\u4e16\u754c\u5171\u7528\u5e76\u6539\u5584\u589e\u5f3a\u5176\u529f\u80fd
\u5728\u7b26\u5408"license.txt"\u6761\u4ef6\u4e0b\uff0c\u5bb9\u8bb8\u5e76\u9f13\u52b1\u590d\u5236\uff0c\u5206\u53d1\u53ca\u4fee\u6539\u3002 +dialog.about.summarytext3=\u66f4\u591a\u4fe1\u606f\u53ca\u7528\u6cd5\u6307\u5357\uff0c\u8bf7\u53c2\u8003\u7f51\u7ad9\uff1a
http:activityworkshop.net/ dialog.about.languages=\u652f\u6301\u8bed\u8a00 dialog.about.translatedby=\u4e2d\u6587\u7ffb\u8bd1\uff1a\u9ed1\u8001\u9648 (Sam Chen) dialog.about.systeminfo=\u7cfb\u7edf\u4fe1\u606f @@ -454,11 +448,6 @@ dialog.about.systeminfo.povray=Povray \u662f\u5426\u5b89\u88c5 dialog.about.systeminfo.exiftool=Exiftool \u662f\u5426\u5b89\u88c5 dialog.about.systeminfo.gpsbabel=Gpsbabel \u662f\u5426\u5b89\u88c5 dialog.about.systeminfo.gnuplot=Gnuplot \u662f\u5426\u5b89\u88c5 -dialog.about.systeminfo.exiflib=Exif \u5e93 -dialog.about.systeminfo.exiflib.internal=\u5185\u90e8 -dialog.about.systeminfo.exiflib.internal.failed=\u5185\u90e8(\u672a\u627e\u5230) -dialog.about.systeminfo.exiflib.external=\u5916\u90e8 -dialog.about.systeminfo.exiflib.external.failed=\u5916\u90e8(\u672a\u627e\u5230) dialog.about.yes=\u662f dialog.about.no=\u5426 dialog.about.credits=\u81f4\u8c22 @@ -556,7 +545,7 @@ dialog.diskcache.deleteall=\u5220\u9664\u6240\u6709\u5730\u56fe\u5757 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.displaysettings.linewidth=\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 @@ -853,7 +842,6 @@ 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\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 diff --git a/tim/prune/load/FieldGuesser.java b/tim/prune/load/FieldGuesser.java index c76318d..970926b 100644 --- a/tim/prune/load/FieldGuesser.java +++ b/tim/prune/load/FieldGuesser.java @@ -4,7 +4,7 @@ import tim.prune.I18nManager; import tim.prune.data.Field; import tim.prune.data.Latitude; import tim.prune.data.Longitude; -import tim.prune.data.Timestamp; +import tim.prune.data.TimestampUtc; /** * Class to try to match data with field names, @@ -317,7 +317,7 @@ public abstract class FieldGuesser { // must be at least 7 characters long if (inValue.length() < 7) {return false;} - Timestamp stamp = new Timestamp(inValue); + TimestampUtc stamp = new TimestampUtc(inValue); return stamp.isValid(); } } diff --git a/tim/prune/load/FileLoader.java b/tim/prune/load/FileLoader.java index cf60f9e..52bdef2 100644 --- a/tim/prune/load/FileLoader.java +++ b/tim/prune/load/FileLoader.java @@ -137,7 +137,7 @@ public class FileLoader else if (fileExtension.equals(".jpg") || fileExtension.equals("jpeg")) { Photo photo = JpegLoader.createPhoto(inFile); - TreeSet photoSet = new TreeSet(); + TreeSet photoSet = new TreeSet(new MediaSorter()); photoSet.add(photo); _app.informPhotosLoaded(photoSet); _app.informNoDataLoaded(); // To trigger load of next file if any diff --git a/tim/prune/load/JpegLoader.java b/tim/prune/load/JpegLoader.java index 672cd02..3e22eba 100644 --- a/tim/prune/load/JpegLoader.java +++ b/tim/prune/load/JpegLoader.java @@ -14,15 +14,16 @@ import tim.prune.I18nManager; import tim.prune.config.Config; import tim.prune.data.Altitude; import tim.prune.data.DataPoint; -import tim.prune.data.Field; import tim.prune.data.LatLonRectangle; import tim.prune.data.Latitude; import tim.prune.data.Longitude; import tim.prune.data.Photo; import tim.prune.data.Timestamp; +import tim.prune.data.TimestampLocal; +import tim.prune.data.TimestampUtc; import tim.prune.data.UnitSetLibrary; import tim.prune.function.Cancellable; -import tim.prune.jpeg.ExifGateway; +import tim.prune.jpeg.InternalExifLibrary; import tim.prune.jpeg.JpegData; /** @@ -224,7 +225,7 @@ public class JpegLoader implements Runnable, Cancellable // Create Photo object Photo photo = new Photo(inFile); // Try to get information out of exif - JpegData jpegData = ExifGateway.getJpegData(inFile); + JpegData jpegData = new InternalExifLibrary().getJpegData(inFile); Timestamp timestamp = null; if (jpegData != null) { @@ -253,12 +254,12 @@ public class JpegLoader implements Runnable, Cancellable } // Use file timestamp if exif timestamp isn't available if (timestamp == null) { - timestamp = new Timestamp(inFile.lastModified()); + timestamp = new TimestampUtc(inFile.lastModified()); } // Apply timestamp to photo and its point (if any) photo.setTimestamp(timestamp); if (photo.getDataPoint() != null) { - photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.Format.ISO8601), false); + // photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.Format.ISO8601), false); } return photo; } @@ -355,7 +356,7 @@ public class JpegLoader implements Runnable, Cancellable if (inDate == null || inTime == null || inDate.length != 3 || inTime.length != 3) { return null; } - return new Timestamp(inDate[0], inDate[1], inDate[2], + return new TimestampLocal(inDate[0], inDate[1], inDate[2], inTime[0], inTime[1], inTime[2]); } @@ -370,7 +371,7 @@ public class JpegLoader implements Runnable, Cancellable Timestamp stamp = null; try { - stamp = new Timestamp(Integer.parseInt(inStamp.substring(0, 4)), + stamp = new TimestampLocal(Integer.parseInt(inStamp.substring(0, 4)), Integer.parseInt(inStamp.substring(5, 7)), Integer.parseInt(inStamp.substring(8, 10)), Integer.parseInt(inStamp.substring(11, 13)), diff --git a/tim/prune/load/TextFileLoader.java b/tim/prune/load/TextFileLoader.java index 8fd30b2..b4a0ca8 100644 --- a/tim/prune/load/TextFileLoader.java +++ b/tim/prune/load/TextFileLoader.java @@ -440,6 +440,11 @@ public class TextFileLoader _vSpeedUnitsDropdown.addItem(I18nManager.getText(spUnit.getNameKey())); } vSpeedGrid.add(_vSpeedUnitsDropdown); + final String vSpeedLabelText = I18nManager.getText("dialog.openoptions.vspeed.intro"); + if (!vSpeedLabelText.isEmpty()) { + vSpeedGrid.add(new JLabel(vSpeedLabelText)); + vSpeedGrid.add(new JLabel("")); + } _vSpeedUpwardsRadio = new JRadioButton(I18nManager.getText("dialog.openoptions.vspeed.positiveup")); JRadioButton vSpeedDownwardsRadio = new JRadioButton(I18nManager.getText("dialog.openoptions.vspeed.positivedown")); ButtonGroup vSpeedDirGroup = new ButtonGroup(); diff --git a/tim/prune/readme.txt b/tim/prune/readme.txt index c8120c1..780ecc0 100644 --- a/tim/prune/readme.txt +++ b/tim/prune/readme.txt @@ -1,11 +1,11 @@ -GpsPrune version 18.6 -===================== +GpsPrune version 19 +=================== GpsPrune is an application for viewing, editing and managing coordinate data from GPS systems, including format conversion, charting, 3d visualisation, audio and photo correlation, and online resource lookup. Full details can be found at https://gpsprune.activityworkshop.net/ -GpsPrune is copyright 2006-2016 activityworkshop.net and distributed under the terms of the Gnu GPL version 2. +GpsPrune is copyright 2006-2018 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_18.6.jar + java -jar gpsprune_19.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,49 +25,28 @@ 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_18.6.jar --lang=DE + java -jar gpsprune_19.jar --lang=DE -New with version 18.6 -===================== -The following fix was made since version 18.5: - - Change of URL for SRTM tiles, and recognise when downloaded file is too small to be valid - -New with version 18.5 -===================== -The following fixes and additions were made since version 18.4: - - When points have no altitudes, send them to a GPS with altitude 0 (prevents weird values when reading back) - - When points are not displayed on map, still colour the lines according to the selected scheme - - When points all have crazy values, don't hang the profile chart - - When images can't be fetched from the internet, don't delete expired cache images - - Translation updates to es, uk, ru, cz - -New with version 18.4 -===================== -The following fixes and additions were made since version 18.3: - - Updated GPSBabel format descriptions (thanks, Juergen!) - - Removed calls to the retired opencaching.com site (thanks, Garmin!) - -New with version 18.3 -===================== -The following fixes and additions were made since version 18.2: - - Fix for saving track names and descriptions containing ampersands in xml formats (thanks, Joe!) - - Additional window icon sizes (thanks, Sebastic!) - - Remember window size - -New with version 18.2 -===================== -The following fixes and additions were made since version 18.1: - - Fix for saving point names containing ampersands in gpx, kml, kmz formats (thanks, Joe!) - - Minor translation updates to nl, it, pl - -New with version 18.1 -===================== +New with version 19 +=================== The following fixes and additions were made since version 18: - Fix for duration rounding bug affecting sub-second timestamps - Wikipedia search now also includes galleries from wikimedia - Photo popup window now gets updated when the track selection changes - - Small improvements to Ukrainian, Dutch translations + - Remove export to SVG function as it has fallen behind the java3d and pov options + - Remove Turkish language because there haven't been any translators since early 2010 + - Function to add waypoints along the track at intervals of distance or time (eg every 5 km) + - Optionally draw arrows on the track lines to show direction of travel + - Waypoint rendering using icons (wishlist 71) + - Allow user to select timezone in which timestamps are displayed (wishlist 61) + - Provide call to geonames.org's OSM node search function, to find amenities (like bus stops) close to the current point + - Fix OSM download function using overpass API + - Fix opencyclemap URLs + - Fix reading of Exif data for some unusual cases (from Adobe Lightroom?) + - Remove Panoramio + - Update wikimedia catalogue + - Debian and Ubuntu packages no longer rely on external libmetadata jar New with version 18 =================== diff --git a/tim/prune/save/FileSaver.java b/tim/prune/save/FileSaver.java index 1e199d9..45af83e 100644 --- a/tim/prune/save/FileSaver.java +++ b/tim/prune/save/FileSaver.java @@ -604,7 +604,7 @@ public class FileSaver if (inPoint.hasTimestamp()) { // format value accordingly - inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat)); + inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat, null)); } } else if (inField == Field.MEDIA_FILENAME) diff --git a/tim/prune/save/GpxExporter.java b/tim/prune/save/GpxExporter.java index 7cac01c..39b6a36 100644 --- a/tim/prune/save/GpxExporter.java +++ b/tim/prune/save/GpxExporter.java @@ -541,7 +541,7 @@ public class GpxExporter extends GenericFunction implements Runnable source = replaceGpxTags(source, "lat=\"", "\"", inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); source = replaceGpxTags(source, "lon=\"", "\"", inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); source = replaceGpxTags(source, "", "", inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES)); - source = replaceGpxTags(source, "", inPoint.getTimestamp().getText(Timestamp.Format.ISO8601)); + source = replaceGpxTags(source, "", inPoint.getTimestamp().getText(Timestamp.Format.ISO8601, null)); if (inPoint.isWaypoint()) { source = replaceGpxTags(source, "", "", XmlUtils.fixCdata(inPoint.getWaypointName())); @@ -693,7 +693,7 @@ public class GpxExporter extends GenericFunction implements Runnable if (inPoint.hasTimestamp() && inSettings.getExportTimestamps()) { inWriter.write("\t\t\n"); } // write waypoint name after elevation and time @@ -762,7 +762,7 @@ public class GpxExporter extends GenericFunction implements Runnable if (inPoint.hasTimestamp() && inSettings.getExportTimestamps()) { inWriter.write("\t\t\t\t\n"); } // photo, audio diff --git a/tim/prune/save/ImageExporter.java b/tim/prune/save/ImageExporter.java index f3383fe..ea18892 100644 --- a/tim/prune/save/ImageExporter.java +++ b/tim/prune/save/ImageExporter.java @@ -40,6 +40,8 @@ import tim.prune.gui.colour.PointColourer; import tim.prune.gui.map.MapSource; import tim.prune.gui.map.MapSourceLibrary; import tim.prune.gui.map.MapUtils; +import tim.prune.gui.map.WpIconDefinition; +import tim.prune.gui.map.WpIconLibrary; import tim.prune.load.GenericFileFilter; import tim.prune.threedee.ImageDefinition; @@ -331,18 +333,33 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer // Now the waypoints final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT); g.setColor(textColour); + WpIconDefinition wpIconDefinition = null; + final int wpType = Config.getConfigInt(Config.KEY_WAYPOINT_ICONS); + if (wpType != WpIconLibrary.WAYPT_DEFAULT) + { + wpIconDefinition = WpIconLibrary.getIconDefinition(wpType, WpIconLibrary.SIZE_MEDIUM); + } // Loop again to draw waypoints for (int i=0; i"); if (point.hasTimestamp()) { - whenList.append(point.getTimestamp().getText(Timestamp.Format.ISO8601)); + whenList.append(point.getTimestamp().getText(Timestamp.Format.ISO8601, null)); } whenList.append("\n"); // Add coordinates to the list diff --git a/tim/prune/save/PovExporter.java b/tim/prune/save/PovExporter.java index c002747..e0fc9d3 100644 --- a/tim/prune/save/PovExporter.java +++ b/tim/prune/save/PovExporter.java @@ -480,7 +480,7 @@ public class PovExporter extends Export3dFunction private void writeStartOfFile(FileWriter inWriter, String inLineSeparator, File inImageFile, File inTerrainFile) throws IOException { - inWriter.write("// Pov file produced by GpsPrune - see http://gpsprune.activityworkshop.net/"); + inWriter.write("// Pov file produced by GpsPrune - see https://gpsprune.activityworkshop.net/"); inWriter.write(inLineSeparator); inWriter.write("#version 3.6;"); inWriter.write(inLineSeparator); @@ -864,7 +864,7 @@ public class PovExporter extends Export3dFunction /** * @param inCode height code to check - * @return validated height code within range 0 to max + * @return validated height code within range 0 to maxHeightCode */ private static byte checkHeightCode(byte inCode) { diff --git a/tim/prune/save/SvgExporter.java b/tim/prune/save/SvgExporter.java deleted file mode 100644 index d623b4a..0000000 --- a/tim/prune/save/SvgExporter.java +++ /dev/null @@ -1,520 +0,0 @@ -package tim.prune.save; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.FlowLayout; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.text.NumberFormat; -import java.util.Iterator; -import java.util.TreeSet; - -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JDialog; -import javax.swing.JFileChooser; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.SwingConstants; - -import tim.prune.App; -import tim.prune.I18nManager; -import tim.prune.UpdateMessageBroker; -import tim.prune.config.Config; -import tim.prune.data.Track; -import tim.prune.function.Export3dFunction; -import tim.prune.gui.DialogCloser; -import tim.prune.load.GenericFileFilter; -import tim.prune.threedee.ThreeDModel; - -/** - * Class to export a 3d scene of the track to a specified Svg file - */ -public class SvgExporter extends Export3dFunction -{ - private Track _track = null; - private JDialog _dialog = null; - private JFileChooser _fileChooser = null; - private double _phi = 0.0, _theta = 0.0; - private JTextField _phiField = null, _thetaField = null; - private JTextField _altitudeFactorField = null; - private JCheckBox _gradientsCheckbox = null; - private static final double _scaleFactor = 200.0; - - - /** - * Constructor - * @param inApp App object - */ - public SvgExporter(App inApp) - { - super(inApp); - _track = inApp.getTrackInfo().getTrack(); - // Set default rotation angles - _phi = 30; _theta = 55; - } - - /** Get the name key */ - public String getNameKey() { - return "function.exportsvg"; - } - - /** - * Set the rotation angles using coordinates for the camera - * @param inX X coordinate of camera - * @param inY Y coordinate of camera - * @param inZ Z coordinate of camera - */ - public void setCameraCoordinates(double inX, double inY, double inZ) - { - // Calculate phi and theta based on camera x,y,z - _phi = Math.toDegrees(Math.atan2(inX, inZ)); - _theta = Math.toDegrees(Math.atan2(inY, Math.sqrt(inX*inX + inZ*inZ))); - } - - - /** - * Show the dialog to select options and export file - */ - public void begin() - { - // Make dialog window to select 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(); - threeDP.setMaximumFractionDigits(3); - _phiField.setText(threeDP.format(_phi)); - _thetaField.setText(threeDP.format(_theta)); - // Set vertical scale - _altitudeFactorField.setText("" + _altFactor); - // Show dialog - _dialog.pack(); - _dialog.setVisible(true); - } - - - /** - * Make the dialog components to select the export options - * @return Component holding gui elements - */ - private Component makeDialogComponents() - { - JPanel panel = new JPanel(); - panel.setLayout(new BorderLayout()); - JLabel introLabel = new JLabel(I18nManager.getText("dialog.exportsvg.text")); - introLabel.setBorder(BorderFactory.createEmptyBorder(4, 4, 6, 4)); - panel.add(introLabel, BorderLayout.NORTH); - // OK, Cancel buttons - JPanel buttonPanel = new JPanel(); - buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); - JButton okButton = new JButton(I18nManager.getText("button.ok")); - okButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - doExport(); - _dialog.dispose(); - } - }); - buttonPanel.add(okButton); - JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); - cancelButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - _dialog.dispose(); - } - }); - buttonPanel.add(cancelButton); - panel.add(buttonPanel, BorderLayout.SOUTH); - - // central panel - JPanel centralPanel = new JPanel(); - centralPanel.setLayout(new GridLayout(0, 2, 10, 4)); - - // rotation angles - JLabel phiLabel = new JLabel(I18nManager.getText("dialog.exportsvg.phi")); - phiLabel.setHorizontalAlignment(SwingConstants.TRAILING); - centralPanel.add(phiLabel); - _phiField = new JTextField("" + _phi); - _phiField.addKeyListener(new DialogCloser(_dialog)); - centralPanel.add(_phiField); - JLabel thetaLabel = new JLabel(I18nManager.getText("dialog.exportsvg.theta")); - thetaLabel.setHorizontalAlignment(SwingConstants.TRAILING); - centralPanel.add(thetaLabel); - _thetaField = new JTextField("" + _theta); - centralPanel.add(_thetaField); - // Altitude exaggeration - JLabel altFactorLabel = new JLabel(I18nManager.getText("dialog.3d.altitudefactor")); - altFactorLabel.setHorizontalAlignment(SwingConstants.TRAILING); - centralPanel.add(altFactorLabel); - _altitudeFactorField = new JTextField("" + _altFactor); - centralPanel.add(_altitudeFactorField); - // Checkbox for gradients or not - JLabel gradientsLabel = new JLabel(I18nManager.getText("dialog.exportsvg.gradients")); - gradientsLabel.setHorizontalAlignment(SwingConstants.TRAILING); - centralPanel.add(gradientsLabel); - _gradientsCheckbox = new JCheckBox(); - _gradientsCheckbox.setSelected(true); - centralPanel.add(_gradientsCheckbox); - - // add this grid to the holder panel - JPanel holderPanel = new JPanel(); - holderPanel.setLayout(new BorderLayout(5, 5)); - JPanel boxPanel = new JPanel(); - boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS)); - boxPanel.add(centralPanel); - holderPanel.add(boxPanel, BorderLayout.CENTER); - - panel.add(holderPanel, BorderLayout.CENTER); - return panel; - } - - - /** - * Select the file and export data to it - */ - private void doExport() - { - // Copy camera coordinates - _phi = checkAngle(_phiField.getText()); - _theta = checkAngle(_thetaField.getText()); - - // OK pressed, so choose output file - if (_fileChooser == null) - { - _fileChooser = new JFileChooser(); - _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG); - _fileChooser.setFileFilter(new GenericFileFilter("filetype.svg", new String[] {"svg"})); - _fileChooser.setAcceptAllFileFilterUsed(false); - // start from directory in config which should be set - final String configDir = Config.getConfigString(Config.KEY_TRACK_DIR); - if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));} - } - - // Allow choose again if an existing file is selected - boolean chooseAgain = false; - do - { - chooseAgain = false; - if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION) - { - // OK pressed and file chosen - File file = _fileChooser.getSelectedFile(); - if (!file.getName().toLowerCase().endsWith(".svg")) { - file = new File(file.getAbsolutePath() + ".svg"); - } - // Check if file exists and if necessary prompt for overwrite - Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")}; - if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame, - I18nManager.getText("dialog.save.overwrite.text"), - I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]) - == JOptionPane.YES_OPTION) - { - // Export the file - if (exportFile(file)) - { - // file saved - store directory in config for later - Config.setConfigString(Config.KEY_TRACK_DIR, file.getParentFile().getAbsolutePath()); - // also store exaggeration - Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_altFactor * 100)); - } - else { - // export failed so need to choose again - chooseAgain = true; - } - } - else { - // overwrite cancelled so need to choose again - chooseAgain = true; - } - } - } while (chooseAgain); - } - - - /** - * Export the track data to the specified file - * @param inFile File object to save to - * @return true if successful - */ - private boolean exportFile(File inFile) - { - FileWriter writer = null; - // find out the line separator for this system - String lineSeparator = System.getProperty("line.separator"); - try - { - // create and scale model - ThreeDModel model = new ThreeDModel(_track); - try - { - // try to use given altitude factor - _altFactor = Double.parseDouble(_altitudeFactorField.getText()); - model.setAltitudeFactor(_altFactor); - } - catch (NumberFormatException nfe) {} - model.scale(); - - boolean useGradients = _gradientsCheckbox.isSelected(); - - // Create file and write basics - writer = new FileWriter(inFile); - writeStartOfFile(writer, useGradients, lineSeparator); - writeBasePlane(writer, lineSeparator); - // write out cardinal letters NESW - writeCardinals(writer, lineSeparator); - - // write out points - writeDataPoints(writer, model, useGradients, lineSeparator); - writeEndOfFile(writer, lineSeparator); - - // everything worked - UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1") - + " " + _track.getNumPoints() + " " + I18nManager.getText("confirm.save.ok2") - + " " + inFile.getAbsolutePath()); - return true; - } - catch (IOException ioe) - { - JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(), - I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE); - } - finally - { - // close file ignoring exceptions - try { - writer.close(); - } - catch (Exception e) {} - } - return false; - } - - - /** - * Write the start of the Svg file - * @param inWriter Writer to use for writing file - * @param inUseGradients true to use gradients, false for flat fills - * @param inLineSeparator line separator to use - * @throws IOException on file writing error - */ - private static void writeStartOfFile(FileWriter inWriter, boolean inUseGradients, - String inLineSeparator) - throws IOException - { - inWriter.write(""); - inWriter.write(inLineSeparator); - inWriter.write(""); - inWriter.write(inLineSeparator); - inWriter.write(""); - inWriter.write(inLineSeparator); - if (inUseGradients) - { - final String defs = "" + - "" + - "" + - "" + - "" + inLineSeparator + - "" + - "" + - "" + - "" + - ""; - inWriter.write(defs); - inWriter.write(inLineSeparator); - } - inWriter.write(""); - inWriter.write(inLineSeparator); - } - - /** - * Write the base plane - * @param inWriter Writer to use for writing file - * @param inLineSeparator line separator to use - * @throws IOException on file writing error - */ - private void writeBasePlane(FileWriter inWriter, String inLineSeparator) - throws IOException - { - // Use model size and camera angles to draw path for base rectangle (using 3d transform) - int[] coords1 = convertCoordinates(-1.0, -1.0, 0); - int[] coords2 = convertCoordinates(1.0, -1.0, 0); - int[] coords3 = convertCoordinates(1.0, 1.0, 0); - int[] coords4 = convertCoordinates(-1.0, 1.0, 0); - final String corners = "M " + coords1[0] + "," + coords1[1] - + " L " + coords2[0] + "," + coords2[1] - + " L " + coords3[0] + "," + coords3[1] - + " L " + coords4[0] + "," + coords4[1] + " z"; - inWriter.write(""); - inWriter.write(inLineSeparator); - } - - /** - * Write the cardinal letters NESW - * @param inWriter Writer to use for writing file - * @param inLineSeparator line separator to use - * @throws IOException on file writing error - */ - private void writeCardinals(FileWriter inWriter, String inLineSeparator) - throws IOException - { - // Use model size and camera angles to calculate positions - int[] coordsN = convertCoordinates(0, 1.0, 0); - writeCardinal(inWriter, coordsN[0], coordsN[1], "cardinal.n", inLineSeparator); - int[] coordsE = convertCoordinates(1.0, 0, 0); - writeCardinal(inWriter, coordsE[0], coordsE[1], "cardinal.e", inLineSeparator); - int[] coordsS = convertCoordinates(0, -1.0, 0); - writeCardinal(inWriter, coordsS[0], coordsS[1], "cardinal.s", inLineSeparator); - int[] coordsW = convertCoordinates(-1.0, 0, 0); - writeCardinal(inWriter, coordsW[0], coordsW[1], "cardinal.w", inLineSeparator); - } - - /** - * Write a single cardinal letter - * @param inWriter Writer to use for writing file - * @param inX x coordinate - * @param inY y coordinate - * @param inKey key for string to write - * @param inLineSeparator line separator to use - * @throws IOException on file writing error - */ - private static void writeCardinal(FileWriter inWriter, int inX, int inY, String inKey, String inLineSeparator) - throws IOException - { - inWriter.write(""); - inWriter.write(I18nManager.getText(inKey)); - inWriter.write(""); - inWriter.write(inLineSeparator); - } - - /** - * Convert the given 3d coordinates into 2d coordinates by rotating and mapping - * @param inX x coordinate (east) - * @param inY y coordinate (north) - * @param inZ z coordinate (up) - * @return 2d coordinates as integer array - */ - private int[] convertCoordinates(double inX, double inY, double inZ) - { - // Rotate by phi degrees around vertical axis - final double cosPhi = Math.cos(Math.toRadians(_phi)); - final double sinPhi = Math.sin(Math.toRadians(_phi)); - final double x2 = inX * cosPhi + inY * sinPhi; - final double y2 = inY * cosPhi - inX * sinPhi; - final double z2 = inZ; - // Rotate by theta degrees around horizontal axis - final double cosTheta = Math.cos(Math.toRadians(_theta)); - final double sinTheta = Math.sin(Math.toRadians(_theta)); - double x3 = x2; - double y3 = y2 * sinTheta + z2 * cosTheta; - // don't need to calculate z3 - // Scale results to sensible scale for svg - x3 = x3 * _scaleFactor + 400; - y3 = -y3 * _scaleFactor + 350; - return new int[] {(int) x3, (int) y3}; - } - - /** - * Finish off the file by closing the tags - * @param inWriter Writer to use for writing file - * @param inLineSeparator line separator to use - * @throws IOException on file writing error - */ - private static void writeEndOfFile(FileWriter inWriter, String inLineSeparator) - throws IOException - { - inWriter.write(inLineSeparator); - inWriter.write(""); - inWriter.write(inLineSeparator); - } - - /** - * Write out all the data points to the file in the balls-and-sticks style - * @param inWriter Writer to use for writing file - * @param inModel model object for getting data points - * @param inUseGradients true to use gradients, false for flat fills - * @param inLineSeparator line separator to use - * @throws IOException on file writing error - */ - private void writeDataPoints(FileWriter inWriter, ThreeDModel inModel, boolean inUseGradients, - String inLineSeparator) - throws IOException - { - final int numPoints = inModel.getNumPoints(); - TreeSet fragments = new TreeSet(); - for (int i=0; i 0.0) - { - int[] baseCoords = convertCoordinates(inModel.getScaledHorizValue(i), inModel.getScaledVertValue(i), 0); - builder.append(""); - builder.append(inLineSeparator); - } - // ball (different according to type) - if (inModel.getPointType(i) == ThreeDModel.POINT_TYPE_WAYPOINT) - { - // waypoint ball - builder.append(""); - } - else - { - // normal track point ball - builder.append(""); - } - builder.append(inLineSeparator); - // add to set - fragments.add(new SvgFragment(builder.toString(), coords[1])); - } - - // Iterate over the sorted set and write to file - Iterator iterator = fragments.iterator(); - while (iterator.hasNext()) { - inWriter.write(iterator.next().getFragment()); - } - } - - - /** - * Check the given angle value - * @param inString String entered by user - * @return validated value - */ - private static double checkAngle(String inString) - { - double value = 0.0; - try { - value = Double.parseDouble(inString); - } - catch (Exception e) {} // ignore parse failures - return value; - } -} diff --git a/tim/prune/save/SvgFragment.java b/tim/prune/save/SvgFragment.java deleted file mode 100644 index 0d9c5d4..0000000 --- a/tim/prune/save/SvgFragment.java +++ /dev/null @@ -1,56 +0,0 @@ -package tim.prune.save; - -/** - * Class to enable the sorting of Svg fragments - */ -public class SvgFragment implements Comparable -{ - private String _fragment = null; - private int _yCoord = 0; - - /** - * Constructor - * @param inFragment fragment of svg source - * @param inYCoord y coordinate of point, for sorting - */ - public SvgFragment(String inFragment, int inYCoord) - { - _fragment = inFragment; - _yCoord = inYCoord; - } - - /** - * @return svg fragment - */ - public String getFragment() - { - return _fragment; - } - - /** - * Compare method - */ - public int compareTo(SvgFragment inOther) - { - int ycompare = _yCoord - inOther._yCoord; - if (ycompare != 0) {return ycompare;} - return _fragment.compareTo(inOther._fragment); - } - - /** - * @param inOther other fragment to compare this one with - * @return true if the fragments are equal - */ - public boolean equals(SvgFragment inOther) - { - return _fragment.equals(inOther._fragment); - } - - /** - * @param inOther other object to compare this one with - * @return true if the objects are equal - */ - public boolean equals(Object inOther) { - return (inOther instanceof SvgFragment?equals((SvgFragment) inOther):false); - } -} diff --git a/tim/prune/threedee/Java3DWindow.java b/tim/prune/threedee/Java3DWindow.java index fdda0fc..579245d 100644 --- a/tim/prune/threedee/Java3DWindow.java +++ b/tim/prune/threedee/Java3DWindow.java @@ -227,17 +227,6 @@ public class Java3DWindow implements ThreeDWindow } }}); panel.add(povButton); - // Add button for exporting svg - JButton svgButton = new JButton(I18nManager.getText("function.exportsvg")); - svgButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - if (_orbit != null) { - callbackRender(FunctionLibrary.FUNCTION_SVGEXPORT); - } - }}); - panel.add(svgButton); - // Close button JButton closeButton = new JButton(I18nManager.getText("button.close")); closeButton.addActionListener(new ActionListener() @@ -614,7 +603,7 @@ public class Java3DWindow implements ThreeDWindow Appearance tAppearance = new Appearance(); if (inBaseImage != null) { - gi.setTextureCoordinateParams(1, 2); // one coord set of two dimensions + gi.setTextureCoordinateParams(1, 2); // one coord set of two dimensions gi.setTextureCoordinates(0, inHelper.getTextureCoordinates()); Texture mapImage = new TextureLoader(inBaseImage.getImage()).getTexture(); tAppearance.setTexture(mapImage); @@ -641,7 +630,7 @@ public class Java3DWindow implements ThreeDWindow /** * Calculate the angles and call them back to the app - * @param inFunction function to call (either pov or svg) + * @param inFunction function to call for export */ private void callbackRender(Export3dFunction inFunction) { @@ -664,8 +653,8 @@ public class Java3DWindow implements ThreeDWindow // Give the settings to the rendering function inFunction.setCameraCoordinates(result.x, result.y, result.z); inFunction.setAltitudeExaggeration(_altFactor); - inFunction.setTerrainDefinition(_terrainDefinition); // ignored by svg, used by pov - inFunction.setImageDefinition(_imageDefinition); // ignored by svg, used by pov + inFunction.setTerrainDefinition(_terrainDefinition); + inFunction.setImageDefinition(_imageDefinition); inFunction.begin(); } diff --git a/tim/prune/undo/UndoAddTimeOffset.java b/tim/prune/undo/UndoAddTimeOffset.java index 5238213..0a50699 100644 --- a/tim/prune/undo/UndoAddTimeOffset.java +++ b/tim/prune/undo/UndoAddTimeOffset.java @@ -11,7 +11,7 @@ public class UndoAddTimeOffset implements UndoOperation { /** Start and end indices of section */ private int _startIndex, _endIndex; - /** time offset */ + /** time offset in seconds */ private long _timeOffset; @@ -19,7 +19,7 @@ public class UndoAddTimeOffset implements UndoOperation * Constructor * @param inStart start index of section * @param inEnd end index of section - * @param inOffset time offset + * @param inOffset time offset in seconds */ public UndoAddTimeOffset(int inStart, int inEnd, long inOffset) { @@ -45,7 +45,7 @@ public class UndoAddTimeOffset implements UndoOperation public void performUndo(TrackInfo inTrackInfo) throws UndoException { // Perform the inverse operation - inTrackInfo.getTrack().addTimeOffset(_startIndex, _endIndex, -_timeOffset, true); + inTrackInfo.getTrack().addTimeOffsetSeconds(_startIndex, _endIndex, -_timeOffset, true); UpdateMessageBroker.informSubscribers(); } } diff --git a/tim/prune/undo/UndoAppendPoints.java b/tim/prune/undo/UndoAppendPoints.java new file mode 100644 index 0000000..14e147b --- /dev/null +++ b/tim/prune/undo/UndoAppendPoints.java @@ -0,0 +1,51 @@ +package tim.prune.undo; + +import tim.prune.I18nManager; +import tim.prune.data.TrackInfo; + +/** + * Operation to undo an operation (such as create marker waypoints) + * in which a number of points were appended to the track + */ +public class UndoAppendPoints implements UndoOperation +{ + private int _previousTrackLength = -1; + private int _numAppended = 0; + + + /** + * Constructor + */ + public UndoAppendPoints(int inTrackLength) + { + _previousTrackLength = inTrackLength; + } + + /** + * @param inNumPoints number of points appended to track + */ + public void setNumPointsAppended(int inNumPoints) + { + _numAppended = inNumPoints; + } + + /** + * @return description of operation including number of points loaded + */ + public String getDescription() + { + return I18nManager.getText("undo.insert") + " (" + _numAppended + ")"; + } + + /** + * Perform the undo operation on the given Track + * @param inTrackInfo TrackInfo object on which to perform the operation + */ + public void performUndo(TrackInfo inTrackInfo) throws UndoException + { + // crop track to previous size + inTrackInfo.getTrack().cropTo(_previousTrackLength); + // clear selection + inTrackInfo.getSelection().clearAll(); + } +} -- 2.43.0