]> gitweb.fperrin.net Git - GpsPrune.git/commitdiff
Version 20.4, May 2021 master
authoractivityworkshop <mail@activityworkshop.net>
Mon, 10 May 2021 17:41:33 +0000 (19:41 +0200)
committeractivityworkshop <mail@activityworkshop.net>
Mon, 10 May 2021 17:41:33 +0000 (19:41 +0200)
139 files changed:
.gitignore [new file with mode: 0644]
README.md
buildtools/build.sh
buildtools/pom.xml
buildtools/version.properties
src/tim/prune/App.java
src/tim/prune/FunctionLibrary.java
src/tim/prune/GpsPrune.java
src/tim/prune/I18nManager.java
src/tim/prune/config/Config.java
src/tim/prune/copyright.txt
src/tim/prune/data/Coordinate.java
src/tim/prune/data/Field.java
src/tim/prune/data/FileInfo.java
src/tim/prune/data/RangeStats.java
src/tim/prune/data/RangeStatsWithGradients.java [new file with mode: 0644]
src/tim/prune/data/Selection.java
src/tim/prune/data/Timestamp.java
src/tim/prune/function/AboutScreen.java
src/tim/prune/function/AddAltitudeOffset.java
src/tim/prune/function/AddTimeOffset.java
src/tim/prune/function/ConnectToPointFunction.java
src/tim/prune/function/CreateMarkerWaypointsFunction.java
src/tim/prune/function/DiskCacheConfig.java
src/tim/prune/function/DistanceTimeLimitFunction.java
src/tim/prune/function/DuplicatePoint.java
src/tim/prune/function/FullRangeDetails.java [deleted file]
src/tim/prune/function/InterpolateFunction.java
src/tim/prune/function/PasteCoordinateList.java [new file with mode: 0644]
src/tim/prune/function/PasteCoordinates.java
src/tim/prune/function/PlayAudioFunction.java
src/tim/prune/function/PlusCodeFunction.java [new file with mode: 0644]
src/tim/prune/function/ProjectPoint.java [new file with mode: 0644]
src/tim/prune/function/RearrangeWaypointsFunction.java
src/tim/prune/function/SearchOpenCachingDeFunction.java
src/tim/prune/function/ShowFullDetails.java [new file with mode: 0644]
src/tim/prune/function/compress/CompressTrackFunction.java
src/tim/prune/function/compress/SingleParameterAlgorithm.java
src/tim/prune/function/distance/DistanceFunction.java
src/tim/prune/function/distance/DistanceTableModel.java
src/tim/prune/function/estimate/EstimateTime.java
src/tim/prune/function/estimate/EstimationParameters.java
src/tim/prune/function/estimate/LearnParameters.java
src/tim/prune/function/gpsies/FormPoster.java [deleted file]
src/tim/prune/function/gpsies/GetGpsiesFunction.java [deleted file]
src/tim/prune/function/gpsies/GpsiesXmlHandler.java [deleted file]
src/tim/prune/function/gpsies/UploadGpsiesFunction.java [deleted file]
src/tim/prune/function/olc/CoordPair.java [new file with mode: 0644]
src/tim/prune/function/olc/OlcArea.java [new file with mode: 0644]
src/tim/prune/function/olc/OlcDecoder.java [new file with mode: 0644]
src/tim/prune/function/search/GenericDownloaderFunction.java
src/tim/prune/function/search/SearchResult.java
src/tim/prune/function/search/TrackListModel.java [moved from src/tim/prune/function/gpsies/TrackListModel.java with 95% similarity]
src/tim/prune/function/search/wikimedia_galleries.txt
src/tim/prune/function/settings/SaveConfig.java
src/tim/prune/function/settings/SetDisplaySettings.java
src/tim/prune/function/settings/SetLanguage.java
src/tim/prune/function/settings/SetPathsFunction.java
src/tim/prune/function/sew/SplitSegmentsFunction.java
src/tim/prune/function/srtm/DownloadSrtmFunction.java [deleted file]
src/tim/prune/function/srtm/LookupSrtmFunction.java
src/tim/prune/function/srtm/SrtmTile.java
src/tim/prune/function/srtm/TileDownloader.java [new file with mode: 0644]
src/tim/prune/function/srtm/TileFinder.java
src/tim/prune/gui/CoordDisplay.java [new file with mode: 0644]
src/tim/prune/gui/DetailsDisplay.java
src/tim/prune/gui/DisplayUtils.java
src/tim/prune/gui/MenuManager.java
src/tim/prune/gui/StatusBar.java
src/tim/prune/gui/Viewport.java
src/tim/prune/gui/colour/ColourerFactory.java
src/tim/prune/gui/colour/FileColourer.java
src/tim/prune/gui/images/wpicon_ring_l.png
src/tim/prune/gui/images/wpicon_ring_m.png
src/tim/prune/gui/map/CacheFailure.java [new file with mode: 0644]
src/tim/prune/gui/map/DiskTileCacher.java
src/tim/prune/gui/map/MapCanvas.java
src/tim/prune/gui/map/MapPosition.java
src/tim/prune/gui/map/MapSource.java
src/tim/prune/gui/map/MapSourceLibrary.java
src/tim/prune/gui/map/MapTileManager.java
src/tim/prune/gui/map/MffMapSource.java
src/tim/prune/gui/map/OsmMapSource.java
src/tim/prune/gui/map/SiteNameUtils.java [new file with mode: 0644]
src/tim/prune/gui/map/TileConsumer.java
src/tim/prune/gui/map/TileDownloader.java
src/tim/prune/gui/profile/ProfileChart.java
src/tim/prune/jpeg/drew/ExifTiffHandler.java
src/tim/prune/lang/prune-texts_af.properties
src/tim/prune/lang/prune-texts_cy.properties [new file with mode: 0644]
src/tim/prune/lang/prune-texts_cz.properties
src/tim/prune/lang/prune-texts_da.properties
src/tim/prune/lang/prune-texts_de.properties
src/tim/prune/lang/prune-texts_de_CH.properties
src/tim/prune/lang/prune-texts_en.properties
src/tim/prune/lang/prune-texts_en_US.properties
src/tim/prune/lang/prune-texts_es.properties
src/tim/prune/lang/prune-texts_fi.properties
src/tim/prune/lang/prune-texts_fr.properties
src/tim/prune/lang/prune-texts_hu.properties
src/tim/prune/lang/prune-texts_it.properties
src/tim/prune/lang/prune-texts_ja.properties
src/tim/prune/lang/prune-texts_ko.properties
src/tim/prune/lang/prune-texts_nl.properties
src/tim/prune/lang/prune-texts_no.properties
src/tim/prune/lang/prune-texts_pl.properties
src/tim/prune/lang/prune-texts_pt.properties
src/tim/prune/lang/prune-texts_ro.properties
src/tim/prune/lang/prune-texts_ru.properties
src/tim/prune/lang/prune-texts_sv.properties
src/tim/prune/lang/prune-texts_tr.properties
src/tim/prune/lang/prune-texts_uk.properties
src/tim/prune/lang/prune-texts_zh.properties
src/tim/prune/load/BabelFileFormats.java
src/tim/prune/load/ContentCacher.java [new file with mode: 0644]
src/tim/prune/load/FileCacher.java
src/tim/prune/load/FileLoader.java
src/tim/prune/load/FileSplitter.java
src/tim/prune/load/JpegLoader.java
src/tim/prune/load/TextCacher.java [new file with mode: 0644]
src/tim/prune/load/TextFileLoader.java
src/tim/prune/load/json/JsonBlock.java [new file with mode: 0644]
src/tim/prune/load/json/JsonFileLoader.java [new file with mode: 0644]
src/tim/prune/load/json/JsonPoint.java [new file with mode: 0644]
src/tim/prune/load/xml/GpxHandler.java
src/tim/prune/readme.txt
src/tim/prune/save/BaseImageConfigDialog.java
src/tim/prune/save/GpxExporter.java
src/tim/prune/save/ImageExporter.java
src/tim/prune/save/MapGrouter.java
src/tim/prune/threedee/Java3DWindow.java
src/tim/prune/threedee/UprightOrbiter.java [new file with mode: 0644]
test/tim/prune/data/RangeStatsTest.java [new file with mode: 0644]
test/tim/prune/function/cache/TileSetTest.java [new file with mode: 0644]
test/tim/prune/function/olc/OlcDecoderTest.java [new file with mode: 0644]
test/tim/prune/function/weather/SingleForecastTest.java [new file with mode: 0644]
test/tim/prune/gui/map/MapSourceTest.java [new file with mode: 0644]
test/tim/prune/gui/map/SiteNameUtilsTest.java [new file with mode: 0644]
test/tim/prune/jpeg/drew/RationalTest.java [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a57e45f
--- /dev/null
@@ -0,0 +1,3 @@
+*.class
+*.jar
+/bin/
index 9e9c08b39e92118fefa1561f332b4add6f6b7d54..169f8ee169ed5b88f0435a06c45fdbd70efd83ad 100644 (file)
--- 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 19.2, 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.
+Here on github you'll find all the sources from version 1 to the current version 20.4, 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 missing French texts and Polish texts are online, to see whether it's a workable idea or not.  Please help with these if you can.
index b38e2ce60f042c9a31274ee4a6dfae683219843f..f4f00f9e4fd17d9ad409629b13fcf594ddcc8b5c 100644 (file)
@@ -1,6 +1,7 @@
 # Build script
+set -e
 # Version number
-PRUNENAME=gpsprune_19.2
+PRUNENAME=gpsprune_20.4
 # remove compile directory
 rm -rf compile
 # remove dist directory
index 82b29ac8a8969475038b95377cacefb182b26d52..b079793cd51a16cb2eda1327760d61aff09fc16c 100644 (file)
@@ -7,7 +7,7 @@
 
        <groupId>tim.prune</groupId>
        <artifactId>gpsprune</artifactId>
-       <version>19.2</version>
+       <version>20.4</version>
        <packaging>jar</packaging>
 
        <name>tim.prune.gpsprune</name>
                        <artifactId>j3dutils</artifactId>
                        <version>${j3dutils.version}</version>
                </dependency>
+               <dependency>
+                 <groupId>org.junit.jupiter</groupId>
+                 <artifactId>junit-jupiter-engine</artifactId>
+                 <version>5.7.1</version>
+                 <scope>test</scope>
+               </dependency>
+               <dependency>
+                 <groupId>org.junit.jupiter</groupId>
+                 <artifactId>junit-jupiter-api</artifactId>
+                 <version>5.7.1</version>
+                 <scope>test</scope>
+               </dependency>
        </dependencies>
 
        <build>
                <outputDirectory>${project.build.directory}/classes</outputDirectory>
                <finalName>${project.artifactId}_${project.version}</finalName>
-               <sourceDirectory>${project.basedir}/</sourceDirectory>
+               <sourceDirectory>${project.basedir}/src</sourceDirectory>
+               <testSourceDirectory>${project.basedir}/test</testSourceDirectory>
                <resources>
                        <resource>
                                <directory>${project.basedir}/src/</directory>
                                <plugin>
                                        <artifactId>maven-compiler-plugin</artifactId>
                                        <version>3.8.0</version>
+                                       <configuration>
+                                         <compilerArgs>
+                                           <arg>-Xlint:deprecation</arg>
+                                         </compilerArgs>
+                                       </configuration>
                                </plugin>
                                <plugin>
                                        <artifactId>maven-jar-plugin</artifactId>
                                                <mainClass>${app.mainClass}</mainClass>
                                        </configuration>
                                </plugin>
-
+                               <plugin>
+                                 <artifactId>maven-surefire-plugin</artifactId>
+                                 <version>2.22.2</version>
+                               </plugin>
+                               <plugin>
+                                 <artifactId>maven-failsafe-plugin</artifactId>
+                                 <version>2.22.2</version>
+                               </plugin>
                        </plugins>
                </pluginManagement>
        </build>
index af259261217d8ce2749853a802e4de6ec4f2547e..15c4a262e4ffad7804e90903a7345fdabb6d3218 100644 (file)
@@ -1 +1 @@
-version=19.2
+version=20.4
index 3a778588ab815a7f30c4b396e4de9d2c266836da..22fa4865b8ddb9ab5b7b3663a4ed2c4ce9c46587 100644 (file)
@@ -69,7 +69,7 @@ public class App
        private AppMode _appMode = AppMode.NORMAL;
 
        /** Enum for the app mode - currently only two options but may expand later */
-       public enum AppMode {NORMAL, DRAWRECT};
+       public enum AppMode {NORMAL, DRAWRECT}
 
 
        /**
@@ -649,7 +649,8 @@ public class App
                loadedTrack.load(inFieldArray, inDataArray, inOptions);
                if (loadedTrack.getNumPoints() <= 0)
                {
-                       showErrorMessage("error.load.dialogtitle", "error.load.nopoints");
+                       String msgKey = (inSourceInfo == null ? "error.load.nopointsintext" : "error.load.nopoints");
+                       showErrorMessage("error.load.dialogtitle", msgKey);
                        // load next file if there's a queue
                        loadNextFile();
                        return;
@@ -715,9 +716,12 @@ public class App
                                undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios());
                                _undoStack.add(undo);
                                _track.combine(inLoadedTrack);
-                               // set source information
-                               inSourceInfo.populatePointObjects(_track, inLoadedTrack.getNumPoints());
-                               _trackInfo.getFileInfo().addSource(inSourceInfo);
+                               if (inSourceInfo != null)
+                               {
+                                       // set source information
+                                       inSourceInfo.populatePointObjects(_track, inLoadedTrack.getNumPoints());
+                                       _trackInfo.getFileInfo().addSource(inSourceInfo);
+                               }
                        }
                        else if (answer == JOptionPane.NO_OPTION)
                        {
@@ -729,11 +733,15 @@ public class App
                                UndoLoad undo = new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), photos);
                                undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios());
                                _undoStack.add(undo);
-                               _lastSavePosition = _undoStack.size();
                                _trackInfo.getSelection().clearAll();
                                _track.load(inLoadedTrack);
-                               inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
-                               _trackInfo.getFileInfo().replaceSource(inSourceInfo);
+                               if (inSourceInfo != null)
+                               {
+                                       _lastSavePosition = _undoStack.size();
+                                       // set source information
+                                       inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
+                                       _trackInfo.getFileInfo().replaceSource(inSourceInfo);
+                               }
                                _trackInfo.getPhotoList().removeCorrelatedPhotos();
                                _trackInfo.getAudioList().removeCorrelatedAudios();
                        }
@@ -744,19 +752,25 @@ public class App
                        UndoLoad undo = new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), null);
                        undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios());
                        _undoStack.add(undo);
-                       _lastSavePosition = _undoStack.size();
                        _trackInfo.getSelection().clearAll();
                        _track.load(inLoadedTrack);
-                       inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
-                       _trackInfo.getFileInfo().addSource(inSourceInfo);
+                       if (inSourceInfo != null)
+                       {
+                               _lastSavePosition = _undoStack.size();
+                               inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
+                               _trackInfo.getFileInfo().addSource(inSourceInfo);
+                       }
                }
                // Update config before subscribers are told
-               boolean isRegularLoad = (inSourceInfo.getFileType() != FILE_TYPE.GPSBABEL);
-               Config.getRecentFileList().addFile(new RecentFile(inSourceInfo.getFile(), isRegularLoad));
+               if (inSourceInfo != null)
+               {
+                       boolean isRegularLoad = (inSourceInfo.getFileType() != FILE_TYPE.GPSBABEL);
+                       Config.getRecentFileList().addFile(new RecentFile(inSourceInfo.getFile(), isRegularLoad));
+                       // Update status bar
+                       UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.loadfile")
+                               + " '" + inSourceInfo.getName() + "'");
+               }
                UpdateMessageBroker.informSubscribers();
-               // Update status bar
-               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.loadfile")
-                       + " '" + inSourceInfo.getName() + "'");
                // update menu
                _menuManager.informFileLoaded();
                // Remove busy lock
index 0441826164d930de8b79acb7d45fd84a742d33ce..a1cb9edc6addb71fb8505f50b7e65f04b28ae685 100644 (file)
@@ -18,12 +18,12 @@ 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.ProjectPoint;
+import tim.prune.function.ShowFullDetails;
 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;
@@ -50,8 +50,6 @@ import tim.prune.function.distance.DistanceFunction;
 import tim.prune.function.edit.PointNameEditor;
 import tim.prune.function.estimate.EstimateTime;
 import tim.prune.function.estimate.LearnParameters;
-import tim.prune.function.gpsies.GetGpsiesFunction;
-import tim.prune.function.gpsies.UploadGpsiesFunction;
 import tim.prune.function.settings.SaveConfig;
 import tim.prune.function.settings.SetAltitudeTolerance;
 import tim.prune.function.settings.SetColours;
@@ -61,7 +59,6 @@ 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;
 import tim.prune.function.srtm.LookupSrtmFunction;
 import tim.prune.function.weather.GetWeatherForecastFunction;
 import tim.prune.load.AudioLoader;
@@ -88,6 +85,7 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_IMPORTBABEL = null;
        public static GenericFunction FUNCTION_SAVECONFIG  = null;
        public static GenericFunction FUNCTION_EDIT_WAYPOINT_NAME = null;
+       public static GenericFunction FUNCTION_PROJECT_POINT = null;
        public static GenericFunction FUNCTION_REARRANGE_WAYPOINTS = null;
        public static GenericFunction FUNCTION_SELECT_SEGMENT = null;
        public static GenericFunction FUNCTION_SPLIT_SEGMENTS = null;
@@ -103,7 +101,6 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_DELETE_BY_DATE = null;
        public static SingleNumericParameterFunction FUNCTION_INTERPOLATE = null;
        public static GenericFunction FUNCTION_LOOKUP_SRTM = null;
-       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;
@@ -112,7 +109,6 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_ADD_ALTITUDE_OFFSET  = null;
        public static GenericFunction FUNCTION_CONVERT_NAMES_TO_TIMES  = null;
        public static GenericFunction FUNCTION_DELETE_FIELD_VALUES  = null;
-       public static GenericFunction FUNCTION_PASTE_COORDINATES = null;
        public static GenericFunction FUNCTION_FIND_WAYPOINT = null;
        public static GenericFunction FUNCTION_DUPLICATE_POINT = null;
        public static GenericFunction FUNCTION_CONNECT_TO_POINT = null;
@@ -127,12 +123,10 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_CHARTS = null;
        public static GenericFunction FUNCTION_3D     = null;
        public static GenericFunction FUNCTION_DISTANCES  = null;
-       public static GenericFunction FUNCTION_FULL_RANGE_DETAILS = null;
+       public static GenericFunction FUNCTION_FULL_DETAILS = null;
        public static GenericFunction FUNCTION_AUTOPLAY_TRACK = null;
        public static GenericFunction FUNCTION_ESTIMATE_TIME = null;
        public static GenericFunction FUNCTION_LEARN_ESTIMATION_PARAMS = null;
-       public static GenericFunction FUNCTION_GET_GPSIES = null;
-       public static GenericFunction FUNCTION_UPLOAD_GPSIES = null;
        public static GenericFunction FUNCTION_GET_WEATHER_FORECAST = null;
        public static GenericFunction FUNCTION_LOAD_AUDIO = null;
        public static GenericFunction FUNCTION_REMOVE_AUDIO = null;
@@ -168,6 +162,7 @@ public abstract class FunctionLibrary
                FUNCTION_IMPORTBABEL = new BabelLoadFromFile(inApp);
                FUNCTION_SAVECONFIG = new SaveConfig(inApp);
                FUNCTION_EDIT_WAYPOINT_NAME = new PointNameEditor(inApp);
+               FUNCTION_PROJECT_POINT = new ProjectPoint(inApp);
                FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp);
                FUNCTION_SELECT_SEGMENT = new SelectSegmentFunction(inApp);
                FUNCTION_SPLIT_SEGMENTS = new SplitSegmentsFunction(inApp);
@@ -183,7 +178,6 @@ public abstract class FunctionLibrary
                FUNCTION_DELETE_BY_DATE = new DeleteByDateFunction(inApp);
                FUNCTION_INTERPOLATE = new InterpolateFunction(inApp);
                FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp);
-               FUNCTION_DOWNLOAD_SRTM = new DownloadSrtmFunction(inApp);
                FUNCTION_NEARBY_WIKIPEDIA = new GetWikipediaFunction(inApp);
                FUNCTION_SEARCH_WIKIPEDIA = new SearchWikipediaNames(inApp);
                FUNCTION_SEARCH_OSMPOIS = new SearchOsmPoisFunction(inApp);
@@ -192,7 +186,6 @@ public abstract class FunctionLibrary
                FUNCTION_ADD_ALTITUDE_OFFSET = new AddAltitudeOffset(inApp);
                FUNCTION_CONVERT_NAMES_TO_TIMES = new ConvertNamesToTimes(inApp);
                FUNCTION_DELETE_FIELD_VALUES = new DeleteFieldValues(inApp);
-               FUNCTION_PASTE_COORDINATES = new PasteCoordinates(inApp);
                FUNCTION_FIND_WAYPOINT = new FindWaypoint(inApp);
                FUNCTION_DUPLICATE_POINT = new DuplicatePoint(inApp);
                FUNCTION_CONNECT_TO_POINT = new ConnectToPointFunction(inApp);
@@ -206,12 +199,10 @@ public abstract class FunctionLibrary
                FUNCTION_CHARTS = new Charter(inApp);
                FUNCTION_3D     = new ShowThreeDFunction(inApp);
                FUNCTION_DISTANCES = new DistanceFunction(inApp);
-               FUNCTION_FULL_RANGE_DETAILS = new FullRangeDetails(inApp);
+               FUNCTION_FULL_DETAILS = new ShowFullDetails(inApp);
                FUNCTION_AUTOPLAY_TRACK = new AutoplayFunction(inApp);
                FUNCTION_ESTIMATE_TIME = new EstimateTime(inApp);
                FUNCTION_LEARN_ESTIMATION_PARAMS = new LearnParameters(inApp);
-               FUNCTION_GET_GPSIES = new GetGpsiesFunction(inApp);
-               FUNCTION_UPLOAD_GPSIES = new UploadGpsiesFunction(inApp);
                FUNCTION_GET_WEATHER_FORECAST = new GetWeatherForecastFunction(inApp);
                FUNCTION_LOAD_AUDIO = new AudioLoader(inApp);
                FUNCTION_REMOVE_AUDIO = new RemoveAudioFunction(inApp);
index f396e9e4fdbc31a4c1ca2deb67cbe24eff7c6b76..6644642cc2168172a78c6bb68f16d724d1aea7a1 100644 (file)
@@ -9,9 +9,11 @@ import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Locale;
+
 import javax.swing.JFrame;
 import javax.swing.JSplitPane;
 import javax.swing.JToolBar;
+import javax.swing.UIManager;
 import javax.swing.WindowConstants;
 
 import tim.prune.config.Config;
@@ -29,16 +31,16 @@ import tim.prune.gui.profile.ProfileChart;
 /**
  * GpsPrune is a tool to visualize, edit, convert and prune GPS data
  * Please see the included readme.txt or https://activityworkshop.net
- * This software is copyright activityworkshop.net 2006-2018 and made available through the Gnu GPL version 2.
+ * This software is copyright activityworkshop.net 2006-2021 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 = "19.2";
+       public static final String VERSION_NUMBER = "20.4";
        /** Build number, just used for about screen */
-       public static final String BUILD_NUMBER = "363d";
+       public static final String BUILD_NUMBER = "387";
        /** Static reference to App object */
        private static App APP = null;
 
@@ -149,6 +151,14 @@ public class GpsPrune
                                Config.setConfigString(Config.KEY_LANGUAGE_FILE, "");
                        }
                }
+
+               // Set look-and-feel
+               try {
+                       String windowStyle = Config.getConfigString(Config.KEY_WINDOW_STYLE);
+                       UIManager.setLookAndFeel(windowStyle);
+               }
+               catch (Exception e) {}
+
                // Set up the window and go
                launch(dataFiles);
        }
index fbf781eb3fef3a79adbdc72548eb5b3c66c83f91..a271f81b9fc5a579ddcce33397a8cb3fbc110ab7 100644 (file)
@@ -18,10 +18,10 @@ import java.util.ResourceBundle;
 public abstract class I18nManager
 {
        /** Properties object into which all the texts are copied */
-       private static Properties LocalTexts = null;
+       private static Properties _localTexts = null;
 
        /** External properties file for developer testing */
-       private static Properties ExternalPropsFile = null;
+       private static Properties _externalPropsFile = null;
 
 
        /**
@@ -33,7 +33,7 @@ public abstract class I18nManager
                final String BUNDLE_NAME = "tim.prune.lang.prune-texts";
                final Locale BACKUP_LOCALE = new Locale("en", "GB");
 
-               LocalTexts = new Properties();
+               _localTexts = new Properties();
                // Load English texts first to use as defaults
                loadFromBundle(ResourceBundle.getBundle(BUNDLE_NAME, BACKUP_LOCALE));
 
@@ -65,7 +65,7 @@ public abstract class I18nManager
                while (e.hasMoreElements())
                {
                        String key = e.nextElement();
-                       LocalTexts.setProperty(key, inBundle.getString(key));
+                       _localTexts.setProperty(key, inBundle.getString(key));
                }
        }
 
@@ -81,9 +81,9 @@ public abstract class I18nManager
                try
                {
                        File file = new File(inFilename);
-                       ExternalPropsFile = new Properties();
+                       _externalPropsFile = new Properties();
                        fis = new FileInputStream(file);
-                       ExternalPropsFile.load(fis);
+                       _externalPropsFile.load(fis);
                        fileLoaded = true; // everything worked
                }
                catch (IOException ioe) {}
@@ -103,17 +103,17 @@ public abstract class I18nManager
        public static String getText(String inKey)
        {
                // look in external props file if available
-               if (ExternalPropsFile != null)
+               if (_externalPropsFile != null)
                {
-                       String extText = ExternalPropsFile.getProperty(inKey);
+                       String extText = _externalPropsFile.getProperty(inKey);
                        if (extText != null) return extText;
                }
                // look in texts if available
-               if (LocalTexts != null)
+               if (_localTexts != null)
                {
                        try
                        {
-                               String localText = LocalTexts.getProperty(inKey);
+                               String localText = _localTexts.getProperty(inKey);
                                if (localText != null) return localText;
                        }
                        catch (MissingResourceException mre) {}
index 84e5fd7dc6b4a248bb1d2bef51f18be225f90425..a5226b37fe2f5cb272f683f975788b58928f167a 100644 (file)
@@ -55,6 +55,8 @@ public abstract class Config
        public static final String KEY_POVRAY_FONT = "prune.povrayfont";
        /** Key for the selected unit set */
        public static final String KEY_UNITSET_KEY  = "prune.unitsetkey";
+       /** Key for the selected coordinate display format */
+       public static final String KEY_COORD_DISPLAY_FORMAT  = "prune.coorddisplay";
        /** Key for index of map source */
        public static final String KEY_MAPSOURCE_INDEX = "prune.mapsource";
        /** Key for number of fixed map sources */
@@ -87,6 +89,8 @@ public abstract class Config
        public static final String KEY_ANTIALIAS = "prune.antialias";
        /** Key for kml track colour */
        public static final String KEY_KML_TRACK_COLOUR = "prune.kmltrackcolour";
+       /** Key for window style (name of look-and-feel) */
+       public static final String KEY_WINDOW_STYLE = "prune.windowstyle";
        /** Key for autosaving settings */
        public static final String KEY_AUTOSAVE_SETTINGS = "prune.autosavesettings";
        /** Key for recently used files */
@@ -105,6 +109,8 @@ public abstract class Config
        public static final String KEY_WAYPOINT_ICON_SIZE = "prune.waypointiconsize";
        /** Id of selected timezone */
        public static final String KEY_TIMEZONE_ID = "prune.timezoneid";
+       /** Last used latlon range */
+       public static final String KEY_LATLON_RANGE = "prune.latlonrange";
 
 
        /** Initialise the default properties */
@@ -170,6 +176,8 @@ public abstract class Config
                _unitSet = UnitSetLibrary.getUnitSet(_configValues.getProperty(KEY_UNITSET_KEY));
                // Adjust map source index if necessary
                adjustSelectedMap();
+               // Reset coord display format
+               setConfigInt(KEY_COORD_DISPLAY_FORMAT, 0);
 
                if (loadFailed) {
                        throw new ConfigException();
@@ -188,15 +196,16 @@ public abstract class Config
                props.put(KEY_GPS_DEVICE, "usb:");
                props.put(KEY_GPS_FORMAT, "garmin");
                props.put(KEY_POVRAY_FONT, "crystal.ttf"); // alternative: DejaVuSans-Bold.ttf
-               props.put(KEY_SHOW_MAP, "0"); // hide by default
+               props.put(KEY_SHOW_MAP, "1"); // show by default
                props.put(KEY_EXIFTOOL_PATH, "exiftool");
                props.put(KEY_GNUPLOT_PATH, "gnuplot");
                props.put(KEY_GPSBABEL_PATH, "gpsbabel");
                props.put(KEY_IMPORT_FILE_FORMAT, "-1"); // no file format selected
                props.put(KEY_KMZ_IMAGE_SIZE, "240");
                props.put(KEY_ANTIALIAS, "1"); // antialias on by default
-               props.put(KEY_AUTOSAVE_SETTINGS, "0"); // autosave false by default
+               props.put(KEY_AUTOSAVE_SETTINGS, "1"); // autosave by default
                props.put(KEY_UNITSET_KEY, "unitset.kilometres"); // metric by default
+               props.put(KEY_COORD_DISPLAY_FORMAT, "0"); // original
                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
@@ -246,6 +255,14 @@ public abstract class Config
                return _configFile;
        }
 
+       /**
+        * Set the file to which config was saved
+        */
+       public static void setConfigFile(File inFile)
+       {
+               _configFile = inFile;
+       }
+
        /**
         * @return config Properties object to allow all config values to be saved
         */
@@ -379,7 +396,7 @@ public abstract class Config
        public static void updatePointColourer(PointColourer inColourer)
        {
                _pointColourer = inColourer;
-               setConfigString(KEY_POINT_COLOURER, ColourerFactory.PointColourerToString(_pointColourer));
+               setConfigString(KEY_POINT_COLOURER, ColourerFactory.pointColourerToString(_pointColourer));
        }
 
        /**
index 3022774654f3f6b66501b143e2ca113bdc103eaa..76ebed3b59cefe58900fc1deb84d91c471e0009c 100644 (file)
@@ -1,4 +1,4 @@
-The source code of GpsPrune is copyright 2006-2018 activityworkshop.net
+The source code of GpsPrune is copyright 2006-2021 activityworkshop.net
 and is distributed under the terms of the Gnu GPL version 2.
 
 Portions of the package jpeg.drew were taken
@@ -6,4 +6,4 @@ 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
+in the source code and some preferred to remain anonymous.
index e30fa4ac357d89a68041fa07eee2f76bf5e8ee22..b668660236a5d164e710a4963a7b7c0ff64975fa 100644 (file)
@@ -475,4 +475,22 @@ public abstract class Coordinate
                return "Coord: " + _cardinal + " (" + _degrees + ") (" + _minutes + ") (" + _seconds + "."
                        + formatFraction(_fracs, _fracDenom) + ") = " + _asDouble;
        }
+
+       /**
+        * From a saved coordinate format display value, get the corresponding value to use
+        * @param inValue value from config
+        * @return coordinate format as int
+        */
+       public static int getCoordinateFormatForDisplay(int inValue)
+       {
+               switch(inValue)
+               {
+                       case FORMAT_DEG:
+                       case FORMAT_DEG_MIN:
+                       case FORMAT_DEG_MIN_SEC:
+                               return inValue;
+                       default:
+                               return FORMAT_NONE;
+               }
+       }
 }
index 7b9d77925c1f39fbd4a1d842d63539245fe93b5b..399d931b5397ddfe4ae43cbcbdbf0a5f33588cea 100644 (file)
@@ -18,6 +18,7 @@ public class Field
        public static final Field WAYPT_NAME = new Field("fieldname.waypointname", true);
        public static final Field WAYPT_TYPE = new Field("fieldname.waypointtype", true);
        public static final Field DESCRIPTION = new Field("fieldname.description", true);
+       public static final Field COMMENT = new Field("fieldname.comment", true);
        public static final Field NEW_SEGMENT = new Field("fieldname.newsegment", true);
 
        public static final Field SPEED          = new Field("fieldname.speed", true);
@@ -66,8 +67,9 @@ public class Field
         */
        public String getName()
        {
-               if (_labelKey != null)
+               if (_labelKey != null) {
                        return I18nManager.getText(_labelKey);
+               }
                return _customLabel;
        }
 
index 41900cb8736df60d668238306079256bf2dca175..0c4ee5fd4606624b8de9ce00af8f57a3284ccee1 100644 (file)
@@ -51,7 +51,9 @@ public class FileInfo
         */
        public void removeSource()
        {
-               _sources.remove(_sources.size()-1);
+               if (!_sources.isEmpty()) {
+                       _sources.remove(_sources.size()-1);
+               }
        }
 
        /**
index 13ecaade10d4e6a8d0eccc87a383762ae399cc00..b32f12fa987344a04f2a419e8cb68e7cfd210975 100644 (file)
 package tim.prune.data;
 
-import tim.prune.config.Config;
-
 /**
- * Class to do calculations of range statistics such as distances, durations,
- * speeds, gradients etc, and to hold the results of the calculations.
- * Used by FullRangeDetails as well as the EstimateTime functions.
+ * Class to do basic calculations of range statistics such as distances, durations,
+ * and altitude ranges, and to hold the results of the calculations.
  */
 public class RangeStats
 {
-       // MAYBE: Split into basic stats (quick to calculate, for detailsdisplay) and full stats (for other two)
-       private boolean _valid = false;
-       private int     _numPoints   = 0;
-       private int     _startIndex = 0, _endIndex = 0;
-       private int     _numSegments = 0;
-       private AltitudeRange _totalAltitudeRange = null, _movingAltitudeRange = null;
-       private AltitudeRange _gentleAltitudeRange = null, _steepAltitudeRange = null;
-       private Timestamp _earliestTimestamp = null, _latestTimestamp = null;
+       private int _numPoints   = 0;
+       private int _numSegments = 0;
+       private boolean _foundTrackPoint = false;
+       protected AltitudeRange _totalAltitudeRange = new AltitudeRange();
+       protected AltitudeRange _movingAltitudeRange = new AltitudeRange();
+       private Timestamp _earliestTimestamp = null, _latestTimestamp = null, _movingTimestamp = null;
        private long _movingMilliseconds = 0L;
        private boolean _timesIncomplete = false;
        private boolean _timesOutOfSequence = false;
-       private double _totalDistanceRads = 0.0, _movingDistanceRads = 0.0;
-       // Note, maximum speed is not calculated here, use the SpeedData class instead
+       protected double _totalDistanceRads = 0.0, _movingDistanceRads = 0.0;
+       protected DataPoint _prevPoint = null;
 
-       private static final double STEEP_ANGLE = 0.15; // gradient steeper than 15% counts as steep
 
+       /** Constructor */
+       public RangeStats()
+       {}
 
        /**
-        * Constructor
-        * @param inTrack track to compile data for
-        * @param inStartIndex start index of range to examine
-        * @param inEndIndex end index (inclusive) of range to examine
+        * Constructor giving Track
+        * @param inTrack track object to calculate with
         */
        public RangeStats(Track inTrack, int inStartIndex, int inEndIndex)
        {
-               if (inTrack != null && inStartIndex >= 0 && inEndIndex > inStartIndex
-                       && inEndIndex < inTrack.getNumPoints())
+               populateFromTrack(inTrack, inStartIndex, inEndIndex);
+       }
+
+       /**
+        * Add the specified points from the given track to the calculations
+        * @param inTrack track object
+        * @param inStartIndex start index (inclusive)
+        * @param inEndIndex end index (inclusive)
+        */
+       protected void populateFromTrack(Track inTrack, int inStartIndex, int inEndIndex)
+       {
+               for (int i=inStartIndex; i<=inEndIndex; i++)
                {
-                       _valid = calculateStats(inTrack, inStartIndex, inEndIndex);
+                       addPoint(inTrack.getPoint(i));
                }
        }
 
        /**
-        * Calculate the statistics and populate the member variables with the results
-        * @param inTrack track
-        * @param inStartIndex start index of range
-        * @param inEndIndex end index (inclusive) of range
-        * @return true on success
+        * @param inPoint point to add to the calculations
         */
-       private boolean calculateStats(Track inTrack, int inStartIndex, int inEndIndex)
+       public void addPoint(DataPoint inPoint)
        {
-               _startIndex = inStartIndex;
-               _endIndex = inEndIndex;
-               _numPoints = inEndIndex - inStartIndex + 1;
-               _totalAltitudeRange  = new AltitudeRange();
-               _movingAltitudeRange = new AltitudeRange();
-               _gentleAltitudeRange = new AltitudeRange();
-               _steepAltitudeRange  = new AltitudeRange();
-               DataPoint prevPoint = null;
-               Altitude prevAltitude = null;
-               _totalDistanceRads = _movingDistanceRads = 0.0;
-               double radsSinceLastAltitude = 0.0;
-               _movingMilliseconds = 0L;
-
-               // Loop over the points in the range
-               for (int i=inStartIndex; i<= inEndIndex; i++)
+               if (inPoint == null)
                {
-                       DataPoint p = inTrack.getPoint(i);
-                       if (p == null) return false;
-                       // ignore all waypoints
-                       if (p.isWaypoint()) continue;
+                       return;
+               }
+               _numPoints++;
+               // ignore all waypoints
+               if (inPoint.isWaypoint()) {
+                       return;
+               }
+               if (inPoint.getSegmentStart() || !_foundTrackPoint) {
+                       _numSegments++;
+               }
+               _foundTrackPoint = true;
+               // Get the distance to the previous track point
+               if (_prevPoint != null)
+               {
+                       double rads = DataPoint.calculateRadiansBetween(_prevPoint, inPoint);
+                       _totalDistanceRads += rads;
+                       if (!inPoint.getSegmentStart()) {
+                               _movingDistanceRads += rads;
+                       }
+               }
 
-                       if (p.getSegmentStart()) {
-                               _numSegments++;
+               // timestamps
+               if (inPoint.getSegmentStart())
+               {
+                       // reset movingTimestamp for moving time at the start
+                       // of each segment
+                       _movingTimestamp = null;
+               }
+               if (inPoint.hasTimestamp())
+               {
+                       Timestamp currTstamp = inPoint.getTimestamp();
+                       if (_earliestTimestamp == null || currTstamp.isBefore(_earliestTimestamp)) {
+                               _earliestTimestamp = currTstamp;
                        }
-                       // Get the distance to the previous track point
-                       if (prevPoint != null)
-                       {
-                               double rads = DataPoint.calculateRadiansBetween(prevPoint, p);
-                               _totalDistanceRads += rads;
-                               if (!p.getSegmentStart()) {
-                                       _movingDistanceRads += rads;
-                               }
-                               // Keep track of rads since last point with an altitude
-                               radsSinceLastAltitude += rads;
+                       if (_latestTimestamp == null || currTstamp.isAfter(_latestTimestamp)) {
+                               _latestTimestamp = currTstamp;
                        }
-                       // Get the altitude difference to the previous track point
-                       if (p.hasAltitude())
+
+                       // Work out duration without segment gaps
+                       if (_movingTimestamp != null)
                        {
-                               Altitude altitude = p.getAltitude();
-                               _totalAltitudeRange.addValue(altitude);
-                               if (p.getSegmentStart()) {
-                                       _movingAltitudeRange.ignoreValue(altitude);
+                               long millisLater = currTstamp.getMillisecondsSince(_movingTimestamp);
+                               if (millisLater < 0) {
+                                       _timesOutOfSequence = true;
                                }
-                               else
-                               {
-                                       _movingAltitudeRange.addValue(altitude);
-                                       if (prevAltitude != null)
-                                       {
-                                               // Work out gradient, see whether to ignore/add to gentle or steep
-                                               double heightDiff = altitude.getMetricValue() - prevAltitude.getMetricValue();
-                                               double metricDist = Distance.convertRadiansToDistance(radsSinceLastAltitude, UnitSetLibrary.UNITS_METRES);
-                                               final boolean isSteep = metricDist < 0.001 || (Math.abs(heightDiff / metricDist) > STEEP_ANGLE);
-                                               if (isSteep) {
-                                                       _steepAltitudeRange.ignoreValue(prevAltitude);
-                                                       _steepAltitudeRange.addValue(altitude);
-                                               }
-                                               else {
-                                                       _gentleAltitudeRange.ignoreValue(prevAltitude);
-                                                       _gentleAltitudeRange.addValue(altitude);
-                                               }
-                                       }
+                               else {
+                                       _movingMilliseconds += millisLater;
                                }
-                               prevAltitude = altitude;
-                               radsSinceLastAltitude = 0.0;
                        }
+                       _movingTimestamp = currTstamp;
+               }
+               else {
+                       _timesIncomplete = true;
+               }
 
-                       if (p.hasTimestamp())
-                       {
-                               if (_earliestTimestamp == null || p.getTimestamp().isBefore(_earliestTimestamp)) {
-                                       _earliestTimestamp = p.getTimestamp();
-                               }
-                               if (_latestTimestamp == null || p.getTimestamp().isAfter(_latestTimestamp)) {
-                                       _latestTimestamp = p.getTimestamp();
-                               }
-                               // Work out duration without segment gaps
-                               if (!p.getSegmentStart() && prevPoint != null && prevPoint.hasTimestamp())
-                               {
-                                       long millisLater = p.getTimestamp().getMillisecondsSince(prevPoint.getTimestamp());
-                                       if (millisLater < 0) {_timesOutOfSequence = true;}
-                                       else {
-                                               _movingMilliseconds += millisLater;
-                                       }
-                               }
+               // altitudes
+               if (inPoint.hasAltitude())
+               {
+                       Altitude altitude = inPoint.getAltitude();
+                       _totalAltitudeRange.addValue(altitude);
+                       if (inPoint.getSegmentStart()) {
+                               _movingAltitudeRange.ignoreValue(altitude);
                        }
-                       else {
-                               _timesIncomplete = true;
+                       else
+                       {
+                               _movingAltitudeRange.addValue(altitude);
                        }
-
-                       prevPoint = p;
                }
-               return true;
-       }
 
+               // allow child classes to do additional calculations
+               doFurtherCalculations(inPoint);
 
-       /** @return true if results are valid */
-       public boolean isValid() {
-               return _valid;
+               _prevPoint = inPoint;
        }
 
-       /** @return start index of range */
-       public int getStartIndex() {
-               return _startIndex;
+       /**
+        * Hook for subclasses to do what they want in addition
+        * @param inPoint incoming point
+        */
+       protected void doFurtherCalculations(DataPoint inPoint)
+       {
        }
 
-       /** @return end index of range */
-       public int getEndIndex() {
-               return _endIndex;
-       }
 
        /** @return number of points in range */
        public int getNumPoints() {
@@ -179,16 +157,6 @@ public class RangeStats
                return _movingAltitudeRange;
        }
 
-       /** @return altitude range of range just considering low gradient bits */
-       public AltitudeRange getGentleAltitudeRange() {
-               return _gentleAltitudeRange;
-       }
-
-       /** @return altitude range of range just considering high gradient bits */
-       public AltitudeRange getSteepAltitudeRange() {
-               return _steepAltitudeRange;
-       }
-
        /** @return the earliest timestamp found */
        public Timestamp getEarliestTimestamp() {
                return _earliestTimestamp;
@@ -239,42 +207,26 @@ public class RangeStats
                return Distance.convertRadiansToDistance(_movingDistanceRads, UnitSetLibrary.UNITS_KILOMETRES);
        }
 
-       /** @return the total gradient in % (including segment gaps) */
-       public double getTotalGradient()
-       {
-               double dist = Distance.convertRadiansToDistance(_totalDistanceRads, UnitSetLibrary.UNITS_METRES);
-               if (dist > 0.0 && _totalAltitudeRange.hasRange()) {
-                       return _totalAltitudeRange.getMetricHeightDiff() / dist * 100.0;
-               }
-               return 0.0;
-       }
-
-       /** @return the moving gradient in % (ignoring segment gaps) */
-       public double getMovingGradient()
-       {
-               double dist = Distance.convertRadiansToDistance(_movingDistanceRads, UnitSetLibrary.UNITS_METRES);
-               if (dist > 0.0 && _movingAltitudeRange.hasRange()) {
-                       return _movingAltitudeRange.getMetricHeightDiff() / dist * 100.0;
-               }
-               return 0.0;
-       }
-
-       /** @return the total vertical speed (including segment gaps) in current vspeed units */
+       /**
+        * @return the total vertical speed (including segment gaps) in metric units
+        */
        public double getTotalVerticalSpeed()
        {
                long time = getTotalDurationInSeconds();
                if (time > 0 && _totalAltitudeRange.hasRange()) {
-                       return _totalAltitudeRange.getMetricHeightDiff() / time * Config.getUnitSet().getVerticalSpeedUnit().getMultFactorFromStd();
+                       return _totalAltitudeRange.getMetricHeightDiff() / time;
                }
                return 0.0;
        }
 
-       /** @return the moving vertical speed (ignoring segment gaps) in current vspeed units */
+       /**
+        * @return the moving vertical speed (ignoring segment gaps) in metric units
+        */
        public double getMovingVerticalSpeed()
        {
                long time = getMovingDurationInSeconds();
                if (time > 0 && _movingAltitudeRange.hasRange()) {
-                       return _movingAltitudeRange.getMetricHeightDiff() / time * Config.getUnitSet().getVerticalSpeedUnit().getMultFactorFromStd();
+                       return _movingAltitudeRange.getMetricHeightDiff() / time;
                }
                return 0.0;
        }
diff --git a/src/tim/prune/data/RangeStatsWithGradients.java b/src/tim/prune/data/RangeStatsWithGradients.java
new file mode 100644 (file)
index 0000000..c495c51
--- /dev/null
@@ -0,0 +1,106 @@
+package tim.prune.data;
+
+/**
+ * Class to do additional range calculations including gradients
+ * Used by full details display as well as the EstimateTime functions.
+ */
+public class RangeStatsWithGradients extends RangeStats
+{
+       private AltitudeRange _gentleAltitudeRange = new AltitudeRange();
+       private AltitudeRange _steepAltitudeRange = new AltitudeRange();
+       private Altitude _prevAltitude = null;
+       private double _radsSinceLastAltitude = 0.0;
+
+       private static final double STEEP_ANGLE = 0.15; // gradient steeper than 15% counts as steep
+
+
+       /**
+        * Default constructor
+        */
+       public RangeStatsWithGradients()
+       {
+               super();
+       }
+
+       /**
+        * Constructor
+        * @param inTrack track object
+        * @param inStartIndex start index
+        * @param inEndIndex end index
+        */
+       public RangeStatsWithGradients(Track inTrack, int inStartIndex, int inEndIndex)
+       {
+               super();
+               populateFromTrack(inTrack, inStartIndex, inEndIndex);
+       }
+
+       /**
+        * Add the given point to the calculations
+        * @param inPoint incoming point
+        */
+       protected void doFurtherCalculations(DataPoint inPoint)
+       {
+               if (_prevPoint != null)
+               {
+                       // Keep track of rads since last point with an altitude
+                       double rads = DataPoint.calculateRadiansBetween(_prevPoint, inPoint);
+                       _radsSinceLastAltitude += rads;
+               }
+
+               if (inPoint.hasAltitude())
+               {
+                       Altitude altitude = inPoint.getAltitude();
+
+                       if (!inPoint.getSegmentStart() && _prevAltitude != null)
+                       {
+                               // Work out gradient, see whether to ignore/add to gentle or steep
+                               double heightDiff = altitude.getMetricValue() - _prevAltitude.getMetricValue();
+                               double metricDist = Distance.convertRadiansToDistance(_radsSinceLastAltitude, UnitSetLibrary.UNITS_METRES);
+                               final boolean isSteep = metricDist < 0.001 || (Math.abs(heightDiff / metricDist) > STEEP_ANGLE);
+                               if (isSteep)
+                               {
+                                       _steepAltitudeRange.ignoreValue(_prevAltitude);
+                                       _steepAltitudeRange.addValue(altitude);
+                               }
+                               else
+                               {
+                                       _gentleAltitudeRange.ignoreValue(_prevAltitude);
+                                       _gentleAltitudeRange.addValue(altitude);
+                               }
+                       }
+                       _prevAltitude = altitude;
+                       _radsSinceLastAltitude = 0.0;
+               }
+
+       }
+
+       /** @return altitude range of range just considering low gradient bits */
+       public AltitudeRange getGentleAltitudeRange() {
+               return _gentleAltitudeRange;
+       }
+
+       /** @return altitude range of range just considering high gradient bits */
+       public AltitudeRange getSteepAltitudeRange() {
+               return _steepAltitudeRange;
+       }
+
+       /** @return the total gradient in % (including segment gaps) */
+       public double getTotalGradient()
+       {
+               double dist = Distance.convertRadiansToDistance(_totalDistanceRads, UnitSetLibrary.UNITS_METRES);
+               if (dist > 0.0 && _totalAltitudeRange.hasRange()) {
+                       return _totalAltitudeRange.getMetricHeightDiff() / dist * 100.0;
+               }
+               return 0.0;
+       }
+
+       /** @return the moving gradient in % (ignoring segment gaps) */
+       public double getMovingGradient()
+       {
+               double dist = Distance.convertRadiansToDistance(_movingDistanceRads, UnitSetLibrary.UNITS_METRES);
+               if (dist > 0.0 && _movingAltitudeRange.hasRange()) {
+                       return _movingAltitudeRange.getMetricHeightDiff() / dist * 100.0;
+               }
+               return 0.0;
+       }
+}
index 7b63d70838e660c9af142aa1b341f5c2bd40ccce..24fda5df99565270b898602ed75f6e5cca39802e 100644 (file)
@@ -11,14 +11,11 @@ public class Selection
 {
        private Track _track = null;
        private int _currentPoint = -1;
-       private boolean _valid = false;
        private int _prevNumPoints = 0;
        private int _startIndex = -1, _endIndex = -1;
        private int _currentPhotoIndex = -1;
        private int _currentAudioIndex = -1;
-       private AltitudeRange _altitudeRange = null;
-       private long _movingMilliseconds = 0L;
-       private double _angMovingDistance = -1.0;
+       private RangeStats _rangeStats = null;
 
 
        /**
@@ -36,7 +33,7 @@ public class Selection
         */
        public void markInvalid()
        {
-               _valid = false;
+               _rangeStats = null;
        }
 
 
@@ -63,6 +60,9 @@ public class Selection
         */
        private void recalculate()
        {
+               if (_rangeStats != null) {
+                       return;
+               }
                final int numPoints = _track.getNumPoints();
                // Recheck if the number of points has changed
                if (numPoints != _prevNumPoints)
@@ -72,52 +72,12 @@ public class Selection
                }
                if (numPoints > 0 && hasRangeSelected())
                {
-                       _altitudeRange = new AltitudeRange();
-                       Altitude altitude = null;
-                       Timestamp time = null, previousTime = null;
-                       DataPoint lastPoint = null, currPoint = null;
-                       _angMovingDistance = 0.0;
-                       _movingMilliseconds = 0L;
-                       // Loop over points in selection
-                       for (int i=_startIndex; i<=_endIndex; i++)
-                       {
-                               currPoint = _track.getPoint(i);
-                               altitude = currPoint.getAltitude();
-                               // Ignore waypoints in altitude calculations
-                               if (!currPoint.isWaypoint() && altitude.isValid())
-                               {
-                                       if (currPoint.getSegmentStart()) {
-                                               _altitudeRange.ignoreValue(altitude);
-                                       }
-                                       else {
-                                               _altitudeRange.addValue(altitude);
-                                       }
-                               }
-                               // Compare timestamps within the segments
-                               time = currPoint.getTimestamp();
-                               if (time.isValid())
-                               {
-                                       // add moving time
-                                       if (!currPoint.getSegmentStart() && previousTime != null && time.isAfter(previousTime)) {
-                                               _movingMilliseconds += time.getMillisecondsSince(previousTime);
-                                       }
-                                       previousTime = time;
-                               }
-                               // Calculate distances, again ignoring waypoints
-                               if (!currPoint.isWaypoint())
-                               {
-                                       if (lastPoint != null)
-                                       {
-                                               double radians = DataPoint.calculateRadiansBetween(lastPoint, currPoint);
-                                               if (!currPoint.getSegmentStart()) {
-                                                       _angMovingDistance += radians;
-                                               }
-                                       }
-                                       lastPoint = currPoint;
-                               }
-                       }
+                       _rangeStats = new RangeStats(_track, _startIndex, _endIndex);
+               }
+               else
+               {
+                       _rangeStats = new RangeStats();
                }
-               _valid = true;
        }
 
 
@@ -126,7 +86,7 @@ public class Selection
         */
        public int getStart()
        {
-               if (!_valid) recalculate();
+               recalculate();
                return _startIndex;
        }
 
@@ -136,7 +96,7 @@ public class Selection
         */
        public int getEnd()
        {
-               if (!_valid) recalculate();
+               recalculate();
                return _endIndex;
        }
 
@@ -145,8 +105,8 @@ public class Selection
         */
        public AltitudeRange getAltitudeRange()
        {
-               if (!_valid) recalculate();
-               return _altitudeRange;
+               recalculate();
+               return _rangeStats.getTotalAltitudeRange();
        }
 
 
@@ -155,8 +115,8 @@ public class Selection
         */
        public long getMovingSeconds()
        {
-               if (!_valid) recalculate();
-               return _movingMilliseconds / 1000L;
+               recalculate();
+               return _rangeStats.getMovingDurationInSeconds();
        }
 
        /**
@@ -164,7 +124,7 @@ public class Selection
         */
        public double getMovingDistance()
        {
-               return Distance.convertRadiansToDistance(_angMovingDistance);
+               return _rangeStats.getMovingDistance();
        }
 
        /**
@@ -176,6 +136,7 @@ public class Selection
                selectRange(-1, -1);
                _currentPhotoIndex = -1;
                _currentAudioIndex = -1;
+               markInvalid();
                check();
        }
 
@@ -276,6 +237,7 @@ public class Selection
                // Clear selected range
                _startIndex = _endIndex = -1;
                // Check for consistency and fire update
+               markInvalid();
                check();
        }
 
@@ -355,6 +317,7 @@ public class Selection
                {
                        // track is empty, clear selections
                        _currentPoint = _startIndex = _endIndex = -1;
+                       markInvalid();
                }
                UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
        }
index ac144bb8c59e8247deb67438a9f5c8504c258ca9..6083cf676bcc468f06178cd88f04e4f88547fa2e 100644 (file)
@@ -22,7 +22,7 @@ public abstract class Timestamp
        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 boolean _millisAddedToTimeFormat = false;
 
 
        /** Possible formats for parsing and displaying timestamps */
@@ -138,7 +138,7 @@ public abstract class Timestamp
        {
                if (!isValid()) return "";
                // Maybe we should add milliseconds to this format?
-               if (hasMilliseconds() && !MillisAddedToTimeFormat)
+               if (hasMilliseconds() && !_millisAddedToTimeFormat)
                {
                        try
                        {
@@ -147,7 +147,7 @@ public abstract class Timestamp
                                if (pattern.indexOf("ss") > 0 && pattern.indexOf("SS") < 0)
                                {
                                        sdf.applyPattern(pattern.replaceFirst("s+", "$0.SSS"));
-                                       MillisAddedToTimeFormat = true;
+                                       _millisAddedToTimeFormat = true;
                                }
                        }
                        catch (ClassCastException cce) {}
index 80a7983418149d97f3cf7fce1e9915cda968dae2..6f98fcdb6b476a36bb002f3a67d4da153df56e7b 100644 (file)
@@ -97,8 +97,8 @@ public class AboutScreen extends GenericFunction
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.summarytext3")).append("</p>");
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.languages")).append(" : ")
                        .append("afrikaans, \u010de\u0161tina, deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano,<br>" +
-                               " magyar, nederlands, polski, portugu\u00EAs, rom\u00E2n\u0103, suomi, \u0440\u0443\u0441\u0441\u043a\u0438\u0439 (russian),<br>" +
-                               " \u4e2d\u6587 (chinese), \u65E5\u672C\u8A9E (japanese), \uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean), schwiizerd\u00FC\u00FCtsch, ukrainian</p>");
+                               " magyar, nederlands, polski, portugu\u00EAs, rom\u00E2n\u0103, suomi, svenska, \u0440\u0443\u0441\u0441\u043a\u0438\u0439 (russian),<br>" +
+                               " \u4e2d\u6587 (chinese), \u65E5\u672C\u8A9E (japanese), \uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean), schwiizerd\u00FC\u00FCtsch</p>");
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.translatedby")).append("</p>");
                JEditorPane descPane = new JEditorPane("text/html", descBuffer.toString());
                descPane.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
@@ -196,7 +196,7 @@ public class AboutScreen extends GenericFunction
                        new JLabel(" Gy\u00F6rgy, HooAU, Sergey, P\u00E9ter, serhijdubyk, Peter, Cristian,"),
                        1, 6);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
-                       new JLabel(" Roman, Erkki"),
+                       new JLabel(" Roman, Erkki, Carlos, Tche333"),
                        1, 7);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
                        new JLabel(I18nManager.getText("dialog.about.credits.translations") + " : "),
index c98ab3e17843a27db638d74ffac802c5400bea36..26849de03711831a49e427b9e7fa7ae34c6d17a9 100644 (file)
@@ -101,7 +101,7 @@ public class AddAltitudeOffset extends GenericFunction
                MouseAdapter mouseListener = new MouseAdapter() {
                        public void mouseReleased(java.awt.event.MouseEvent arg0) {
                                _okButton.setEnabled(Math.abs(getOffset()) > 0.001);
-                       };
+                       }
                };
                _editField.addKeyListener(keyListener);
                _editField.addMouseListener(mouseListener);
index 2e259bb5f5895e94690355d2cbee039080a8434d..9e8306feffe340955ca8adb82656dca9ea491a32 100644 (file)
@@ -104,7 +104,7 @@ public class AddTimeOffset extends GenericFunction
                JPanel descPanel = new JPanel();
                descPanel.setLayout(new GridLayout(0, 2));
                descPanel.add(makeRightLabel("dialog.addtimeoffset.days"));
-               _dayField = new WholeNumberField(3);
+               _dayField = new WholeNumberField(4);
                descPanel.add(_dayField);
                descPanel.add(makeRightLabel("dialog.addtimeoffset.hours"));
                _hourField = new WholeNumberField(3);
@@ -126,7 +126,7 @@ public class AddTimeOffset extends GenericFunction
                MouseAdapter mouseListener = new MouseAdapter() {
                        public void mouseReleased(java.awt.event.MouseEvent arg0) {
                                _okButton.setEnabled(getOffsetSecs() != 0L);
-                       };
+                       }
                };
                _dayField.addKeyListener(keyListener);
                _hourField.addKeyListener(keyListener);
index 0998020f2f2f871ed9d1d8522af91d13b72ef9b5..9921dc590aa8bfaed816f4e4456c241bc72c3e7b 100644 (file)
@@ -42,9 +42,9 @@ public class ConnectToPointFunction extends GenericFunction
                boolean connectPhoto = (point != null && photo != null && point.getPhoto() == null);
                boolean connectAudio = (point != null && audio != null && point.getAudio() == null);
 
-               if (connectPhoto && connectAudio) {
+               // if (connectPhoto && connectAudio) {
                        // TODO: Let user choose whether to connect photo/audio or both
-               }
+               // }
                // Make undo object
                UndoOperation undo = new UndoConnectMedia(point, connectPhoto?photo.getName():null,
                        connectAudio?audio.getName():null);
index f3dd1f1d9bca2eba8f78f42a3a136294363543e9..92b4ce56c497b6dbd33924d43f2984cea20ae3a7 100644 (file)
@@ -8,12 +8,14 @@ import tim.prune.UpdateMessageBroker;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Field;
 import tim.prune.data.FieldList;
+import tim.prune.data.RangeStats;
 import tim.prune.data.Track;
+import tim.prune.data.UnitSetLibrary;
 import tim.prune.undo.UndoAppendPoints;
 
 /**
- * Function to create waypoints marking either
- * at regular distance intervals or time intervals
+ * Function to create waypoints marking regular distance intervals,
+ * regular time intervals, or halfway points
  */
 public class CreateMarkerWaypointsFunction extends DistanceTimeLimitFunction
 {
@@ -22,12 +24,21 @@ public class CreateMarkerWaypointsFunction extends DistanceTimeLimitFunction
        /** Counter of previously used multiple */
        private int _previousMultiple = 0;
 
+       /*
+        * Type of halfway point
+        */
+       private enum HalfwayType
+       {
+               HALF_DISTANCE,
+               HALF_CLIMB,
+               HALF_DESCENT
+       }
 
        /**
         * Constructor
         */
        public CreateMarkerWaypointsFunction(App inApp) {
-               super(inApp);
+               super(inApp, true);
        }
 
        /**
@@ -51,55 +62,34 @@ public class CreateMarkerWaypointsFunction extends DistanceTimeLimitFunction
         */
        protected void performFunction()
        {
-               // Distribute either by distance or time
+               // Determine which kind of markers to create
                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);
+               final double distLimitKm = getDistanceLimitKilometres();
+               final boolean createByDistance = (distLimitKm > 0.0);
+               final boolean createHalves = isHalvesSelected();
 
                // 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<numPoints; i++)
+               if (createByTime || createByDistance) {
+                       createWaypointsAtIntervals(timeLimitSeconds, distLimitKm);
+               }
+               else if (createHalves)
                {
-                       currPoint = _app.getTrackInfo().getTrack().getPoint(i);
-                       if (!currPoint.isWaypoint())
-                       {
-                               if (!currPoint.getSegmentStart() && prevPoint != null)
-                               {
-                                       // Calculate current value
-                                       if (createByTime)
-                                       {
-                                               if (currPoint.hasTimestamp() && prevPoint.hasTimestamp())
-                                               {
-                                                       currValue += (currPoint.getTimestamp().getMillisecondsSince(prevPoint.getTimestamp()) / 1000.0);
-                                                       processValue(prevPoint, prevValue, timeLimitSeconds, currPoint, currValue);
-                                               }
-                                       }
-                                       else if (createByDistance)
-                                       {
-                                               currValue += DataPoint.calculateRadiansBetween(prevPoint, currPoint);
-                                               processValue(prevPoint, prevValue, distLimitRadians, currPoint, currValue);
-                                       }
-                               }
-                               prevPoint = currPoint;
-                               prevValue = currValue;
-                       }
+                       createHalfwayWaypoints();
+               }
+               else
+               {
+                       return;
                }
 
-               // System.out.println(_pointsToAdd.size() + " markers to add...");
                if (!_pointsToAdd.isEmpty())
                {
+                       // Make undo object
+                       final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
+                       UndoAppendPoints undo = new UndoAppendPoints(numPoints);
+
                        // Append created points to Track
                        Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE, Field.WAYPT_NAME};
                        final int numPointsToAdd = _pointsToAdd.size();
@@ -109,13 +99,53 @@ public class CreateMarkerWaypointsFunction extends DistanceTimeLimitFunction
                        _app.getTrackInfo().getTrack().combine(wpTrack);
 
                        undo.setNumPointsAppended(numPointsToAdd);
-                       _app.completeFunction(undo, I18nManager.getText("confirm.interpolate"));
-                       // TODO: Maybe add new token including number of points added/created
+                       final String confirmMessage = I18nManager.getTextWithNumber("confirm.pointsadded", _pointsToAdd.size());
+                       _app.completeFunction(undo, confirmMessage);
                        UpdateMessageBroker.informSubscribers();
                }
                _dialog.dispose();
        }
 
+       /**
+        * Create waypoints according to the given intervals
+        * @param inTimeLimitSeconds
+        * @param inDistLimitKm distance limit in kilometres
+        */
+       private void createWaypointsAtIntervals(int inTimeLimitSeconds, double inDistLimitKm)
+       {
+               final boolean createByTime = (inTimeLimitSeconds > 0);
+               final boolean createByDistance = (inDistLimitKm > 0.0);
+
+               // Make new waypoints, looping through the points in the track
+               DataPoint currPoint = null, prevPoint = null;
+               double currValue = 0.0, prevValue = 0.0;
+               RangeStats rangeStats = new RangeStats();
+               final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
+               for (int i=0; i<numPoints; i++)
+               {
+                       currPoint = _app.getTrackInfo().getTrack().getPoint(i);
+                       rangeStats.addPoint(currPoint);
+
+                       if (!currPoint.isWaypoint())
+                       {
+                               // Calculate current value
+                               if (createByTime)
+                               {
+                                       currValue = rangeStats.getMovingDurationInSeconds();
+                                       processValue(prevPoint, prevValue, inTimeLimitSeconds, currPoint, currValue);
+                               }
+                               else if (createByDistance)
+                               {
+                                       currValue = rangeStats.getMovingDistanceKilometres();
+                                       processValue(prevPoint, prevValue, inDistLimitKm, currPoint, currValue);
+                               }
+
+                               prevPoint = currPoint;
+                               prevValue = currValue;
+                       }
+               }
+       }
+
        /**
         * Consider a pair of points in the track to see if a new marker should be inserted between them
         * @param inPrevPoint previous point
@@ -138,8 +168,124 @@ public class CreateMarkerWaypointsFunction extends DistanceTimeLimitFunction
                        DataPoint marker = DataPoint.interpolate(inPrevPoint, inCurrPoint, fractionFromPrev);
                        marker.setFieldValue(Field.WAYPT_NAME, createLimitDescription(m), false);
                        _pointsToAdd.add(marker);
-                       // System.out.println("I would add a point here with values " + inPrevValue + " and " + inCurrValue);
                }
                _previousMultiple = currMultiple;
        }
+
+       /**
+        * Create waypoints for the halfway markers
+        */
+       private void createHalfwayWaypoints()
+       {
+               // Calculate the details of the whole track so we can see what to halve
+               final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
+               RangeStats totalStats = new RangeStats();
+               DataPoint currPoint = null;
+               for (int i=0; i<numPoints; i++)
+               {
+                       currPoint = _app.getTrackInfo().getTrack().getPoint(i);
+                       totalStats.addPoint(currPoint);
+               }
+               // Calculate total moving distance of track
+               final double totalDist = totalStats.getMovingDistanceKilometres();
+               // If the track has altitudes, also calculate total climb and total descent
+               final double totalClimb = totalStats.getMovingAltitudeRange().getClimb(UnitSetLibrary.UNITS_METRES);
+               final double totalDescent = totalStats.getMovingAltitudeRange().getDescent(UnitSetLibrary.UNITS_METRES);
+
+               final double halfDistance = totalDist / 2.0;
+               final double halfClimb = totalClimb / 2.0;
+               final double halfDescent = totalDescent / 2.0;
+
+               // Now loop through points again, looking for the halfway points
+               RangeStats partialStats = new RangeStats();
+               DataPoint prevPoint = null;
+               boolean createdDistance = false, createdClimb = false, createdDescent = false;
+               double prevDistance = 0.0, prevClimb = 0.0, prevDescent = 0.0;
+               for (int i=0; i<numPoints; i++)
+               {
+                       currPoint = _app.getTrackInfo().getTrack().getPoint(i);
+                       partialStats.addPoint(currPoint);
+                       if (!currPoint.isWaypoint())
+                       {
+                               // distance
+                               if (!createdDistance && totalDist > 0.0)
+                               {
+                                       final double currDist = partialStats.getMovingDistanceKilometres();
+                                       createdDistance = processHalfValue(prevPoint, prevDistance, halfDistance,
+                                               currPoint, currDist, HalfwayType.HALF_DISTANCE);
+                                       prevDistance = currDist;
+                               }
+                               // climb
+                               if (!createdClimb && totalClimb > 0.0)
+                               {
+                                       final double currClimb = partialStats.getMovingAltitudeRange().getClimb(UnitSetLibrary.UNITS_METRES);
+                                       createdClimb = processHalfValue(prevPoint, prevClimb, halfClimb,
+                                               currPoint, currClimb, HalfwayType.HALF_CLIMB);
+                                       prevClimb = currClimb;
+                               }
+                               // descent
+                               if (!createdDescent && totalDescent > 0.0)
+                               {
+                                       final double currDescent = partialStats.getMovingAltitudeRange().getDescent(UnitSetLibrary.UNITS_METRES);
+                                       createdDescent = processHalfValue(prevPoint, prevDescent, halfDescent,
+                                               currPoint, currDescent, HalfwayType.HALF_DESCENT);
+                                       prevDescent = currDescent;
+                               }
+
+                               prevPoint = currPoint;
+                       }
+               }
+       }
+
+       /**
+        * Consider a pair of points in the track to see if a new halfway marker should be inserted between them
+        * @param inPrevPoint previous point
+        * @param inPrevValue value of function at this previous point
+        * @param inTargetValue target halfway value
+        * @param inCurrPoint current point
+        * @param inCurrValue value of function at this current point
+        * @param inType type of halfway point
+        */
+       private boolean processHalfValue(DataPoint inPrevPoint, double inPrevValue, double inTargetValue,
+               DataPoint inCurrPoint, double inCurrValue, HalfwayType inType)
+       {
+               if (inPrevValue <= inTargetValue && inCurrValue >= inTargetValue)
+               {
+                       // Calculate position of limit between the two points
+                       final double valueBeforeBreak = inTargetValue - inPrevValue;
+                       final double valueAfterBreak = inCurrValue - inTargetValue;
+                       final double fractionFromPrev = valueBeforeBreak / (valueBeforeBreak + valueAfterBreak);
+                       DataPoint marker = DataPoint.interpolate(inPrevPoint, inCurrPoint, fractionFromPrev);
+                       marker.setFieldValue(Field.WAYPT_NAME, createHalfwayName(inType), false);
+                       _pointsToAdd.add(marker);
+                       return true;
+               }
+               return false;
+       }
+
+       /**
+        * Create the name of the halfway point according to type
+        * @param inType type of point
+        */
+       private String createHalfwayName(HalfwayType inType)
+       {
+               String typeString = null;
+               switch (inType)
+               {
+                       case HALF_DISTANCE:
+                               typeString = "distance";
+                               break;
+                       case HALF_CLIMB:
+                               typeString = "climb";
+                               break;
+                       case HALF_DESCENT:
+                               typeString = "descent";
+                               break;
+               }
+               if (typeString != null)
+               {
+                       return I18nManager.getText("dialog.markers.half." + typeString);
+               }
+               return "half";
+       }
 }
index 915bed1041bd6fdc7342c390ed70ab4575d4868c..2e2fa06d5f7e1f4a4ce08a40ecf26299232c76db 100644 (file)
@@ -9,6 +9,7 @@ import java.awt.event.KeyAdapter;
 import java.awt.event.KeyEvent;
 import java.io.File;
 
+import javax.swing.BorderFactory;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
 import javax.swing.JDialog;
@@ -17,6 +18,7 @@ import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JTextField;
+import javax.swing.border.EtchedBorder;
 
 import tim.prune.App;
 import tim.prune.DataSubscriber;
@@ -62,6 +64,9 @@ public class DiskCacheConfig extends GenericFunction
        private Component makeContents()
        {
                JPanel dialogPanel = new JPanel();
+               dialogPanel.setBorder(BorderFactory.createCompoundBorder(
+                       BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
+               );
                dialogPanel.setLayout(new BorderLayout(0, 5));
                // top panel
                JPanel topPanel = new JPanel();
index eaa7b7d8a16ccd8fa42079a3421f6a4c8919ec96..48075e72efe9132ebedc0c640240776d842fd571 100644 (file)
@@ -10,8 +10,7 @@ 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.BorderFactory;
 import javax.swing.ButtonGroup;
 import javax.swing.JButton;
 import javax.swing.JComboBox;
@@ -28,6 +27,7 @@ import tim.prune.data.Field;
 import tim.prune.data.TimeDifference;
 import tim.prune.data.Unit;
 import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.GuiGridLayout;
 import tim.prune.gui.WholeNumberField;
 
 /**
@@ -40,6 +40,10 @@ public abstract class DistanceTimeLimitFunction extends GenericFunction
        protected JDialog _dialog = null;
        /** Radio buttons for splitting by distance and time */
        private JRadioButton _distLimitRadio = null, _timeLimitRadio = null;
+       /** Radio button for splitting by fraction (such as half-distance) */
+       private JRadioButton _halvesRadio = null;
+       /** Flag for whether to offer halves or not */
+       private boolean _showHalves = false;
        /** Dropdown for selecting distance units */
        private JComboBox<String> _distUnitsDropdown = null;
        /** Text field for entering distance */
@@ -52,29 +56,28 @@ public abstract class DistanceTimeLimitFunction extends GenericFunction
 
 
        /**
-        * React to item changes and key presses
+        * React to item changes and key presses by enabling / disabling ok button
         */
-       private abstract class ChangeListener extends KeyAdapter implements ItemListener
+       private class ChangeListener extends KeyAdapter implements ItemListener
        {
-               /** Method to be implemented */
-               public abstract void optionsChanged();
-
                /** Item changed in ItemListener */
                public void itemStateChanged(ItemEvent arg0) {
-                       optionsChanged();
+                       enableOkButton();
                }
 
                /** Key released in KeyListener */
                public void keyReleased(KeyEvent arg0) {
-                       optionsChanged();
+                       enableOkButton();
                }
        }
 
        /**
         * Constructor
         */
-       public DistanceTimeLimitFunction(App inApp) {
+       public DistanceTimeLimitFunction(App inApp, boolean inShowHalves)
+       {
                super(inApp);
+               _showHalves = inShowHalves;
        }
 
        /**
@@ -112,28 +115,31 @@ public abstract class DistanceTimeLimitFunction extends GenericFunction
                JPanel dialogPanel = new JPanel();
                dialogPanel.setLayout(new BorderLayout(5, 5));
 
-               // Make radio buttons for three different options
+               // Make radio buttons for the options
                _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit") + ": ");
                _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + ": ");
+               if (_showHalves) {
+                       _halvesRadio = new JRadioButton(I18nManager.getText("dialog.markers.halves"));
+               }
                ButtonGroup radioGroup = new ButtonGroup();
                radioGroup.add(_distLimitRadio);
                radioGroup.add(_timeLimitRadio);
+               if (_showHalves) {
+                       radioGroup.add(_halvesRadio);
+               }
 
                // 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();
-                       }
-               };
+               limitsPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+               GuiGridLayout grid = new GuiGridLayout(limitsPanel, new double[] {0.5, 1.0},
+                       new boolean[] {false, false});
+               ChangeListener optionsChangedListener = new ChangeListener();
                // distance limits
-               JPanel distLimitPanel = new JPanel();
-               distLimitPanel.setLayout(new FlowLayout());
+               grid.add(_distLimitRadio);
                _distLimitRadio.setSelected(true);
                _distLimitRadio.addItemListener(optionsChangedListener);
-               distLimitPanel.add(_distLimitRadio);
+               JPanel distLimitPanel = new JPanel();
+               distLimitPanel.setLayout(new FlowLayout());
                _distanceField = new WholeNumberField(3);
                _distanceField.addKeyListener(optionsChangedListener);
                distLimitPanel.add(_distanceField);
@@ -143,13 +149,13 @@ public abstract class DistanceTimeLimitFunction extends GenericFunction
                _distUnitsDropdown.addItemListener(optionsChangedListener);
                distLimitPanel.add(_distUnitsDropdown);
                distLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
-               limitsPanel.add(distLimitPanel);
+               grid.add(distLimitPanel);
 
                // time limit panel
+               grid.add(_timeLimitRadio);
+               _timeLimitRadio.addItemListener(optionsChangedListener);
                JPanel timeLimitPanel = new JPanel();
                timeLimitPanel.setLayout(new FlowLayout());
-               _timeLimitRadio.addItemListener(optionsChangedListener);
-               timeLimitPanel.add(_timeLimitRadio);
                _limitHourField = new WholeNumberField(2);
                _limitHourField.addKeyListener(optionsChangedListener);
                timeLimitPanel.add(_limitHourField);
@@ -159,7 +165,14 @@ public abstract class DistanceTimeLimitFunction extends GenericFunction
                timeLimitPanel.add(_limitMinField);
                timeLimitPanel.add(new JLabel(I18nManager.getText("units.minutes")));
                timeLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
-               limitsPanel.add(timeLimitPanel);
+               grid.add(timeLimitPanel);
+
+               // halves
+               if (_showHalves)
+               {
+                       grid.add(_halvesRadio);
+                       _halvesRadio.addItemListener(optionsChangedListener);
+               }
 
                dialogPanel.add(limitsPanel, BorderLayout.NORTH);
 
@@ -203,6 +216,9 @@ public abstract class DistanceTimeLimitFunction extends GenericFunction
                else if (_timeLimitRadio.isSelected()) {
                        enabled = _limitHourField.getValue() > 0 || _limitMinField.getValue() > 0;
                }
+               else if (_halvesRadio != null && _halvesRadio.isSelected()) {
+                       enabled = true;
+               }
                _okButton.setEnabled(enabled);
 
                // Also enable/disable the other fields
@@ -245,6 +261,21 @@ public abstract class DistanceTimeLimitFunction extends GenericFunction
                return 0.0;
        }
 
+       /**
+        * @return selected distance limit in km, or 0.0
+        */
+       protected double getDistanceLimitKilometres()
+       {
+               return Distance.convertRadiansToDistance(getDistanceLimitRadians(), UnitSetLibrary.UNITS_KILOMETRES);
+       }
+
+       /**
+        * @return true if "halves" option was selected
+        */
+       protected boolean isHalvesSelected() {
+               return _halvesRadio != null && _halvesRadio.isSelected();
+       }
+
        /**
         * The dialog has been completed and OK pressed, so do the corresponding function
         */
index 341066a68422160a84a2877155a8c64a1ead516d..7739092831816f238bdc3db92b68eb5b6e24c3e7 100644 (file)
@@ -30,7 +30,8 @@ public class DuplicatePoint extends GenericFunction
        public void begin()
        {
                DataPoint point = _app.getTrackInfo().getCurrentPoint();
-               if (point != null) {
+               if (point != null)
+               {
                        // Pass information back to App to complete function
                        _app.createPoint(point.clonePoint());
                }
diff --git a/src/tim/prune/function/FullRangeDetails.java b/src/tim/prune/function/FullRangeDetails.java
deleted file mode 100644 (file)
index 07ef39e..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-package tim.prune.function;
-
-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.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-
-import javax.swing.BorderFactory;
-import javax.swing.JButton;
-import javax.swing.JDialog;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-
-import tim.prune.App;
-import tim.prune.GenericFunction;
-import tim.prune.I18nManager;
-import tim.prune.config.Config;
-import tim.prune.data.RangeStats;
-import tim.prune.data.Selection;
-import tim.prune.data.Unit;
-import tim.prune.gui.DisplayUtils;
-import tim.prune.gui.profile.SpeedData;
-
-/**
- * Class to show the full range details in a separate popup
- */
-public class FullRangeDetails extends GenericFunction
-{
-       /** Dialog */
-       private JDialog _dialog = null;
-       /** Label for number of points */
-       private JLabel _numPointsLabel = null;
-       /** Label for number of segments */
-       private JLabel _numSegsLabel = null;
-       /** Label for the maximum speed */
-       private JLabel _maxSpeedLabel = null;
-
-       /** Label for heading of "total" column */
-       private JLabel _colTotalLabel = null;
-       /** Label for heading of "segments" column */
-       private JLabel _colSegmentsLabel = null;
-       /** Labels for distances */
-       private JLabel _totalDistanceLabel = null, _movingDistanceLabel = null;
-       /** Labels for durations */
-       private JLabel _totalDurationLabel = null, _movingDurationLabel = null;
-       /** Labels for climbs */
-       private JLabel _totalClimbLabel = null, _movingClimbLabel = null;
-       /** Labels for descents */
-       private JLabel _totalDescentLabel = null, _movingDescentLabel = null;
-       /** Labels for pace */
-       private JLabel _totalPaceLabel = null, _movingPaceLabel = null;
-       /** Labels for gradient */
-       private JLabel _totalGradientLabel = null, _movingGradientLabel = null;
-       /** Labels for speed */
-       private JLabel _totalSpeedLabel, _movingSpeedLabel = null;
-       /** Labels for vertical speed */
-       private JLabel _totalVertSpeedLabel, _movingVertSpeedLabel = null;
-
-
-       /**
-        * Constructor
-        * @param inApp App object
-        */
-       public FullRangeDetails(App inApp)
-       {
-               super(inApp);
-       }
-
-       /** Get the name key */
-       public String getNameKey() {
-               return "function.fullrangedetails";
-       }
-
-       /**
-        * 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();
-               }
-               updateDetails();
-               _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));
-               // Label at top
-               JLabel topLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.intro") + ":");
-               topLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
-               dialogPanel.add(topLabel, BorderLayout.NORTH);
-
-               // Details panel in middle
-               JPanel midPanel = new JPanel();
-               midPanel.setLayout(new GridLayout(0, 3, 6, 2));
-               midPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
-               // Number of points
-               JLabel pointsLabel = new JLabel(I18nManager.getText("details.track.points") + ": ");
-               pointsLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(pointsLabel);
-               _numPointsLabel = new JLabel("100");
-               midPanel.add(_numPointsLabel);
-               midPanel.add(new JLabel(" "));
-               // Number of segments
-               JLabel segLabel = new JLabel(I18nManager.getText("details.range.numsegments") + ": ");
-               segLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(segLabel);
-               _numSegsLabel = new JLabel("100");
-               midPanel.add(_numSegsLabel);
-               midPanel.add(new JLabel(" "));
-               // Maximum speed
-               JLabel maxSpeedLabel = new JLabel(I18nManager.getText("details.range.maxspeed") + ": ");
-               maxSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(maxSpeedLabel);
-               _maxSpeedLabel = new JLabel("10 km/h");
-               midPanel.add(_maxSpeedLabel);
-               midPanel.add(new JLabel(" "));
-
-               // blank row
-               for (int i=0; i<3; i++) midPanel.add(new JLabel(" "));
-
-               // Row for column headings
-               midPanel.add(new JLabel(" "));
-               _colTotalLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.coltotal"));
-               midPanel.add(_colTotalLabel);
-               _colSegmentsLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.colsegments"));
-               midPanel.add(_colSegmentsLabel);
-
-               // Distance
-               JLabel distLabel = new JLabel(I18nManager.getText("fieldname.distance") + ": ");
-               distLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(distLabel);
-               _totalDistanceLabel = new JLabel("5 km");
-               midPanel.add(_totalDistanceLabel);
-               _movingDistanceLabel = new JLabel("5 km");
-               midPanel.add(_movingDistanceLabel);
-
-               // Duration
-               JLabel durationLabel = new JLabel(I18nManager.getText("fieldname.duration") + ": ");
-               durationLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(durationLabel);
-               _totalDurationLabel = new JLabel("15 min");
-               midPanel.add(_totalDurationLabel);
-               _movingDurationLabel = new JLabel("15 min");
-               midPanel.add(_movingDurationLabel);
-
-               // Speed
-               JLabel speedLabel = new JLabel(I18nManager.getText("details.range.avespeed") + ": ");
-               speedLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(speedLabel);
-               _totalSpeedLabel = new JLabel("5.5 km/h");
-               midPanel.add(_totalSpeedLabel);
-               _movingSpeedLabel = new JLabel("5.5 km/h");
-               midPanel.add(_movingSpeedLabel);
-
-               // Pace
-               JLabel paceLabel = new JLabel(I18nManager.getText("details.range.pace") + ": ");
-               paceLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(paceLabel);
-               _totalPaceLabel = new JLabel("8 min/km");
-               midPanel.add(_totalPaceLabel);
-               _movingPaceLabel = new JLabel("8 min/km");
-               midPanel.add(_movingPaceLabel);
-
-               // Climb
-               JLabel climbLabel = new JLabel(I18nManager.getText("details.range.climb") + ": ");
-               climbLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(climbLabel);
-               _totalClimbLabel = new JLabel("1000 m");
-               midPanel.add(_totalClimbLabel);
-               _movingClimbLabel = new JLabel("1000 m");
-               midPanel.add(_movingClimbLabel);
-               // Descent
-               JLabel descentLabel = new JLabel(I18nManager.getText("details.range.descent") + ": ");
-               descentLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(descentLabel);
-               _totalDescentLabel = new JLabel("1000 m");
-               midPanel.add(_totalDescentLabel);
-               _movingDescentLabel = new JLabel("1000 m");
-               midPanel.add(_movingDescentLabel);
-
-               // Gradient
-               JLabel gradientLabel = new JLabel(I18nManager.getText("details.range.gradient") + ": ");
-               gradientLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(gradientLabel);
-               _totalGradientLabel = new JLabel("10 %");
-               midPanel.add(_totalGradientLabel);
-               _movingGradientLabel = new JLabel("10 %");
-               midPanel.add(_movingGradientLabel);
-
-               // Vertical speed
-               JLabel vSpeedLabel = new JLabel(I18nManager.getText("fieldname.verticalspeed") + ": ");
-               vSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
-               midPanel.add(vSpeedLabel);
-               _totalVertSpeedLabel = new JLabel("1 m/s");
-               midPanel.add(_totalVertSpeedLabel);
-               _movingVertSpeedLabel = new JLabel("1 m/s");
-               midPanel.add(_movingVertSpeedLabel);
-
-               dialogPanel.add(midPanel, BorderLayout.CENTER);
-               // button panel at bottom
-               JPanel buttonPanel = new JPanel();
-               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
-               JButton closeButton = new JButton(I18nManager.getText("button.close"));
-               closeButton.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _dialog.dispose();
-                       }
-               });
-               closeButton.addKeyListener(new KeyAdapter() {
-                       public void keyPressed(KeyEvent inE) {
-                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
-                               super.keyPressed(inE);
-                       }
-               });
-               buttonPanel.add(closeButton);
-               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
-               return dialogPanel;
-       }
-
-
-       /**
-        * Update the labels with the current details
-        */
-       private void updateDetails()
-       {
-               Selection selection = _app.getTrackInfo().getSelection();
-               // Do the calculations with a separate class
-               RangeStats stats = new RangeStats(_app.getTrackInfo().getTrack(), selection.getStart(), selection.getEnd());
-
-               // Number of points
-               _numPointsLabel.setText("" + stats.getNumPoints());
-               // Number of segments
-               _numSegsLabel.setText("" + stats.getNumSegments());
-               final boolean isMultiSegments = (stats.getNumSegments() > 1);
-               // Set visibility of third column accordingly
-               _movingDistanceLabel.setVisible(isMultiSegments);
-               _movingDurationLabel.setVisible(isMultiSegments || stats.getTimestampsOutOfSequence());
-               // FIXME: What to show if timestamps are out of sequence? Warning message?
-               _movingClimbLabel.setVisible(isMultiSegments);
-               _movingDescentLabel.setVisible(isMultiSegments);
-               _movingSpeedLabel.setVisible(isMultiSegments);
-               _movingPaceLabel.setVisible(isMultiSegments);
-               _movingGradientLabel.setVisible(isMultiSegments);
-               _movingVertSpeedLabel.setVisible(isMultiSegments);
-
-               // Total and moving distance in current units
-               final Unit distUnit = Config.getUnitSet().getDistanceUnit();
-               final String distUnitsStr = I18nManager.getText(distUnit.getShortnameKey());
-               _totalDistanceLabel.setText(DisplayUtils.roundedNumber(stats.getTotalDistance()) + " " + distUnitsStr);
-               _movingDistanceLabel.setText(DisplayUtils.roundedNumber(stats.getMovingDistance()) + " " + distUnitsStr);
-
-               // Duration
-               _totalDurationLabel.setText(DisplayUtils.buildDurationString(stats.getTotalDurationInSeconds()));
-               _movingDurationLabel.setText(DisplayUtils.buildDurationString(stats.getMovingDurationInSeconds()));
-
-               // Climb and descent
-               final Unit altUnit = Config.getUnitSet().getAltitudeUnit();
-               final String altUnitsStr = " " + I18nManager.getText(altUnit.getShortnameKey());
-               if (stats.getTotalAltitudeRange().hasRange()) {
-                       _totalClimbLabel.setText(stats.getTotalAltitudeRange().getClimb(altUnit) + altUnitsStr);
-                       _totalDescentLabel.setText(stats.getTotalAltitudeRange().getDescent(altUnit) + altUnitsStr);
-               }
-               else {
-                       _totalClimbLabel.setText("");
-                       _totalDescentLabel.setText("");
-               }
-               if (stats.getMovingAltitudeRange().hasRange()) {
-                       _movingClimbLabel.setText(stats.getMovingAltitudeRange().getClimb(altUnit) + altUnitsStr);
-                       _movingDescentLabel.setText(stats.getMovingAltitudeRange().getDescent(altUnit) + altUnitsStr);
-               }
-               else {
-                       _movingClimbLabel.setText("");
-                       _movingDescentLabel.setText("");
-               }
-
-               // Overall pace and speed
-               final String speedUnitsStr = I18nManager.getText(Config.getUnitSet().getSpeedUnit().getShortnameKey());
-               long numSecs = stats.getTotalDurationInSeconds();
-               double dist = stats.getTotalDistance();
-               if (numSecs > 0 && dist > 0)
-               {
-                       _totalSpeedLabel.setText(DisplayUtils.roundedNumber(dist/numSecs*3600.0) + " " + speedUnitsStr);
-                       _totalPaceLabel.setText(DisplayUtils.buildDurationString((long) (numSecs/dist))
-                               + " / " + distUnitsStr);
-               }
-               else {
-                       _totalSpeedLabel.setText("");
-                       _totalPaceLabel.setText("");
-               }
-               // and same for within the segments
-               numSecs = stats.getMovingDurationInSeconds();
-               dist = stats.getMovingDistance();
-               if (numSecs > 0 && dist > 0)
-               {
-                       _movingSpeedLabel.setText(DisplayUtils.roundedNumber(dist/numSecs*3600.0) + " " + speedUnitsStr);
-                       _movingPaceLabel.setText(DisplayUtils.buildDurationString((long) (numSecs/dist))
-                               + " / " + distUnitsStr);
-               }
-               else {
-                       _movingSpeedLabel.setText("");
-                       _movingPaceLabel.setText("");
-               }
-
-               // Gradient
-               if (stats.getTotalAltitudeRange().hasRange()) {
-                       _totalGradientLabel.setText(DisplayUtils.formatOneDp(stats.getTotalGradient()) + " %");
-               }
-               else {
-                       _totalGradientLabel.setText("");
-               }
-               if (stats.getMovingAltitudeRange().hasRange()) {
-                       _movingGradientLabel.setText(DisplayUtils.formatOneDp(stats.getMovingGradient()) + " %");
-               }
-               else {
-                       _movingGradientLabel.setText("");
-               }
-
-               // Maximum speed
-               SpeedData speeds = new SpeedData(_app.getTrackInfo().getTrack());
-               speeds.init(Config.getUnitSet());
-               double maxSpeed = 0.0;
-               for (int i=selection.getStart(); i<=selection.getEnd(); i++)
-               {
-                       if (speeds.hasData(i) && (speeds.getData(i) > maxSpeed)) {
-                               maxSpeed = speeds.getData(i);
-                       }
-               }
-               if (maxSpeed > 0.0) {
-                       _maxSpeedLabel.setText(DisplayUtils.roundedNumber(maxSpeed) + " " + speedUnitsStr);
-               }
-               else {
-                       _maxSpeedLabel.setText("");
-               }
-
-               // vertical speed
-               final String vertSpeedUnitsStr = I18nManager.getText(Config.getUnitSet().getVerticalSpeedUnit().getShortnameKey());
-               if (stats.getMovingAltitudeRange().hasRange() && stats.getTotalDurationInSeconds() > 0)
-               {
-                       // got an altitude and time - do totals
-                       _totalVertSpeedLabel.setText(DisplayUtils.roundedNumber(stats.getTotalVerticalSpeed()) + " " + vertSpeedUnitsStr);
-                       _movingVertSpeedLabel.setText(DisplayUtils.roundedNumber(stats.getMovingVerticalSpeed()) + " " + vertSpeedUnitsStr);
-               }
-               else
-               {
-                       // no vertical speed available
-                       _totalVertSpeedLabel.setText("");
-                       _movingVertSpeedLabel.setText("");
-               }
-       }
-}
index 221fdaf989c6c62141d0dfbe7ac29cfb66b069f0..ce0dad59faa1603d2831c8cbe2f6579dfb120dfe 100644 (file)
@@ -122,7 +122,8 @@ public class InterpolateFunction extends SingleNumericParameterFunction
                // Replace track with new points array
                if (track.replaceContents(newPoints))
                {
-                       _app.completeFunction(undo, I18nManager.getText("confirm.interpolate"));
+                       final String confirmMessage = I18nManager.getTextWithNumber("confirm.pointsadded", totalInserted);
+                       _app.completeFunction(undo, confirmMessage);
                        // Alter selection
                        _app.getTrackInfo().getSelection().selectRange(startIndex, endIndex + totalInserted);
                }
diff --git a/src/tim/prune/function/PasteCoordinateList.java b/src/tim/prune/function/PasteCoordinateList.java
new file mode 100644 (file)
index 0000000..9fc259e
--- /dev/null
@@ -0,0 +1,147 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.load.TextFileLoader;
+
+/**
+ * Class to provide the function to paste a list of coordinates
+ * and create points for them as if they were loaded from a text file
+ */
+public class PasteCoordinateList extends GenericFunction
+{
+       private JDialog _dialog = null;
+       private JTextArea _coordArea = null;
+       private JButton _okButton = null;
+
+
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public PasteCoordinateList(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.pastecoordinatelist";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               // Make dialog window
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
+               // MAYBE: Paste clipboard into the edit area
+               _coordArea.setText("");
+               enableOK();
+               _dialog.setVisible(true);
+       }
+
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout(0, 10));
+               dialogPanel.add(new JLabel(I18nManager.getText("dialog.pastecoordinatelist.desc")), BorderLayout.NORTH);
+               _coordArea = new JTextArea(8, 35);
+               _coordArea.setLineWrap(true);
+               _coordArea.setWrapStyleWord(true);
+               JScrollPane coordsPane = new JScrollPane(_coordArea);
+               // Listeners to enable/disable ok button
+               KeyAdapter keyListener = new KeyAdapter() {
+                       /** Key released */
+                       public void keyReleased(KeyEvent inE) {
+                               enableOK();
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
+                                       _dialog.dispose();
+                               }
+                       }
+               };
+               MouseAdapter mouseListener = new MouseAdapter() {
+                       public void mouseReleased(MouseEvent inE) {
+                               enableOK();
+                       }
+               };
+               _coordArea.addKeyListener(keyListener);
+               _coordArea.addMouseListener(mouseListener);
+               dialogPanel.add(coordsPane, BorderLayout.CENTER);
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               ActionListener okListener = new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               if (_okButton.isEnabled()) {finish();}
+                       }
+               };
+               _okButton.addActionListener(okListener);
+               _okButton.setEnabled(false);
+               buttonPanel.add(_okButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               return dialogPanel;
+       }
+
+       /**
+        * Enable or disable the OK button based on the contents of the text field
+        */
+       private void enableOK()
+       {
+               String text = _coordArea.getText();
+               _okButton.setEnabled(text != null && text.length() > 6
+                       && (text.indexOf(' ') >= 0 || text.indexOf(',') >= 0));
+       }
+
+       /**
+        * Finish the dialog when OK pressed
+        */
+       private void finish()
+       {
+               new TextFileLoader(_app, _parentFrame).loadText(_coordArea.getText());
+               _dialog.dispose();
+       }
+}
index c9b2ad1c7ff3f74da6e8162fbb84d8fa886c1e42..c60bb854afe03cabd19189495b38399ba2f0c06d 100644 (file)
@@ -183,7 +183,8 @@ public class PasteCoordinates extends GenericFunction
                else if (items.length == 3) {
                        point = parseValues(items[0].trim(), items[1].trim(), items[2].trim());
                }
-               else {
+               else
+               {
                        // Splitting with commas didn't work, so try spaces
                        items = _coordField.getText().split(" ");
                        if (items.length == 2) {
@@ -211,7 +212,8 @@ public class PasteCoordinates extends GenericFunction
                                I18nManager.getText("dialog.pastecoordinates.nothingfound"),
                                I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
                }
-               else {
+               else
+               {
                        // See if name was entered
                        String name = _nameField.getText();
                        if (name != null && name.length() > 0) {
index 2ac71e93b6e38bbbaab727aa1a6d7d421c4a117a..44b77d9bc3ca7827081ff48f86a53c3143f9e27e 100644 (file)
@@ -1,11 +1,11 @@
 package tim.prune.function;
 
+import java.awt.Desktop;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
 
 import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;
@@ -146,17 +146,10 @@ public class PlayAudioFunction extends GenericFunction implements Runnable
                {
                        try
                        {
-                               Class<?> d = Class.forName("java.awt.Desktop");
-                               d.getDeclaredMethod("open", new Class[] {File.class}).invoke(
-                                       d.getDeclaredMethod("getDesktop").invoke(null), new Object[] {inFile});
-                               //above code mimics: Desktop.getDesktop().open(audioFile);
+                               Desktop.getDesktop().open(inFile);
                                played = true;
                        }
-                       catch (InvocationTargetException e) {
-                               System.err.println("ITE: " + e.getCause().getClass().getName() + " - " + e.getCause().getMessage());
-                               played = false;
-                       }
-                       catch (Exception ignore) {
+                       catch (IOException ignore) {
                                System.err.println(ignore.getClass().getName() + " - " + ignore.getMessage());
                                played = false;
                        }
diff --git a/src/tim/prune/function/PlusCodeFunction.java b/src/tim/prune/function/PlusCodeFunction.java
new file mode 100644 (file)
index 0000000..b208b7f
--- /dev/null
@@ -0,0 +1,209 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+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.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.Field;
+import tim.prune.function.olc.OlcArea;
+import tim.prune.function.olc.OlcDecoder;
+import tim.prune.gui.GuiGridLayout;
+
+/**
+ * Class to provide the function to parse
+ * OpenLocationCodes, or PlusCodes
+ */
+public class PlusCodeFunction extends GenericFunction
+{
+       private JDialog _dialog = null;
+       private JTextField _codeField = null;
+       private JButton _okButton = null;
+
+
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public PlusCodeFunction(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.enterpluscode";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               // Make dialog window
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
+               // MAYBE: Paste clipboard into the edit field
+               _codeField.setText("");
+               enableOK();
+               _dialog.setVisible(true);
+       }
+
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout(0, 10));
+               dialogPanel.add(new JLabel(I18nManager.getText("dialog.pluscode.desc")), BorderLayout.NORTH);
+               JPanel mainPanel = new JPanel();
+               GuiGridLayout grid = new GuiGridLayout(mainPanel);
+               _codeField = new JTextField("", 12);
+               // Listeners to enable/disable ok button
+               KeyAdapter keyListener = new KeyAdapter() {
+                       /** Key released */
+                       public void keyReleased(KeyEvent inE) {
+                               enableOK();
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
+                                       _dialog.dispose();
+                               }
+                       }
+               };
+               MouseAdapter mouseListener = new MouseAdapter() {
+                       public void mouseReleased(MouseEvent inE) {
+                               enableOK();
+                       }
+               };
+               _codeField.addKeyListener(keyListener);
+               _codeField.addMouseListener(mouseListener);
+               JLabel codeLabel = new JLabel(I18nManager.getText("dialog.pluscode.code"));
+               codeLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+               grid.add(codeLabel);
+               grid.add(_codeField);
+               dialogPanel.add(mainPanel, BorderLayout.CENTER);
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               ActionListener okListener = new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               if (_okButton.isEnabled()) {finish();}
+                       }
+               };
+               _okButton.addActionListener(okListener);
+               _okButton.setEnabled(false);
+               _codeField.addActionListener(okListener);
+               buttonPanel.add(_okButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               return dialogPanel;
+       }
+
+       /**
+        * Enable or disable the OK button based on the contents of the text field
+        */
+       private void enableOK()
+       {
+               String text = _codeField.getText();
+               _okButton.setEnabled(text != null && text.length() > 7
+                       && text.indexOf(' ') < 0 && text.indexOf(',') < 0);
+       }
+
+       /**
+        * Finish the dialog when OK pressed
+        */
+       private void finish()
+       {
+               OlcArea area = OlcDecoder.decode(_codeField.getText());
+
+               if (area == null)
+               {
+                       JOptionPane.showMessageDialog(_parentFrame,
+                               I18nManager.getText("dialog.pluscode.nothingfound"),
+                               I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
+               }
+               else if (loadTrack(area))
+               {
+                       _dialog.dispose();
+               }
+       }
+
+       /**
+        * Load the generated points from the given area
+        * @param inArea rectangular area
+        * @return true on success
+        */
+       private boolean loadTrack(OlcArea inArea)
+       {
+               if (inArea == null)
+               {
+                       return false;
+               }
+
+               final Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.WAYPT_NAME};
+               _app.autoAppendNextFile();
+
+               if (inArea.minLat == inArea.maxLat && inArea.minLon == inArea.maxLon)
+               {
+                       String[][] pointData = new String[1][];
+                       pointData[0] = new String[3]; // lat, long, name
+                       pointData[0][0] = "" + inArea.minLat;
+                       pointData[0][1] = "" + inArea.minLon;
+                       pointData[0][2] = _codeField.getText();
+                       _app.informDataLoaded(fields, pointData, null, null);
+               }
+               else
+               {
+                       String[][] pointData = new String[6][];
+                       for (int i=0; i<5; i++)
+                       {
+                               pointData[i] = new String[3]; // lat, long, name
+                               pointData[i][0] = "" + ((i%4==0 || i==3) ? inArea.minLat : inArea.maxLat);
+                               pointData[i][1] = "" + ((i%4==0 || i==1) ? inArea.minLon : inArea.maxLon);
+                               pointData[i][2] = null;
+                       }
+                       // Middle point with name
+                       pointData[5] = new String[3]; // lat, long, name
+                       pointData[5][0] = "" + ((inArea.minLat + inArea.maxLat) / 2.0);
+                       pointData[5][1] = "" + ((inArea.minLon + inArea.maxLon) / 2.0);
+                       pointData[5][2] = _codeField.getText();
+                       _app.informDataLoaded(fields, pointData, null, null);
+               }
+               return true;
+       }
+}
diff --git a/src/tim/prune/function/ProjectPoint.java b/src/tim/prune/function/ProjectPoint.java
new file mode 100644 (file)
index 0000000..ccdc509
--- /dev/null
@@ -0,0 +1,223 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+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.data.Unit;
+import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.DecimalNumberField;
+import tim.prune.gui.GuiGridLayout;
+import tim.prune.gui.WholeNumberField;
+
+
+/**
+ * Class to provide the function to project the current point
+ * with a given bearing and distance
+ */
+public class ProjectPoint extends GenericFunction
+{
+       private JDialog _dialog = null;
+       private WholeNumberField _bearingField = null;
+       private JLabel _distanceDescLabel = null;
+       private DecimalNumberField _distanceField = null;
+       private boolean _distanceIsMetric = true;
+       private JTextField _nameField = null;
+       private JButton _okButton = null;
+
+
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public ProjectPoint(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.projectpoint";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               // Make dialog window
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
+
+               // Clear fields
+               _bearingField.setText("");
+               _distanceField.setText("");
+               _nameField.setText("");
+               // Set the units of the distance label
+               setLabelText();
+               enableOK();
+               _dialog.setVisible(true);
+       }
+
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout(0, 10));
+               dialogPanel.add(new JLabel(I18nManager.getText("dialog.projectpoint.desc")), BorderLayout.NORTH);
+               JPanel mainPanel = new JPanel();
+               GuiGridLayout grid = new GuiGridLayout(mainPanel);
+               _bearingField = new WholeNumberField(3);
+               _distanceField = new DecimalNumberField(false);
+               // Listeners to enable/disable ok button
+               KeyAdapter keyListener = new KeyAdapter() {
+                       /** Key released */
+                       public void keyReleased(KeyEvent inE) {
+                               enableOK();
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
+                                       _dialog.dispose();
+                               }
+                       }
+               };
+               MouseAdapter mouseListener = new MouseAdapter() {
+                       public void mouseReleased(MouseEvent inE) {
+                               enableOK();
+                       }
+               };
+               _bearingField.addKeyListener(keyListener);
+               _bearingField.addMouseListener(mouseListener);
+               _distanceField.addKeyListener(keyListener);
+               _distanceField.addMouseListener(mouseListener);
+
+               JLabel bearingLabel = new JLabel(I18nManager.getText("dialog.projectpoint.bearing"));
+               bearingLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+               grid.add(bearingLabel);
+               grid.add(_bearingField);
+
+               // Distance including units
+               _distanceDescLabel = new JLabel(I18nManager.getText("fieldname.distance") + " (ft)");
+               // Note, this label will be reset at each run
+               _distanceDescLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+               grid.add(_distanceDescLabel);
+               grid.add(_distanceField);
+
+               // Waypoint name
+               JLabel nameLabel = new JLabel(I18nManager.getText("dialog.pointnameedit.name"));
+               nameLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+               grid.add(nameLabel);
+               _nameField = new JTextField("", 12);
+               grid.add(_nameField);
+               dialogPanel.add(mainPanel, BorderLayout.CENTER);
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               ActionListener okListener = new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               if (_okButton.isEnabled()) {finish();}
+                       }
+               };
+               _okButton.addActionListener(okListener);
+               _okButton.setEnabled(false);
+
+               buttonPanel.add(_okButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               return dialogPanel;
+       }
+
+       /**
+        * Set the label text according to the current units
+        */
+       private void setLabelText()
+       {
+               Unit distUnit = Config.getUnitSet().getDistanceUnit();
+               _distanceIsMetric = (distUnit == UnitSetLibrary.UNITS_METRES || distUnit == UnitSetLibrary.UNITS_KILOMETRES);
+               distUnit = _distanceIsMetric ? UnitSetLibrary.UNITS_METRES : UnitSetLibrary.UNITS_FEET;
+               final String unitKey = distUnit.getShortnameKey();
+               _distanceDescLabel.setText(I18nManager.getText("fieldname.distance") + " (" + I18nManager.getText(unitKey) + ")");
+       }
+
+       /**
+        * Enable or disable the OK button based on the contents of the input fields
+        */
+       private void enableOK()
+       {
+               final boolean bearingOk = !_bearingField.getText().isEmpty()
+                       && _bearingField.getValue() < 360;
+               final boolean distanceOk = _distanceField.getValue() > 0.0;
+               _okButton.setEnabled(bearingOk && distanceOk);
+       }
+
+       /**
+        * Finish the dialog when OK pressed
+        */
+       private void finish()
+       {
+               DataPoint currPoint = _app.getTrackInfo().getCurrentPoint();
+               Unit distUnit = _distanceIsMetric ? UnitSetLibrary.UNITS_METRES : UnitSetLibrary.UNITS_FEET;
+               final double projectRads = Distance.convertDistanceToRadians(_distanceField.getValue(), distUnit);
+               final double origLatRads = Math.toRadians(currPoint.getLatitude().getDouble());
+               final double origLonRads = Math.toRadians(currPoint.getLongitude().getDouble());
+               final double bearingRads = Math.toRadians(_bearingField.getValue());
+
+               double lat2 = Math.asin(Math.sin(origLatRads) * Math.cos(projectRads)
+                       + Math.cos(origLatRads) * Math.sin(projectRads) * Math.cos(bearingRads));
+               double lon2 = origLonRads + Math.atan2(Math.sin(bearingRads) * Math.sin(projectRads) * Math.cos(origLatRads),
+                       Math.cos(projectRads) - Math.sin(origLatRads) * Math.sin(lat2));
+
+               double finalLatDeg = Math.toDegrees(lat2);
+               double finalLonDeg = Math.toDegrees(lon2);
+
+               // Create point and append to track
+               DataPoint point = new DataPoint(new Latitude(finalLatDeg, Coordinate.FORMAT_DEG),
+                       new Longitude(finalLonDeg, Coordinate.FORMAT_DEG), null);
+               point.setFieldValue(Field.WAYPT_NAME, _nameField.getText(), false);
+               _app.createPoint(point);
+
+               _dialog.dispose();
+       }
+}
index 17e937027c2d021593979e7c54fefcdf4851f940..c62b13097875c1485209feb243fa623b69b767d0 100644 (file)
@@ -119,7 +119,7 @@ public class RearrangeWaypointsFunction extends RearrangeFunction
                // Exit if the data is already in the specified order
                final boolean wpsToStart = (inRearrangeOption == Rearrange.TO_START);
                final boolean doSort = (inSortOption != SortMode.DONT_SORT);
-               if (numWaypoints == 0 || numNonWaypoints == 0
+               if (numWaypoints == 0
                        || (wpsToStart && !wayAfterNon && nonAfterWay && !doSort)
                        || (!wpsToStart && wayAfterNon && !nonAfterWay && !doSort)
                        || inRearrangeOption == Rearrange.TO_NEAREST)
index 20e2fc81d4de00698f7265c4651212bf71ff740d..39df30a6657050ee8b50f9485f6e19679eaf38af 100644 (file)
@@ -84,7 +84,7 @@ public class SearchOpenCachingDeFunction extends GenericDownloaderFunction
        private void submitSearch(double inLat, double inLon)
        {
                // The only parameters are lat and long from the current point
-               String urlString = "http://opencaching.de/search.php?searchto=searchbydistance&showresult=1"
+               String urlString = "https://opencaching.de/search.php?searchto=searchbydistance&showresult=1"
                        + "&output=XML&sort=bydistance&lat=" + inLat
                        + "&lon=" + inLon + "&distance=" + MAX_DISTANCE + "&unit=km";
                // Parse the returned XML with a special handler
diff --git a/src/tim/prune/function/ShowFullDetails.java b/src/tim/prune/function/ShowFullDetails.java
new file mode 100644 (file)
index 0000000..8bba3d0
--- /dev/null
@@ -0,0 +1,419 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.util.TimeZone;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextArea;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+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;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Field;
+import tim.prune.data.Photo;
+import tim.prune.data.RangeStatsWithGradients;
+import tim.prune.data.Selection;
+import tim.prune.data.SpeedCalculator;
+import tim.prune.data.SpeedValue;
+import tim.prune.data.Track;
+import tim.prune.data.Unit;
+import tim.prune.data.UnitSet;
+import tim.prune.gui.CoordDisplay;
+import tim.prune.gui.DisplayUtils;
+
+
+/**
+ * Class to show the full point/range details in a separate popup
+ */
+public class ShowFullDetails extends GenericFunction
+{
+       private JDialog _dialog = null;
+       private JTabbedPane _tabs = null;
+       private JButton _okButton = null;
+
+       private JTextArea _pointTextArea = null;
+       private JTextArea _rangeTextArea = null;
+
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public ShowFullDetails(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.viewfulldetails";
+       }
+
+       /**
+        * 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();
+               }
+               updateDetails();
+               _dialog.setVisible(true);
+               _okButton.requestFocus();
+       }
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel mainPanel = new JPanel();
+               mainPanel.setLayout(new BorderLayout());
+
+               _tabs = new JTabbedPane();
+               mainPanel.add(_tabs, BorderLayout.CENTER);
+
+               JPanel pointPanel = new JPanel();
+               pointPanel.setLayout(new BorderLayout());
+               _pointTextArea = new JTextArea(I18nManager.getText("details.nopointselection"));
+               _pointTextArea.setEditable(false);
+               _pointTextArea.setLineWrap(true);
+               JScrollPane scrollPane = new JScrollPane(_pointTextArea);
+               scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+               scrollPane.setPreferredSize(new Dimension(500, 230));
+               pointPanel.add(scrollPane, BorderLayout.CENTER);
+               _tabs.add(I18nManager.getText("details.pointdetails"), pointPanel);
+
+               JPanel rangePanel = new JPanel();
+               rangePanel.setLayout(new BorderLayout());
+               _rangeTextArea = new JTextArea(I18nManager.getText("details.norangeselection"));
+               _rangeTextArea.setEditable(false);
+               _rangeTextArea.setLineWrap(true);
+               JScrollPane scrollPane2 = new JScrollPane(_rangeTextArea);
+               scrollPane2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+               scrollPane2.setPreferredSize(new Dimension(500, 230));
+               rangePanel.add(scrollPane2, BorderLayout.CENTER);
+               _tabs.add(I18nManager.getText("details.rangedetails"), rangePanel);
+
+               // OK button at the bottom
+               JPanel okPanel = new JPanel();
+               okPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               _okButton.addActionListener(new ActionListener()
+               {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                       }
+               });
+               _okButton.addKeyListener(new KeyListener() {
+                       public void keyPressed(KeyEvent e)
+                       {
+                               if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
+                       }
+                       public void keyTyped(KeyEvent e) {}
+                       public void keyReleased(KeyEvent e) {}
+               });
+               okPanel.add(_okButton);
+               mainPanel.add(okPanel, BorderLayout.SOUTH);
+               return mainPanel;
+       }
+
+
+       /**
+        * Update the labels with the current details
+        */
+       private void updateDetails()
+       {
+               if (_app.getTrackInfo().getCurrentPoint() != null)
+               {
+                       final String pointString = makePointDescription(_app.getTrackInfo().getTrack(),
+                               _app.getTrackInfo().getSelection().getCurrentPointIndex());
+                       _pointTextArea.setText(pointString);
+                       // Select point tab
+                       _tabs.setSelectedIndex(0);
+               }
+               else
+               {
+                       _pointTextArea.setText(I18nManager.getText("details.nopointselection"));
+                       // Select range tab
+                       _tabs.setSelectedIndex(1);
+               }
+
+               Selection selection = _app.getTrackInfo().getSelection();
+               if (selection.hasRangeSelected())
+               {
+                       RangeStatsWithGradients stats = new RangeStatsWithGradients(_app.getTrackInfo().getTrack(),
+                               selection.getStart(), selection.getEnd());
+                       SpeedValue maxSpeed = calculateMaxSpeed(_app.getTrackInfo().getTrack(),
+                               selection.getStart(), selection.getEnd());
+                       _rangeTextArea.setText(makeRangeDescription(stats, maxSpeed));
+               }
+               else
+               {
+                       _rangeTextArea.setText(I18nManager.getText("details.norangeselection"));
+               }
+       }
+
+       /**
+        * Calculate the maximum horizontal speed value in the given selection
+        * @param inTrack track object
+        * @param inStartIndex start of selection
+        * @param inEndIndex end of selection
+        * @return max speed, if any
+        */
+       private static SpeedValue calculateMaxSpeed(Track inTrack, int inStartIndex, int inEndIndex)
+       {
+               SpeedValue maxSpeed = new SpeedValue();
+               SpeedValue currSpeed = new SpeedValue();
+               for (int i=inStartIndex; i<=inEndIndex; i++)
+               {
+                       SpeedCalculator.calculateSpeed(inTrack, i, currSpeed);
+                       if (currSpeed.isValid() && (!maxSpeed.isValid() || currSpeed.getValue() > maxSpeed.getValue()))
+                       {
+                               maxSpeed.setValue(currSpeed.getValue());
+                       }
+               }
+               return maxSpeed;
+       }
+
+       /**
+        * @param inTrack current track
+        * @param inPointIndex current point index
+        * @return string describing point details
+        */
+       private static String makePointDescription(Track inTrack, int inPointIndex)
+       {
+               DataPoint point = inTrack.getPoint(inPointIndex);
+               if (point == null)
+               {
+                       return "";
+               }
+
+               final int coordDisplayFormat = Coordinate.getCoordinateFormatForDisplay(
+                       Config.getConfigInt(Config.KEY_COORD_DISPLAY_FORMAT));
+               StringBuffer result = new StringBuffer();
+               final String latStr = CoordDisplay.makeCoordinateLabel(point.getLatitude(), coordDisplayFormat);
+               final String lonStr = CoordDisplay.makeCoordinateLabel(point.getLongitude(), coordDisplayFormat);
+               addTextPair(result, "fieldname.latitude", latStr);
+               addTextPair(result, "fieldname.longitude", lonStr);
+               addTextPair(result, "fieldname.coordinates", latStr + ", " + lonStr);
+
+               if (point.hasAltitude())
+               {
+                       final Unit altUnit = Config.getUnitSet().getAltitudeUnit();
+                       addTextPair(result, "fieldname.altitude", "" + point.getAltitude().getValue(altUnit),
+                               I18nManager.getText(altUnit.getShortnameKey()));
+               }
+               if (point.hasTimestamp())
+               {
+                       TimeZone timezone = TimezoneHelper.getSelectedTimezone();
+                       addTextPair(result, "fieldname.date", point.getTimestamp().getDateText(timezone));
+                       addTextPair(result, "fieldname.timestamp", point.getTimestamp().getTimeText(timezone));
+               }
+
+               addTextPair(result, "fieldname.waypointname", point.getWaypointName());
+
+               addTextPair(result, "fieldname.description", point.getFieldValue(Field.DESCRIPTION));
+
+               addTextPair(result, "fieldname.comment", point.getFieldValue(Field.COMMENT));
+
+               addTextPair(result, "fieldname.waypointtype", point.getFieldValue(Field.WAYPT_TYPE));
+
+               // Speed can come from either timestamps and distances, or speed values in data
+               SpeedValue speedValue = new SpeedValue();
+               SpeedCalculator.calculateSpeed(inTrack, inPointIndex, speedValue);
+               UnitSet unitSet = Config.getUnitSet();
+               if (speedValue.isValid())
+               {
+                       final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
+                       String speed = DisplayUtils.roundedNumber(speedValue.getValue());
+                       addTextPair(result, "fieldname.speed", speed, speedUnitsStr);
+               }
+
+               // Now do the vertical speed in the same way
+               SpeedCalculator.calculateVerticalSpeed(inTrack, inPointIndex, speedValue);
+               if (speedValue.isValid())
+               {
+                       final String vSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
+                       String speed = DisplayUtils.roundedNumber(speedValue.getValue());
+                       addTextPair(result, "fieldname.verticalspeed", speed, vSpeedUnitsStr);
+               }
+
+               Photo currentPhoto = point.getPhoto();
+               if (currentPhoto != null)
+               {
+                       addTextPair(result, "details.photofile", currentPhoto.getName());
+                       addTextPair(result, "details.media.fullpath", currentPhoto.getFullPath());
+               }
+
+               AudioClip currentAudio = point.getAudio();
+               if (currentAudio != null)
+               {
+                       addTextPair(result, "details.audio.file", currentAudio.getName());
+                       addTextPair(result, "details.media.fullpath", currentAudio.getFullPath());
+               }
+
+               return result.toString();
+       }
+
+       /**
+        * Make the range description text
+        * @param inStats stats object
+        * @param inMaxSpeed maximum speed info
+        * @return string describing range
+        */
+       private static String makeRangeDescription(RangeStatsWithGradients inStats, SpeedValue inMaxSpeed)
+       {
+               StringBuffer result = new StringBuffer();
+               addTextPair(result, "details.track.points", "" + inStats.getNumPoints());
+               addTextPair(result, "details.range.numsegments", "" + inStats.getNumSegments());
+               final boolean hasMultipleSegments = (inStats.getNumSegments() > 1);
+
+               UnitSet unitSet = Config.getUnitSet();
+               final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
+               if (inMaxSpeed.isValid())
+               {
+                       final String maxSpeedStr = DisplayUtils.roundedNumber(inMaxSpeed.getValue()) + " " + speedUnitsStr;
+                       addTextPair(result, "details.range.maxspeed", maxSpeedStr);
+               }
+
+               addHeading(result, "dialog.fullrangedetails.colsegments");
+               final Unit distUnit = Config.getUnitSet().getDistanceUnit();
+               final String distUnitsStr = I18nManager.getText(distUnit.getShortnameKey());
+               final double movingDist = inStats.getMovingDistance();
+               addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(movingDist),
+                       distUnitsStr);
+               long numSecs = inStats.getMovingDurationInSeconds();
+               addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(numSecs));
+
+               if (numSecs > 0 && movingDist > 0.0)
+               {
+                       addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(movingDist/numSecs*3600.0),
+                               speedUnitsStr);
+                       addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (numSecs/movingDist)),
+                               "/ " + distUnitsStr);
+               }
+               final Unit altUnit = unitSet.getAltitudeUnit();
+               final String altUnitsStr = I18nManager.getText(altUnit.getShortnameKey());
+               if (inStats.getMovingAltitudeRange().hasRange())
+               {
+                       AltitudeRange altRange = inStats.getMovingAltitudeRange();
+                       addTextPair(result, "fieldname.altitude", "" + altRange.getMinimum(altUnit) + altUnitsStr + " "
+                               + I18nManager.getText("details.altitude.to") + " "
+                               + altRange.getMaximum(altUnit) + altUnitsStr);
+                       addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
+                       addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
+                       addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getMovingGradient()), "%");
+                       if (numSecs > 0)
+                       {
+                               final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
+                               final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getMovingVerticalSpeed());
+                               addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
+                       }
+               }
+
+               if (hasMultipleSegments)
+               {
+                       addHeading(result, "dialog.fullrangedetails.coltotal");
+                       final double totalDist = inStats.getTotalDistance();
+                       addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(totalDist), distUnitsStr);
+                       long totalSecs = inStats.getTotalDurationInSeconds();
+                       addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(totalSecs));
+                       if (totalSecs > 0 && totalDist > 0.0)
+                       {
+                               addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(totalDist/totalSecs*3600.0),
+                                       speedUnitsStr);
+                               addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (totalSecs/totalDist)),
+                                       "/ " + distUnitsStr);
+                       }
+                       if (inStats.getTotalAltitudeRange().hasRange())
+                       {
+                               AltitudeRange altRange = inStats.getTotalAltitudeRange();
+                               addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
+                               addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
+                               addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getTotalGradient()), "%");
+                               if (totalSecs > 0)
+                               {
+                                       final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
+                                       final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getTotalVerticalSpeed());
+                                       addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
+                               }
+                       }
+               }
+               return result.toString();
+       }
+
+       /**
+        * Add the label and value to the buffer
+        * @param inBuffer buffer to append to
+        * @param inLabelKey label key
+        * @param inValue value text
+        */
+       private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue)
+       {
+               addTextPair(inBuffer, inLabelKey, inValue, null);
+       }
+
+       /**
+        * Add the label and value to the buffer
+        * @param inBuffer buffer to append to
+        * @param inLabelKey label key
+        * @param inValue value text
+        * @param inUnits optional units string
+        */
+       private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue, String inUnits)
+       {
+               if (inValue != null && !inValue.equals(""))
+               {
+                       inBuffer.append(I18nManager.getText(inLabelKey));
+                       inBuffer.append(": ");
+                       inBuffer.append(inValue);
+                       if (inUnits != null && !inUnits.equals(""))
+                       {
+                               inBuffer.append(' ');
+                               inBuffer.append(inUnits);
+                       }
+                       inBuffer.append("\n");
+               }
+       }
+
+       /**
+        * Add a heading to the buffer
+        * @param inBuffer buffer to append to
+        * @param inLabelKey key for heading
+        */
+       private static void addHeading(StringBuffer inBuffer, String inLabelKey)
+       {
+               final String heading = I18nManager.getText(inLabelKey);
+               inBuffer.append('\n').append(heading).append('\n');
+               for (int i=0; i<heading.length(); i++)
+               {
+                       inBuffer.append('=');
+               }
+               inBuffer.append('\n');
+       }
+}
index 8e89360d7f79ba820f2743a001e319a42dd99a6c..1c127b3e109dd090a8f3bbff585766a20df8e17d 100644 (file)
@@ -147,7 +147,7 @@ public class CompressTrackFunction extends MarkAndDeleteFunction
                        public void actionPerformed(ActionEvent arg0)
                        {
                                preview();
-                       };
+                       }
                };
                // construct track details to be used by all algorithms
                TrackDetails details = new TrackDetails(_track);
index fbf0ccff49c550b79969cda539ec4b4bde008513..a949c1c16b8792c0a3e09890fdfe3c45561b66cb 100644 (file)
@@ -37,9 +37,9 @@ public abstract class SingleParameterAlgorithm extends CompressionAlgorithm
                _parameterField = new JTextField();
                // Add listener to parameter field to re-run preview (and en/disable ok) when param changed
                _parameterField.addKeyListener(new KeyListener() {
-                       public void keyTyped(java.awt.event.KeyEvent arg0) {};
-                       public void keyPressed(java.awt.event.KeyEvent arg0) {};
-                       public void keyReleased(java.awt.event.KeyEvent arg0) {if (isActivated()) _listener.actionPerformed(null);};
+                       public void keyTyped(java.awt.event.KeyEvent arg0) {}
+                       public void keyPressed(java.awt.event.KeyEvent arg0) {}
+                       public void keyReleased(java.awt.event.KeyEvent arg0) {if (isActivated()) _listener.actionPerformed(null);}
                });
        }
 
index 5fe6e7e7d9e307d901ad25c23b2a4ea021acc3d3..f37332420e5b662f2d472d4e76e1132725826cb3 100644 (file)
@@ -77,8 +77,9 @@ public class DistanceFunction extends GenericFunction
                }
                _fromModel.init(pointList);
                _distModel.init(pointList);
-               _pointTable.getSelectionModel().setSelectionInterval(0, 0);
-               _distModel.recalculate(0);
+               final int pointIndex = getPointIndex(pointList, _app.getTrackInfo());
+               _pointTable.getSelectionModel().setSelectionInterval(pointIndex, pointIndex);
+               _distModel.recalculate(pointIndex);
                _dialog.setVisible(true);
        }
 
@@ -114,12 +115,7 @@ public class DistanceFunction extends GenericFunction
                // second table for distances
                _distModel = new DistanceTableModel();
                JTable distTable = new JTable(_distModel);
-               // Use reflection to call distTable.setAutoCreateRowSorter(true) which is new with Java 1.6
-               try {
-                       Class<?> d = Class.forName("javax.swing.JTable");
-                       d.getDeclaredMethod("setAutoCreateRowSorter", new Class[]{Boolean.TYPE}).invoke(distTable, Boolean.TRUE);
-               }
-               catch (Exception e) {}
+               distTable.setAutoCreateRowSorter(true);
                scrollPane = new JScrollPane(distTable);
                scrollPane.setPreferredSize(new Dimension(200, 250));
                mainPanel.add(scrollPane);
@@ -168,4 +164,25 @@ public class DistanceFunction extends GenericFunction
                }
                return pointList;
        }
+
+       /**
+        * Find the point to select from the given point list
+        * @param pointList list of points
+        * @param inTrackInfo current track info to get selected point (if any)
+        * @return index of point to be selected
+        */
+       private static int getPointIndex(ArrayList<DataPoint> pointList, TrackInfo inTrackInfo)
+       {
+               DataPoint currPoint = inTrackInfo.getCurrentPoint();
+               if (currPoint != null && currPoint.isWaypoint())
+               {
+                       // Currently selected point is a waypoint, so select this one for convenience
+                       for (int i=0; i<pointList.size(); i++) {
+                               if (pointList.get(i) == currPoint) {
+                                       return i;
+                               }
+                       }
+               }
+               return 0;
+       }
 }
index 2c1d83d1337b56af1a8ab1fbad0a3cc938b3d5a2..b0340b9e5948f6735a2684488fc2ccea7dfb77c3 100644 (file)
@@ -37,7 +37,7 @@ public class DistanceTableModel extends GenericTableModel
        {
                if (inColumnIndex == 0) {return getPointName(inRowIndex);}
                if (_distances == null) {return 0.0;}
-               return new Double(_distances[inRowIndex]);
+               return Double.valueOf(_distances[inRowIndex]);
        }
 
        /**
index ef6979358edaefb64828469f18a2c41e6daf8846..8caad11892eb83dc219cd2a92bc0198dedb9456b 100644 (file)
@@ -23,7 +23,7 @@ import tim.prune.App;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.config.Config;
-import tim.prune.data.RangeStats;
+import tim.prune.data.RangeStatsWithGradients;
 import tim.prune.data.Selection;
 import tim.prune.data.Unit;
 import tim.prune.gui.DecimalNumberField;
@@ -54,7 +54,7 @@ public class EstimateTime extends GenericFunction
        private JLabel _descentParamLabel = null;
        private DecimalNumberField _gentleDescentField = null, _steepDescentField = null;
        /** Range stats */
-       private RangeStats _stats = null;
+       private RangeStatsWithGradients _stats = null;
 
 
        /**
@@ -78,7 +78,8 @@ public class EstimateTime extends GenericFunction
        {
                // Get the stats on the selection before launching the dialog
                Selection selection = _app.getTrackInfo().getSelection();
-               _stats = new RangeStats(_app.getTrackInfo().getTrack(), selection.getStart(), selection.getEnd());
+               _stats = new RangeStatsWithGradients(_app.getTrackInfo().getTrack(),
+                       selection.getStart(), selection.getEnd());
 
                if (_stats.getMovingDistance() < 0.01)
                {
@@ -297,7 +298,11 @@ public class EstimateTime extends GenericFunction
                EstimationParameters estParams = new EstimationParameters(Config.getConfigString(Config.KEY_ESTIMATION_PARAMS));
 
                String[] paramValues = estParams.getStrings();
-               if (paramValues == null || paramValues.length != 5) {return;} // TODO: What to do in case of error?
+               if (paramValues == null || paramValues.length != 5)
+               {
+                       // TODO: What to do in case of error?
+                       return;
+               }
                // Flat time is either for 5 km, 3 miles or 3 nm
                _flatSpeedLabel.setText(I18nManager.getText("dialog.estimatetime.parameters.timefor") +
                        " " + EstimationParameters.getStandardDistance() + ": ");
index 1b8046740b767b21bc8e55cdda4134831d092efa..afe59383da9c6d21962dcdfc0ae0b47748f83753 100644 (file)
@@ -6,7 +6,7 @@ import java.util.Locale;
 
 import tim.prune.I18nManager;
 import tim.prune.config.Config;
-import tim.prune.data.RangeStats;
+import tim.prune.data.RangeStatsWithGradients;
 import tim.prune.data.Unit;
 import tim.prune.data.UnitSetLibrary;
 
@@ -254,9 +254,11 @@ public class EstimationParameters
         * @param inStats stats of current range
         * @return estimated number of minutes required
         */
-       public double applyToStats(RangeStats inStats)
+       public double applyToStats(RangeStatsWithGradients inStats)
        {
-               if (inStats == null || !inStats.isValid()) return 0.0;
+               if (inStats == null) {
+                       return 0.0;
+               }
                final Unit METRES = UnitSetLibrary.UNITS_METRES;
                final double STANDARD_CLIMB = 100.0; // metres
                final double STANDARD_DISTANCE = 5.0; // kilometres
index 74021dd3b2ba46fd03e6fddbd9054b50ba6ae65a..3efaa2f33161808ac9bab161d4a9cb024dd9a8fa 100644 (file)
@@ -26,7 +26,7 @@ import tim.prune.I18nManager;
 import tim.prune.config.Config;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Distance;
-import tim.prune.data.RangeStats;
+import tim.prune.data.RangeStatsWithGradients;
 import tim.prune.data.Track;
 import tim.prune.data.Unit;
 import tim.prune.data.UnitSetLibrary;
@@ -100,7 +100,7 @@ public class LearnParameters extends GenericFunction implements Runnable
        {
                _progress.setMaximum(100);
                // Go through the track and collect the range stats for each sample
-               ArrayList<RangeStats> statsList = new ArrayList<RangeStats>(20);
+               ArrayList<RangeStatsWithGradients> statsList = new ArrayList<RangeStatsWithGradients>(20);
                Track track = _app.getTrackInfo().getTrack();
                final int numPoints = track.getNumPoints();
                final int sampleSize = numPoints / 30;
@@ -108,15 +108,15 @@ public class LearnParameters extends GenericFunction implements Runnable
                for (int i=0; i<30; i++)
                {
                        int startIndex = i * sampleSize;
-                       RangeStats stats = getRangeStats(track, startIndex, startIndex + sampleSize, prevStartIndex);
+                       RangeStatsWithGradients stats = getRangeStats(track, startIndex, startIndex + sampleSize, prevStartIndex);
                        if (stats != null && stats.getMovingDistanceKilometres() > 1.0
                                && !stats.getTimestampsIncomplete() && !stats.getTimestampsOutOfSequence()
                                && stats.getTotalDurationInSeconds() > 100
-                               && stats.getStartIndex() > prevStartIndex)
+                               && startIndex > prevStartIndex)
                        {
                                // System.out.println("Got stats for " + stats.getStartIndex() + " to " + stats.getEndIndex());
                                statsList.add(stats);
-                               prevStartIndex = stats.getStartIndex();
+                               prevStartIndex = startIndex;
                        }
                        _progress.setValue(i);
                }
@@ -246,7 +246,8 @@ public class LearnParameters extends GenericFunction implements Runnable
         * @param inPreviousStartIndex the previously used start index, or -1
         * @return range stats object or null if required information missing from this bit of the track
         */
-       private RangeStats getRangeStats(Track inTrack, int inStartIndex, int inEndIndex, int inPreviousStartIndex)
+       private RangeStatsWithGradients getRangeStats(Track inTrack, int inStartIndex,
+               int inEndIndex, int inPreviousStartIndex)
        {
                // Check parameters
                if (inTrack == null || inStartIndex < 0 || inEndIndex <= inStartIndex || inStartIndex > inTrack.getNumPoints()) {
@@ -297,7 +298,7 @@ public class LearnParameters extends GenericFunction implements Runnable
 
                // Check moving distance
                if (movingRads >= minimumRads) {
-                       return new RangeStats(inTrack, start, endIndex);
+                       return new RangeStatsWithGradients(inTrack, start, endIndex);
                }
                return null;
        }
@@ -307,12 +308,12 @@ public class LearnParameters extends GenericFunction implements Runnable
         * @param inStatsList list of (non-null) RangeStats objects
         * @return A matrix with n rows and 5 columns
         */
-       private static Matrix buildAMatrix(ArrayList<RangeStats> inStatsList)
+       private static Matrix buildAMatrix(ArrayList<RangeStatsWithGradients> inStatsList)
        {
                final Unit METRES = UnitSetLibrary.UNITS_METRES;
                Matrix result = new Matrix(inStatsList.size(), 5);
                int row = 0;
-               for (RangeStats stats : inStatsList)
+               for (RangeStatsWithGradients stats : inStatsList)
                {
                        result.setValue(row, 0, stats.getMovingDistanceKilometres());
                        result.setValue(row, 1, stats.getGentleAltitudeRange().getClimb(METRES));
@@ -329,11 +330,11 @@ public class LearnParameters extends GenericFunction implements Runnable
         * @param inStatsList list of (non-null) RangeStats objects
         * @return B matrix with single column of n rows
         */
-       private static Matrix buildBMatrix(ArrayList<RangeStats> inStatsList)
+       private static Matrix buildBMatrix(ArrayList<RangeStatsWithGradients> inStatsList)
        {
                Matrix result = new Matrix(inStatsList.size(), 1);
                int row = 0;
-               for (RangeStats stats : inStatsList)
+               for (RangeStatsWithGradients stats : inStatsList)
                {
                        result.setValue(row, 0, stats.getMovingDurationInSeconds() / 60.0); // convert seconds to minutes
                        row++;
@@ -372,12 +373,13 @@ public class LearnParameters extends GenericFunction implements Runnable
         * @param inRowToIgnore row index to ignore, or -1 to use them all
         * @return true if the samples look ok
         */
-       private static boolean isRangeSetSufficient(ArrayList<RangeStats> inRangeSet, int inRowToIgnore)
+       private static boolean isRangeSetSufficient(ArrayList<RangeStatsWithGradients> inRangeSet, int inRowToIgnore)
        {
-               int numGC = 0, numSC = 0, numGD = 0, numSD = 0; // number of samples with gentle/steep climb/descent values > 0
+               // number of samples with gentle/steep climb/descent values > 0
+               int numGC = 0, numSC = 0, numGD = 0, numSD = 0;
                final Unit METRES = UnitSetLibrary.UNITS_METRES;
                int i = 0;
-               for (RangeStats stats : inRangeSet)
+               for (RangeStatsWithGradients stats : inRangeSet)
                {
                        if (i != inRowToIgnore)
                        {
@@ -396,7 +398,7 @@ public class LearnParameters extends GenericFunction implements Runnable
         * @param inStatsList list of stats
         * @return results in an object
         */
-       private MatrixResults reduceSamples(ArrayList<RangeStats> inStatsList)
+       private MatrixResults reduceSamples(ArrayList<RangeStatsWithGradients> inStatsList)
        {
                int statsIndexToRemove = -1;
                Matrix answer = null;
diff --git a/src/tim/prune/function/gpsies/FormPoster.java b/src/tim/prune/function/gpsies/FormPoster.java
deleted file mode 100644 (file)
index 5939e63..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-package tim.prune.function.gpsies;\r
-\r
-import java.net.HttpURLConnection;\r
-import java.net.URLConnection;\r
-import java.net.URL;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.util.Random;\r
-import java.io.OutputStream;\r
-\r
-/**\r
- * Taken from the Client HTTP Request class at com.myjavatools.web\r
- * and subsequently simplified and modified\r
- * @author Vlad Patryshev\r
- */\r
-public class FormPoster\r
-{\r
-       private URLConnection _connection = null;\r
-       private OutputStream _os = null;\r
-       private static final Random RANDOM_GEN = new Random();\r
-       private static final String BOUNDARY = "---------------------------"\r
-               + randomString() + randomString() + randomString();\r
-\r
-\r
-       /** Connect (if not already connected) */\r
-       protected void connect() throws IOException {\r
-               if (_os == null) _os = _connection.getOutputStream();\r
-       }\r
-\r
-       /** Write a single character */\r
-       protected void write(char c) throws IOException {\r
-               connect();\r
-               _os.write(c);\r
-       }\r
-\r
-       /** Write a string */\r
-       protected void write(String s) throws IOException {\r
-               connect();\r
-               _os.write(s.getBytes());\r
-       }\r
-\r
-       /** Write a -r-n newline sequence */\r
-       protected void newline() throws IOException {\r
-               write("\r\n");\r
-       }\r
-\r
-       /** Write a string followed by a newline */\r
-       protected void writeln(String s) throws IOException {\r
-               write(s);\r
-               newline();\r
-       }\r
-\r
-       /** Generate a random alphanumeric string */\r
-       private static String randomString() {\r
-               return Long.toString(RANDOM_GEN.nextLong(), 36);\r
-       }\r
-\r
-       /** Write a boundary marker */\r
-       private void boundary() throws IOException {\r
-               write("--");\r
-               write(BOUNDARY);\r
-       }\r
-\r
-\r
-       /**\r
-        * Creates a new multipart POST HTTP request for a specified URL\r
-        * @param url the URL to send request to\r
-        * @throws IOException\r
-        */\r
-       public FormPoster(URL inUrl) throws IOException\r
-       {\r
-               _connection = inUrl.openConnection();\r
-               _connection.setDoOutput(true);\r
-               _connection.setRequestProperty("Content-Type",\r
-                       "multipart/form-data; boundary=" + BOUNDARY);\r
-       }\r
-\r
-       /** Write a header with the given name */\r
-       private void writeName(String inName) throws IOException\r
-       {\r
-               newline();\r
-               write("Content-Disposition: form-data; name=\"");\r
-               write(inName);\r
-               write('"');\r
-       }\r
-\r
-       /**\r
-        * adds a string parameter to the request\r
-        * @param name parameter name\r
-        * @param value parameter value\r
-        * @throws IOException\r
-        */\r
-       public void setParameter(String inName, String inValue) throws IOException\r
-       {\r
-               boundary();\r
-               writeName(inName);\r
-               newline(); newline();\r
-               writeln(inValue);\r
-       }\r
-\r
-       /** Pipe the contents of the input stream to the output stream */\r
-       private static void pipe(InputStream in, OutputStream out) throws IOException\r
-       {\r
-               byte[] buf = new byte[500000];\r
-               int nread;\r
-               synchronized (in) {\r
-                       while((nread = in.read(buf, 0, buf.length)) >= 0) {\r
-                               out.write(buf, 0, nread);\r
-                       }\r
-               }\r
-               out.flush();\r
-               buf = null;\r
-       }\r
-\r
-       /**\r
-        * adds a file parameter to the request\r
-        * @param inName parameter name\r
-        * @param inFilename the name of the file\r
-        * @param inStream input stream to read the contents of the file from\r
-        * @throws IOException\r
-        */\r
-       public void setParameter(String inName, String inFilename, InputStream inStream) throws IOException\r
-       {\r
-               boundary();\r
-               writeName(inName);\r
-               write("; filename=\"");\r
-               write(inFilename);\r
-               write('"');\r
-               newline();\r
-               write("Content-Type: ");\r
-               String type = URLConnection.guessContentTypeFromName(inFilename);\r
-               if (type == null) {type = "application/octet-stream";}\r
-               writeln(type);\r
-               newline();\r
-               pipe(inStream, _os);\r
-               newline();\r
-       }\r
-\r
-       /**\r
-        * posts the requests to the server\r
-        * @return input stream with the server response\r
-        * @throws IOException\r
-        */\r
-       public InputStream post() throws IOException\r
-       {\r
-               boundary();\r
-               writeln("--");\r
-               _os.close();\r
-               return _connection.getInputStream();\r
-       }\r
-\r
-       /**\r
-        * @return the HTTP response code, 200 for success or -1 if not available\r
-        */\r
-       public int getResponseCode() throws IOException\r
-       {\r
-               if (_connection != null && _connection instanceof HttpURLConnection) {\r
-                       return ((HttpURLConnection) _connection).getResponseCode();\r
-               }\r
-               return -1;\r
-       }\r
-}\r
diff --git a/src/tim/prune/function/gpsies/GetGpsiesFunction.java b/src/tim/prune/function/gpsies/GetGpsiesFunction.java
deleted file mode 100644 (file)
index cdcc9a9..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-package tim.prune.function.gpsies;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import tim.prune.App;
-import tim.prune.GpsPrune;
-import tim.prune.I18nManager;
-import tim.prune.function.search.GenericDownloaderFunction;
-import tim.prune.function.search.SearchResult;
-import tim.prune.load.xml.XmlFileLoader;
-import tim.prune.load.xml.ZipFileLoader;
-
-/**
- * Function to load track information from Gpsies.com
- * according to the currently viewed area
- */
-public class GetGpsiesFunction extends GenericDownloaderFunction
-{
-       /** Number of results per page */
-       private static final int RESULTS_PER_PAGE = 20;
-       /** Maximum number of results to get */
-       private static final int MAX_RESULTS = 60;
-       /** New API key (specific to this program) */
-       private static final String GPSIES_API_KEY = "oumgvvbckiwpvsnb";
-
-
-       /**
-        * Constructor
-        * @param inApp App object
-        */
-       public GetGpsiesFunction(App inApp) {
-               super(inApp);
-       }
-
-       /**
-        * @return name key
-        */
-       public String getNameKey() {
-               return "function.getgpsies";
-       }
-
-       /**
-        * @param inColNum index of column, 0 or 1
-        * @return key for this column
-        */
-       protected String getColumnKey(int inColNum)
-       {
-               if (inColNum == 0) return "dialog.gpsies.column.name";
-               return "dialog.gpsies.column.length";
-       }
-
-
-       /**
-        * Run method to call gpsies.com in separate thread
-        */
-       public void run()
-       {
-               _statusLabel.setText(I18nManager.getText("confirm.running"));
-               // Act on callback to update list and send another request if necessary
-               double[] coords = _app.getViewport().getBounds();
-               int currPage = 1;
-
-               ArrayList<SearchResult> trackList = null;
-               URL url = null;
-               String descMessage = "";
-               InputStream inStream = null;
-               // Loop for each page of the results
-               do
-               {
-                       // Example http://ws.gpsies.com/api.do?BBOX=10,51,12,53&limit=20&resultPage=1&key=oumgvvbckiwpvsnb
-                       String urlString = "http://ws.gpsies.com/api.do?BBOX=" +
-                               coords[1] + "," + coords[0] + "," + coords[3] + "," + coords[2] +
-                               "&limit=" + RESULTS_PER_PAGE + "&resultPage=" + currPage +
-                               "&key=" + GPSIES_API_KEY;
-                       // Parse the returned XML with a special handler
-                       GpsiesXmlHandler xmlHandler = new GpsiesXmlHandler();
-                       try
-                       {
-                               url = new URL(urlString);
-                               SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
-                               URLConnection conn = url.openConnection();
-                               conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
-                               inStream = conn.getInputStream();
-                               saxParser.parse(inStream, xmlHandler);
-                       }
-                       catch (Exception e) {
-                               descMessage = e.getClass().getName() + " - " + e.getMessage();
-                       }
-                       // Close stream and ignore errors
-                       try {
-                               inStream.close();
-                       } catch (Exception e) {}
-                       // Add track list to model
-                       trackList = xmlHandler.getTrackList();
-                       _trackListModel.addTracks(trackList);
-
-                       // Compare number of results with results per page and call again if necessary
-                       currPage++;
-               }
-               while (trackList != null && trackList.size() == RESULTS_PER_PAGE
-                       && _trackListModel.getRowCount() < MAX_RESULTS && !_cancelled);
-               // Set status label according to error or "none found", leave blank if ok
-               if (descMessage.equals("") && (trackList == null || trackList.size() == 0)) {
-                       descMessage = I18nManager.getText("dialog.gpsies.nonefound");
-               }
-               _statusLabel.setText(descMessage);
-       }
-
-       /**
-        * Load the selected track(s)
-        */
-       protected void loadSelected()
-       {
-               // Find the row(s) selected in the table and get the corresponding track
-               int numSelected = _trackTable.getSelectedRowCount();
-               if (numSelected < 1) return;
-               int[] rowNums = _trackTable.getSelectedRows();
-               for (int i=0; i<numSelected; i++)
-               {
-                       int rowNum = rowNums[i];
-                       if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
-                       {
-                               String url = _trackListModel.getTrack(rowNum).getDownloadLink();
-                               XmlFileLoader xmlLoader = new XmlFileLoader(_app);
-                               ZipFileLoader loader = new ZipFileLoader(_app, xmlLoader);
-                               if (i>0) _app.autoAppendNextFile();
-                               try
-                               {
-                                       loader.openStream(new URL(url).openStream());
-                               }
-                               catch (IOException ioe) {
-                                       System.err.println("IO Exception : " + ioe.getMessage());
-                               }
-                       }
-               }
-               // Close the dialog
-               _cancelled = true;
-               _dialog.dispose();
-       }
-}
diff --git a/src/tim/prune/function/gpsies/GpsiesXmlHandler.java b/src/tim/prune/function/gpsies/GpsiesXmlHandler.java
deleted file mode 100644 (file)
index c549c59..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-package tim.prune.function.gpsies;
-
-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 gpsies.com
- */
-public class GpsiesXmlHandler extends DefaultHandler
-{
-       private String _value = null;
-       private ArrayList<SearchResult> _trackList = null;
-       private SearchResult _track = 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("tracks")) {
-                       _trackList = new ArrayList<SearchResult>();
-               }
-               else if (inTagName.equals("track")) {
-                       _track = new SearchResult();
-               }
-               _value = null;
-               super.startElement(inUri, inLocalName, inTagName, inAttributes);
-       }
-
-       /**
-        * React to the end of an XML tag
-        */
-       public void endElement(String inUri, String inLocalName, String inTagName)
-       throws SAXException
-       {
-               if (inTagName.equals("track")) {
-                       _trackList.add(_track);
-               }
-               else if (inTagName.equals("title")) {
-                       _track.setTrackName(_value);
-               }
-               else if (inTagName.equals("description")) {
-                       _track.setDescription(_value);
-               }
-               else if (inTagName.equals("fileId")) {
-                       _track.setWebUrl("https://gpsies.com/map.do?fileId=" + _value);
-               }
-               else if (inTagName.equals("trackLengthM")) {
-                       try {
-                               _track.setLength(Double.parseDouble(_value));
-                       }
-                       catch (NumberFormatException nfe) {}
-               }
-               else if (inTagName.equals("downloadLink")) {
-                       _track.setDownloadLink(_value);
-               }
-               super.endElement(inUri, inLocalName, inTagName);
-       }
-
-       /**
-        * React to characters received inside tags
-        */
-       public void characters(char[] inCh, int inStart, int inLength)
-       throws SAXException
-       {
-               String value = new String(inCh, inStart, inLength);
-               _value = (_value==null?value:_value+value);
-               super.characters(inCh, inStart, inLength);
-       }
-
-       /**
-        * @return the list of tracks
-        */
-       public ArrayList<SearchResult> getTrackList()
-       {
-               return _trackList;
-       }
-}
diff --git a/src/tim/prune/function/gpsies/UploadGpsiesFunction.java b/src/tim/prune/function/gpsies/UploadGpsiesFunction.java
deleted file mode 100644 (file)
index 3cef9db..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-package tim.prune.function.gpsies;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.FlowLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JDialog;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JPasswordField;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-import javax.swing.JTextField;
-import javax.swing.border.EtchedBorder;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-
-import tim.prune.App;
-import tim.prune.GenericFunction;
-import tim.prune.I18nManager;
-import tim.prune.function.browser.BrowserLauncher;
-import tim.prune.gui.GuiGridLayout;
-import tim.prune.save.GpxExporter;
-import tim.prune.save.SettingsForExport;
-
-/**
- * Function to upload track information up to Gpsies.com
- */
-public class UploadGpsiesFunction extends GenericFunction
-{
-       /** Dialog object */
-       private JDialog _dialog = null;
-       /** Edit box for user name */
-       private JTextField _usernameField = null;
-       /** Edit box for password */
-       private JPasswordField _passwordField = null;
-       /** Name of track */
-       private JTextField _nameField = null;
-       /** Description */
-       private JTextArea _descField = null;
-       /** Private checkbox */
-       private JCheckBox _privateCheckbox = null;
-       /** Activity checkboxes */
-       private JCheckBox[] _activityCheckboxes = null;
-       /** Writer object for GPX export */
-       private OutputStreamWriter _writer = null;
-       /** upload button */
-       private JButton _uploadButton = null;
-
-       /** URL to post form to */
-       private static final String GPSIES_URL = "http://www.gpsies.com/upload.do";
-       /** Keys for describing activities */
-       private static final String[] ACTIVITY_KEYS = {"trekking", "walking", "jogging",
-               "biking", "motorbiking", "snowshoe", "sailing", "skating"};
-
-       /**
-        * Constructor
-        * @param inApp App object
-        */
-       public UploadGpsiesFunction(App inApp) {
-               super(inApp);
-       }
-
-       /**
-        * @return name key
-        */
-       public String getNameKey() {
-               return "function.uploadgpsies";
-       }
-
-       /**
-        * Begin the function
-        */
-       public void begin()
-       {
-               // Initialise dialog, show empty list
-               if (_dialog == null)
-               {
-                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
-                       _dialog.setLocationRelativeTo(_parentFrame);
-                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
-                       _dialog.getContentPane().add(makeDialogComponents());
-                       _dialog.pack();
-               }
-               // Show dialog
-               _dialog.setVisible(true);
-       }
-
-
-       /**
-        * Create dialog components
-        * @return Panel containing all gui elements in dialog
-        */
-       private Component makeDialogComponents()
-       {
-               JPanel dialogPanel = new JPanel();
-               dialogPanel.setLayout(new BorderLayout());
-
-               JPanel gridPanel = new JPanel();
-               GuiGridLayout grid = new GuiGridLayout(gridPanel);
-               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.username")));
-               _usernameField = new JTextField(15);
-               grid.add(_usernameField);
-               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.password")));
-               _passwordField = new JPasswordField(15);
-               grid.add(_passwordField);
-               // Track name and description
-               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.column.name")));
-               _nameField = new JTextField(15);
-               grid.add(_nameField);
-               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.description")));
-               _descField = new JTextArea(5, 15);
-               _descField.setLineWrap(true);
-               _descField.setWrapStyleWord(true);
-               grid.add(new JScrollPane(_descField));
-               // Listener on all these text fields to enable/disable the ok button
-               KeyAdapter keyListener = new KeyAdapter() {
-                       /** Key released */
-                       public void keyReleased(KeyEvent inE) {
-                               enableOK();
-                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
-                                       _dialog.dispose();
-                               }
-                       }
-               };
-               _usernameField.addKeyListener(keyListener);
-               _passwordField.addKeyListener(keyListener);
-               _nameField.addKeyListener(keyListener);
-               // Listen for tabs on description field, to change focus not enter tabs
-               _descField.addKeyListener(new KeyAdapter() {
-                       /** Key pressed */
-                       public void keyPressed(KeyEvent inE) {
-                               if (inE.getKeyCode() == KeyEvent.VK_TAB) {
-                                       inE.consume();
-                                       if (inE.isShiftDown()) {
-                                               _nameField.requestFocusInWindow();
-                                       }
-                                       else {
-                                               _privateCheckbox.requestFocusInWindow();
-                                       }
-                               }
-                       }
-               });
-               // Listen for Ctrl-backspace on password field (delete contents)
-               _passwordField.addKeyListener(new KeyAdapter() {
-                       /** Key released */
-                       public void keyReleased(KeyEvent inE) {
-                               if (inE.isControlDown() && (inE.getKeyCode() == KeyEvent.VK_BACK_SPACE
-                                       || inE.getKeyCode() == KeyEvent.VK_DELETE)) {
-                                       _passwordField.setText("");
-                               }
-                       }
-               });
-               // Checkbox for private / public
-               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.keepprivate")));
-               _privateCheckbox = new JCheckBox();
-               _privateCheckbox.setSelected(true);
-               grid.add(_privateCheckbox);
-
-               // panel for activity type checkboxes
-               JPanel activityPanel = new JPanel();
-               activityPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
-               ChangeListener checkListener = new ChangeListener() {
-                       public void stateChanged(ChangeEvent arg0) {
-                               enableOK();
-                       }
-               };
-               // Why not a simple grid layout here?
-               GuiGridLayout actGrid = new GuiGridLayout(activityPanel, new double[] {1.0, 1.0}, new boolean[] {false, false});
-               final int numActivities = ACTIVITY_KEYS.length;
-               _activityCheckboxes = new JCheckBox[numActivities];
-               for (int i=0; i<numActivities; i++)
-               {
-                       _activityCheckboxes[i] = new JCheckBox(I18nManager.getText("dialog.gpsies.activity." + ACTIVITY_KEYS[i]));
-                       _activityCheckboxes[i].addChangeListener(checkListener);
-                       actGrid.add(_activityCheckboxes[i]);
-               }
-               grid.add(new JLabel(I18nManager.getText("dialog.gpsies.activities")));
-               grid.add(activityPanel);
-               JPanel midPanel = new JPanel();
-               midPanel.setLayout(new BoxLayout(midPanel, BoxLayout.Y_AXIS));
-               midPanel.add(gridPanel);
-               dialogPanel.add(midPanel, BorderLayout.CENTER);
-
-               // button panel at bottom
-               JPanel buttonPanel = new JPanel();
-               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
-               _uploadButton = new JButton(I18nManager.getText("button.upload"));
-               _uploadButton.setEnabled(false);
-               _uploadButton.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               startUpload();
-                       }
-               });
-               buttonPanel.add(_uploadButton);
-               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
-               cancelButton.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               _dialog.dispose();
-                       }
-               });
-               buttonPanel.add(cancelButton);
-               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
-               dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
-               return dialogPanel;
-       }
-
-
-       /**
-        * Check the inputs and enable or disable the upload button
-        */
-       private void enableOK()
-       {
-               // Check for lengths of input fields - only username, password and filename are required
-               boolean ok = (_usernameField.getText().length() > 0 && _nameField.getText().length() > 0);
-               if (ok) {
-                       // also check password field
-                       char[] pass = _passwordField.getPassword();
-                       ok = pass.length > 0;
-                       for (int i=0; i<pass.length; i++) {pass[i] = '0';} // recommended by javadoc
-                       if (ok) {
-                               ok = false;
-                               for (int i=0; i<_activityCheckboxes.length; i++) {
-                                       ok = ok || _activityCheckboxes[i].isSelected();
-                               }
-                       }
-               }
-               _uploadButton.setEnabled(ok);
-       }
-
-
-       /**
-        * Start the upload process (require separate thread?)
-        */
-       private void startUpload()
-       {
-               BufferedReader reader = null;
-               try
-               {
-                       FormPoster poster = new FormPoster(new URL(GPSIES_URL));
-                       poster.setParameter("device", "Prune");
-                       poster.setParameter("username", _usernameField.getText());
-                       poster.setParameter("password", new String(_passwordField.getPassword()));
-                       boolean hasActivity = false;
-                       for (int i=0; i<ACTIVITY_KEYS.length; i++)
-                       {
-                               if (_activityCheckboxes[i].isSelected()) {
-                                       hasActivity = true;
-                                       poster.setParameter("trackTypes", ACTIVITY_KEYS[i]);
-                               }
-                       }
-                       if (!hasActivity) {poster.setParameter("trackTypes", "walking");} // default if none given
-                       poster.setParameter("filename", _nameField.getText());
-                       poster.setParameter("fileDescription", _descField.getText());
-                       poster.setParameter("startpointCountry", "DE");
-                       poster.setParameter("endpointCountry", "DE"); // both those will be corrected by gpsies
-                       poster.setParameter("status", (_privateCheckbox.isSelected()?"3":"1"));
-                       poster.setParameter("submit", "speichern"); // required
-                       // Use Pipes to connect the GpxExporter's output with the FormPoster's input
-                       PipedInputStream iStream = new PipedInputStream();
-                       PipedOutputStream oStream = new PipedOutputStream(iStream);
-                       _writer = new OutputStreamWriter(oStream);
-                       new Thread(new Runnable() {
-                               public void run() {
-                                       try {
-                                               GpxExporter.exportData(_writer, _app.getTrackInfo(), _nameField.getText(),
-                                                       null, new SettingsForExport(), null);
-                                       } catch (IOException e) {}
-                                       finally {
-                                               try {_writer.close();} catch (IOException e) {}
-                                       }
-                               }
-                       }).start();
-                       poster.setParameter("formFile", "filename.gpx", iStream);
-
-                       BufferedInputStream answer = new BufferedInputStream(poster.post());
-                       int response = poster.getResponseCode();
-                       reader = new BufferedReader(new InputStreamReader(answer));
-                       String line = reader.readLine();
-                       // Try to extract gpsies page url from the returned message
-                       String pageUrl = null;
-                       if (response == 200 && line.substring(0, 2).toUpperCase().equals("OK"))
-                       {
-                               final int bracketPos = line.indexOf('[');
-                               if (bracketPos > 0 && line.endsWith("]")) {
-                                       pageUrl = line.substring(bracketPos + 1, line.length()-1);
-                               }
-                       }
-                       if (pageUrl != null)
-                       {
-                               // OK received and managed to extract a Url from the return message.
-                               int userChoice = JOptionPane.showConfirmDialog(_app.getFrame(),
-                                       I18nManager.getText("dialog.gpsies.confirmopenpage"),
-                                       I18nManager.getText(getNameKey()), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
-                               if (userChoice == JOptionPane.OK_OPTION) {
-                                       BrowserLauncher.launchBrowser(pageUrl);
-                               }
-                       }
-                       else {
-                               _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.gpsies.uploadnotok")
-                                       + ": " + line);
-                       }
-               }
-               catch (MalformedURLException e) {}
-               catch (IOException ioe) {
-                       _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.gpsies.uploadfailed") + ": "
-                               + ioe.getClass().getName() + " : " + ioe.getMessage());
-               }
-               finally {
-                       try {if (reader != null) reader.close();} catch (IOException e) {}
-               }
-               _dialog.dispose();
-       }
-}
diff --git a/src/tim/prune/function/olc/CoordPair.java b/src/tim/prune/function/olc/CoordPair.java
new file mode 100644 (file)
index 0000000..778b46d
--- /dev/null
@@ -0,0 +1,75 @@
+package tim.prune.function.olc;
+
+class ParseException extends Exception {}
+
+/**
+ * Pair of coordinates
+ */
+class CoordPair
+{
+       /** Alphabet of allowed characters */
+       private static final String ALPHABET = "23456789CFGHJMPQRVWX";
+
+       public double lat = 0.0;
+       public double lon = 0.0;
+
+       /** Constructor */
+       public CoordPair(double inLat, double inLon)
+       {
+               lat = inLat;
+               lon = inLon;
+       }
+
+       /** Constant pair to represent padding */
+       public static CoordPair PADDING = new CoordPair(-1.0, -1.0);
+
+       /**
+        * Try to parse the given pair of characters into a CoordPair
+        * @param inFirst first character of pair
+        * @param inSecond second character of pair
+        * @return CoordPair from (0, 0) to (19/20, 19/20)
+        * @throws ParseException
+        */
+       public static CoordPair decode(char inFirst, char inSecond) throws ParseException
+       {
+               final boolean isFirstPadding = (inFirst == '0');
+               final boolean isSecondPadding = (inSecond == '0');
+               if (isFirstPadding && isSecondPadding) {return CoordPair.PADDING;}
+               if (isFirstPadding || isSecondPadding) {throw new ParseException();}
+               // Try to turn these characters into numbers
+               final double lat = decodeChar(inFirst);
+               final double lon = decodeChar(inSecond);
+               return new CoordPair(lat / 20.0, lon / 20.0);
+       }
+
+       /**
+        * Try to parse the given single character into a CoordPair
+        * @param inChar single character from level 11
+        * @return CoordPair from (0, 0) to (19/20, 19/20)
+        * @throws ParseException
+        */
+       public static CoordPair decode(char inChar) throws ParseException
+       {
+               // Try to turn this character into a number
+               final int charIndex = decodeChar(inChar);
+               final int lat = charIndex / 4;
+               final int lon = charIndex % 4;
+               return new CoordPair(lat / 5.0, lon / 4.0);
+       }
+
+       /**
+        * Get the index from the given character
+        * @param inChar character from OLC
+        * @return index from 0 to 19
+        * @throws ParseException if character not found
+        */
+       private static int decodeChar(char inChar) throws ParseException
+       {
+               final int index = ALPHABET.indexOf(inChar);
+               if (index < 0)
+               {
+                       throw new ParseException();
+               }
+               return index;
+       }
+}
diff --git a/src/tim/prune/function/olc/OlcArea.java b/src/tim/prune/function/olc/OlcArea.java
new file mode 100644 (file)
index 0000000..bc7a3e8
--- /dev/null
@@ -0,0 +1,21 @@
+package tim.prune.function.olc;
+
+/**
+ * Class to represent the result of an OLC decoding
+ */
+public class OlcArea
+{
+       public double minLat = 0.0;
+       public double maxLat = 0.0;
+       public double minLon = 0.0;
+       public double maxLon = 0.0;
+
+       /** Constructor */
+       public OlcArea(double inMinLat, double inMinLon, double inMaxLat, double inMaxLon)
+       {
+               minLat = inMinLat;
+               minLon = inMinLon;
+               maxLat = inMaxLat;
+               maxLon = inMaxLon;
+       }
+}
diff --git a/src/tim/prune/function/olc/OlcDecoder.java b/src/tim/prune/function/olc/OlcDecoder.java
new file mode 100644 (file)
index 0000000..122f6fe
--- /dev/null
@@ -0,0 +1,101 @@
+package tim.prune.function.olc;
+
+
+/**
+ * Decoder of OLC (Open Location Code) strings
+ */
+public class OlcDecoder
+{
+       /**
+        * Decode the given String into an OlcArea object
+        * @param inCode code representing an OLC
+        * @return an OlcArea object, or null if parsing failed
+        */
+       public static OlcArea decode(String inCode)
+       {
+               if (inCode == null || inCode.length() < 8) {
+                       return null;
+               }
+               String code = inCode.trim().toUpperCase();
+               if (code.length() < 8 || code.length() > 12) {
+                       return null;
+               }
+               double lat = 0.0, lon = 0.0;
+               double resolution = 400.0;
+               int charPos = 0;
+               int numSteps = 0;
+               boolean amPadding = false;
+               try
+               {
+                       while (charPos < inCode.length())
+                       {
+                               if (charPos == 0 || charPos == 2 || charPos == 4 || charPos == 6 || charPos == 9)
+                               {
+                                       // take next two characters, make pair, position += 2
+                                       CoordPair pair = CoordPair.decode(code.charAt(charPos), code.charAt(charPos+1));
+                                       if (pair == CoordPair.PADDING) {
+                                               amPadding = true;
+                                       }
+                                       else if (amPadding)
+                                       {
+                                               return null;
+                                       }
+                                       else
+                                       {
+                                               // Add to current lat, lon
+                                               lat += (pair.lat * resolution);
+                                               lon += (pair.lon * resolution);
+                                               numSteps++;
+                                               resolution /= 20.0;
+                                       }
+                                       charPos += 2;
+                               }
+                               else if (charPos == 8)
+                               {
+                                       if (code.charAt(charPos) != '+')
+                                       {
+                                               return null;
+                                       }
+                                       charPos += 1;
+                               }
+                               else if (charPos == 11)
+                               {
+                                       // take next character, make pair
+                                       CoordPair pair = CoordPair.decode(code.charAt(charPos));
+                                       // Add to current lat, lon
+                                       lat += (pair.lat * resolution);
+                                       lon += (pair.lon * resolution);
+                                       charPos += 1;
+                                       numSteps++;
+                                       resolution /= 20.0;
+                               }
+                               else
+                               {
+                                       return null;
+                               }
+                       }
+
+                       // Make OlcArea object and return it
+                       if (numSteps < 1)
+                       {
+                               return null;
+                       }
+                       else if (numSteps < 6)
+                       {
+                               // make four points
+                               lat -= 90.0;
+                               lon -= 180.0;
+                               return new OlcArea(lat, lon, lat+resolution, lon+resolution);
+                       }
+                       else
+                       {
+                               // make single point:
+                               lat -= 90.0;
+                               lon -= 180.0;
+                               return new OlcArea(lat, lon, lat+resolution*2.5, lon+resolution*2.0);
+                       }
+               }
+               catch (ParseException e) {}
+               return null;
+       }
+}
\ No newline at end of file
index fa8d4c33d90bf43838f19f039d9b510226fccf51..079bf9d11e0ce70852fd21131aa5f4ced0dcd72a 100644 (file)
@@ -25,11 +25,10 @@ import tim.prune.App;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.function.browser.BrowserLauncher;
-import tim.prune.function.gpsies.TrackListModel;
 
 /**
  * Function to load track information from any source,
- * subclassed for special cases like gpsies, wikipedia or OSM
+ * subclassed for special cases like wikipedia or OSM
  */
 public abstract class GenericDownloaderFunction extends GenericFunction implements Runnable
 {
index d757b017a2d6456e9e43475418116c84e6144ec3..52b902f85e08f04b7d02f74ff239514427c34aaf 100644 (file)
@@ -1,7 +1,7 @@
 package tim.prune.function.search;
 
 /**
- * Class to hold a search result from wikipedia / gpsies etc
+ * Class to hold a search result from wikipedia or other online service
  */
 public class SearchResult implements Comparable<SearchResult>
 {
similarity index 95%
rename from src/tim/prune/function/gpsies/TrackListModel.java
rename to src/tim/prune/function/search/TrackListModel.java
index 850921303801d33878c6b429ec2709087ed7fd6a..d9163be25cb894c27ea7feadf47cad7909bbc223 100644 (file)
@@ -1,4 +1,4 @@
-package tim.prune.function.gpsies;
+package tim.prune.function.search;
 
 import java.text.NumberFormat;
 import java.util.ArrayList;
@@ -9,10 +9,9 @@ import javax.swing.table.AbstractTableModel;
 import tim.prune.I18nManager;
 import tim.prune.config.Config;
 import tim.prune.data.Unit;
-import tim.prune.function.search.SearchResult;
 
 /**
- * Model for list of tracks from a search result (eg gpsies.com, geonames, overpass)
+ * Model for list of tracks from a search result (eg geonames, overpass)
  */
 public class TrackListModel extends AbstractTableModel
 {
index 364723712a0c785fabb6de8d9c6643d55fabef40..6b1ba55192f6dbb68666bab73c72a3fc20121539 100644 (file)
+116/122/A-180 Z light pillbox          49 57 30.7 N    14 05 23.85 E
+A-4/51/A-200 Y light pillbox   Back to 1938 state restored Pillbox Type 37 built in a unique modification into road embankment between Dolní Bezděkov and Bratronice, Kladno District, Czech Republic.       50 04 47.94 N   14 02 01.85 E
 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      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.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 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 Montmajour           43 42 20.16 N   4 39 50.04 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
+Abbaye Saint Wandrille Monastery Saint Wandrille in France     49 31 46.42 N   0 45 59.58 E
+Abeilhan       Abeilhan is a commune of the Hérault département in the Region of Occitanie - France. 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
+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
 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
+Adissan        Adissan is a commune of the Hérault département in the Region of Occitanie - France.  43 32 09.96 N   3 25 45.12 E
+Aérodrome de Besançon-Thise          47 16 25.68 N   6 04 59.09 E
+A Estrada      A Estrada is a municipality in Galicia, Spain in the province of Pontevedra.    41 32 35.1 N    2 12 59.63 E
+Agel   Agel is a commune of the Hérault département - France.        43 20 18.96 N   2 51 13.68 E
 Agrón, Ames           42 54 20.32 N   8 41 43.21 W
+A Guarda       A Guarda is a municipality in Galicia, in the province of Pontevedra.   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
 Aguiño, Ribeira               42 31 24.99 N   9 00 57.6 W
-Aichi prefecture               35 04 59 N      136 58 59 E
 Aiguafreda     Montseny        41 46 05.2 N    2 15 05.39 E
+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
 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
+Alte Kanzlei, Stuttgart                48 46 39.1 N    9 10 42.97 E
 Alt Katholische Christuskirche Offenbach               50 06 01.91 N   8 45 52.28 E
+A Merca        A Merca is a municipality in Galicia, in the province of Ourense.       42 13 22.53 N   7 54 15.55 W
 Ames           42 51 35.95 N   8 38 58.21 W
+A Mezquita             42 00 42.5 N    7 02 43.17 W
 Amoeiro                42 24 37.45 N   7 57 15.38 W
 Ansemil, Silleda               42 44 21.56 N   8 16 12.47 W
 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
+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
+Aquis Querquennis              41 58 12 N      7 58 48 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 Triomf de Barcelona             41 23 28 N      2 10 50.02 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 Constantine            41 53 22.92 N   12 29 26.88 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
+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
+Askersund      Askersund is a town in Sweden.  58 53 00 N      14 54 00 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
+Äußerer Plauenscher Friedhof Cemetery „Äußerer Plauenscher Friedhof“ in Dresden-Plauen 51 01 11.89 N   13 42 26.03 E
 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
+A Veiga        A Veiga is a municipality in Galicia, in the province of Ourense.       42 15 01.36 N   7 01 30.25 W
 Avión Avión is a municipality in Galicia, in the province of Ourense.        42 22 23.73 N   8 15 02.64 W
+A Walk on Main Street, Ferndale, California    Ferndale is a small town in Humboldt County, California.        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
 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
+B-6/61/A-220 Z light pillbox   B-6/61/A-220Z light pillbox, Beroun     49 57 25.96 N   14 05 35.6 E
+B-7/5/A-200 Z light pillbox            49 56 28.3 N    14 07 53.68 E
+B-7/6/A-200 Z light pillbox            49 56 19.34 N   14 07 56.34 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
+Baker Glacier  Chugach National Forest, Alaska 61 04 53 N      148 21 52 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.14 W
-Basilique Notre-Dame de la Daurade             43 36 03 N      1 26 23 E
+Basilique Notre-Dame de la Daurade             36 02.88 N      1 26 22.92 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
+Basilique Saint-Sauveur de Rennes              48 06 42 N      1 40 54 W
 Beade  Beade is a municipality in Galicia, in the province of Ourense. 42 20 06.72 N   8 08 45.05 W
+Bear Lake (Alaska)     Kenai Peninsula, Alaska 60 12 03 N      149 21 11 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.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
+Bibracte       Bibracte est le nom de la cité gauloise, capitale des Eduens, qui était située au sommet du mont Beuvray (France)    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
-Bishkek                42 52 00 N      74 34 00 E
+Billings Glacier       Chugach National Forest, Alaska 60 52 55 N      148 34 28 W
+Bishkek                42 52 00.12 N   74 34 00.12 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
 Boke   Boke is a small village in the German district Paderborn; it belongs to the city of Delbrück.  51 43 48 N      8 33 43.56 E
 Bolongaropalast        The Bolongaro Palace is a baroque building in Frankfurt-Höchst.        50 06 04 N      8 33 08 E
 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
+Borovsk        Borovsk, a town established in 1358, stands between Moscow and Kaluga.  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
+Brancion       Brancion is a small fortified medieval city in Burgundy, Central France.        46 32 51 N      4 47 48.12 E
+Broad Pass     George Parks Highway, Alaska    63 19 22 N      149 10 10 W
+Brudermühlbrücke     The Brudermühlbrücke is a bridge in Munich across the Isar.   48 06 45.36 N   11 33 36 E
+Brudermühlstraße     The Brudermühlstraße is a street in Munich, part of the Mittlerer Ring around the city centre.        48 06 43.92 N   11 32 58.56 E
 Buenos Aires           34 36 13 S      58 22 54 W
 Bueu   Bueu is a municipality in Galicia, Spain in the province of Pontevedra. 42 19 24.69 N   8 47 23.1 W
-Buffalo Central Terminal       The New York Central Terminal in Buffalo, USA, was a key railroad station from 1929 to 1979.    42 53 22.44 N   78 49 51.08 W
+Bürgerhospital (Stuttgart)            48 47 34.6 N    9 10 45.95 E
 Burg Stahleck  Stahleck Castle in Bacharach is a 12th century castle occupying a commanding view of the Rhine in the Loreley valley. Nowadays it's a youth hostel.     50 03 29.49 N   7 45 56.46 E
 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  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
+Cabo Fisterra          42 52 57 N      9 16 19.92 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
+Cáceres       Cáceres is the capital of Cáceres Province, in Extremadura, Spain.    39 28 23 N      6 22 16 W
+Cadaqués              42 17 18.96 N   3 16 40.08 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
@@ -117,7 +120,8 @@ Candelabro de Paracas       The Paracas Candelabra is a well-known prehistoric geoglyp
 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
+Capela de São Nicolau         32 41 08 27.15 N        8 36 55.57 W
+Cap Glacier    Chugach National Forest, Alaska 60 56 57 N      147 54 57 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
 Carballeda de Avia     Carballeda de Avia is a municipality in Galicia, in the province of Ourense.    42 19 12.81 N   8 09 53.03 W
 Cariño        Cariño is a municipality in the province A Coruña, Galicia, Spain.    43 43 58 N      7 52 39.76 W
@@ -126,8 +130,7 @@ 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à     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
+Casa Milà             41 23 43.08 N   2 09 42.12 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
@@ -138,7 +141,7 @@ 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
 Castelo de Maceda              42 16 13.92 N   7 39 32.36 W
 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 Monterreal          42 07 32.16 N   8 51 00 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  Castillo de San Felipe is a castle in the county of Ferrol, Galicia     43 27 53 N      8 16 54 W
@@ -146,27 +149,28 @@ 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.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 Vilalba             43 17 52.8 N    7 40 56.42 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.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 Baroña              42 41 41.8 N    9 01 55.99 W
 Castro de Rei          43 12 32.14 N   7 23 58.38 W
 Catacaos               5 16 00 S       80 41 00 W
+Cataract Glacier       Chugach National Forest, Alaska 61 01 39 N      148 25 23 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 Chartres             48 26 52 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
+Cathédrale Saint-Julien du Mans       Interior        48 00 33 N      0 11 56 E
+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
 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
@@ -176,56 +180,45 @@ 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.71 W
-Chapelle de Languidou  Chapel in Plovan / Bretagne     47 54 49.13 N   4 21 09.72 W
+Chapelle de Languidou          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 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 royale Saint-Louis, Dreux             48 44 18 N      1 21 48 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
 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
-Church of Adelboden    The gothic village church of Adelboden was built in the 15th century    46 29 34.1 N    7 33 32.4 E
-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'Angers              47 28 11.67 N   0 33 33.61 W
 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 d'Azay-le-Rideau      The Château de Azay-le-Rideau is one of the most famous castles in the French Loire Valley.    47 15 33 N      0 27 58 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 Châteaudun                48 04 14 N      1 19 25 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 Condé             49 00 20 N      3 33 34 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
 Château de Domfront           48 35 39 N      0 39 09 W
+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 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 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 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
@@ -233,34 +226,49 @@ 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 des Adhémar          44 33 33.02 N   4 45 15.35 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 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
 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 Varillettes        The Château de Varillettes is a château in the Cantal.        45 01 26.76 N   3 08 53.16 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 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
+Château d'If          43 16 47.5 N    5 19 30.5 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
+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-Gaillard      Château-Gaillard is a castle in Les Andelys (Normandie, France).       49 14 17.26 N   1 24 09.91 E
+Chena Hot Springs, Alaska              65 03 10 N      146 03 19 W
+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
+Church of Adelboden    The gothic village church of Adelboden was built in the 15th century    46 29 34.1 N    7 33 32.4 E
+Church of Saints Apostles Peter and Paul in Vilnius            54 41 38.82 N   25 18 22.69 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
+Church of St. Johns in Vilnius         54 40 57.5 N    25 17 19.22 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
+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 42.84 N   2 23 39.12 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
+City Palace (Udaipur)  The City Palace in Udaipur was the royal palace of the Maharana of Mewar. The palace is located on the east bank of Lake Pichola in Udaipur, Rajasthan, India.  24 34 37.2 N    73 41 00.96 E
+Ciudad Real            38 58 59.88 N   3 55 00.12 W
+Clos Vougeot   Clos Vougeot is a famous vineyard in Burgundy, France.  47 10 29.72 N   4 57 19.74 E
+Cochrane Bay   Chugach National Forest, Alaska 60 44 18 N      148 20 06 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
+Collégiale Saint-Thiébaut de Thann           47 48 40 N      7 06 06 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
 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
 Conciergerie   The Conciergerie in the Palais de Justice, Paris, France        48 51 23 N      2 20 44 E
+Córdoba (Argentina)   Intendancy square       31 24 00 S      64 11 00 W
 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
@@ -269,41 +277,165 @@ Couvent de la Divine Providence de Saint-Jean-de-Bassel          48 48 12.12 N   6 59 32.0
 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
+Cygnus olor from Carolasee     The swans and pond are there since 1882.        51 01 59.16 N   13 45 52.69 E
 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
+Delaney Park, Anchorage, Alaska                61 12 47 N      149 54 04 W
 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       München        48 07 57.85 N   11 32 40.69 E
+Deutsches Museum Verkehrszentrum       Ganghoferstraße 29, 80339 München     48 07 57.85 N   11 32 40.69 E
 Deyrulzaferan          37 17 57.6 N    40 47 33.9 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. 21 06 29.99 N   105 57 06.31 E
+Doran Strait   Chugach National Forest, Alaska 61 04 34 N      148 11 20 W
+Dorfkirche Dabergotz           52 54 10.44 N   12 43 30.42 E
+Dorfkirche Golzow (Barnim)             52 54 41.37 N   13 48 32.69 E
+Dorfkirche Hohenfinow          52 48 38.1 N    13 55 29.08 E
+Dorfkirche Kirchlotheim                51 10 07.83 N   8 53 46.05 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
+Dorfkirche Zechow              53 03 18.01 N   12 54 44.88 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         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
-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
+É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
+École Jules Ferry de Royan            45 37 38.16 N   1 01 33.66 W
+Effnerplatz    The Effnerplatz is a square in the north of Munich in the Borough Bogenhausen.  48 09 09 N      11 36 51.12 E
+Église de la Sainte-Trinité (Lauterbourg)            48 58 30.1 N    8 10 40.77 E
+Église de l'Assomption simultanée (La Petite-Pierre)         48 51 25.2 N    7 18 55.84 E
+Église de Saint-Lothain               46 49 27.84 N   5 38 30.12 E
+Église de Saint-Paul de Frontignan    L'Église de Saint-Paul de Frontignan est une église catholique de l'ancien diocèse de Maguelone en Languedoc, France.        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 de la Dalbade               43 35 51.36 N   1 26 32.89 E
+Église Notre-Dame-de-la-Nativité (Saverne)           48 44 28.32 N   7 21 50.36 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 (Guebwiller)                47 54 20.75 N   7 12 52.56 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 du Temple Neuf (Strasbourg)                48 35 00 N      7 44 54 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 (Sélestat)                48 15 36.36 N   7 27 11.56 E
+Église protestante (Weiterswiller)            48 51 10.44 N   7 24 50.9 E
+Église Sainte-Marie-Madeleine de Rennes-le-Château           42 55 41.05 N   2 15 45.69 E
+Église Saint-Eustache de Paris                48 51 48 N      2 20 42 E
+Église Saint-Gervais-Saint-Protais            48 51 19.8 N    2 21 16.6 E
+Église Saint-Laurent (Paris)          48 52 29.45 N   2 21 29.92 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-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
+Eglise Saint-Pierre des Cuisines       L'Église Saint-Pierre des Cuisines, située rue de la Boule, à côté de la place Saint-Pierre à Toulouse, est la plus vieille église du sud-ouest de la France. Elle est construite sur une ancienne nécropole gallo-romaine du IVe siècle.      43 36 15.49 N   1 26 08.26 E
+Église Saint-Pierre-Saint-Paul de Rueil-Malmaison             48 52 35.4 N    2 10 53.15 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 Saint-Sulpice          48 51 04 N      2 20 05 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
-El Padul               37 01 27 N      3 37 36 W
+Églises St Pierre le Vieux (Strasbourg)       protestant church       48 34 58 N      7 44 24 E
+Église St Antoine de Padoue (Saverne)         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 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 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 Walburge (Walbourg)                48 53 05.51 N   7 47 21.97 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-Baptiste (Saint-Jean-Saverne)          48 46 18.7 N    7 21 48.5 E
+Église St Jean-Baptiste simultanée (Hohwiller)               48 45 12.78 N   7 27 56.77 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 protestante (Wissembourg)              49 02 18.96 N   7 56 33.36 E
+Église St Jean (Strasbourg)           48 35 04 N      7 44 25 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 Médard (Bœrsch)           48 28 40.44 N   7 26 24.4 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 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
+Eklutna Village Cemetery       Anchorage, Alaska       61 27 38 N      149 21 42 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
+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
+Esther Passage Chugach National Forest, Alaska 60 53 20 N      147 56 27 W
 European Parliament    The European Parliament is the parliament of the European Union.        48 35 51.82 N   7 46 09.82 E
+Explorer Glacier       Chugach National Forest, Alaska 60 46 34 N      148 55 03 W
 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
@@ -312,115 +444,129 @@ 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.04 N   11 17 39.19 E
+Filialkirche hl. Gotthart in Lansach, Weißenstein             46 41 12.84 N   13 42 16.56 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
+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
+Forge d'Étueffont             47 43 20.9 N    6 55 15.19 E
+Fortaleza da Nogueirosa                43 23 29.12 N   8 08 09.12 W
+Fortaleza de San Paio de Narla         43 00 25.83 N   7 49 14 W
 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 de Vézelois              47 36 01.67 N   6 54 29.41 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
-Fortaleza da Nogueirosa                43 23 29.12 N   8 08 09.12 W
-Fortaleza de San Paio de Narla         43 00 25.83 N   7 49 14 W
 Forte di Gavi  Il Forte di Gavi è una fortezza storica costruita su un preesistente castello di origine medioevale.   44 41 27.95 N   8 48 15.55 E
 Fortezza del Priamar   La fortezza del Priamar è un antico insediamento storico presente nella città ligure di Savona, Italia.       44 18 16.29 N   8 29 03.44 E
-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
+Fort Ross      Fort Ross, a former Russian fur trade outpost, located on the coast of Northern California.     38 30 51.35 N   123 14 36.88 W
 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
+Fox Island (Alaska)    Kenai Peninsula, Alaska 59 54 46 N      149 20 52 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
+Fuzhou         26 04 16 N      119 18 13 E
+Galata Bridge  Galata Bridge crosses the Golden Horn in Istanbul, Turkey       41 01 13.1 N    28 58 24.4 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
+Gardens of Nymphenburg Palace  Der Nymphenburger Schlosspark ist eines der größten und bedeutendsten Gartenkunstwerke Deutschlands.  48 09 28 N      11 29 34 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-Est              48 52 36.84 N   2 21 33.12 E
+Gare de Paris-Nord             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
+Garmischer Straße     The Garmischer Straße is a street in Munich, part of the Mittlerer Ring around the city centre.        48 07 28.2 N    11 31 14.52 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
-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.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
 Glanum         43 46 26 N      4 49 57 E
+Godwin Glacier Chugach National Forest, Alaska 60 07 45 N      149 12 52 W
 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
+Göltzschtalbrücke            50 37 21.29 N   12 14 37.46 E
+Goose Lake Park, Anchorage, Alaska             61 11 49 N      149 49 13 W
 Gorle          45 42 14 N      9 43 08 E
+Görzig (Rietz-Neuendorf)      Train station   52 14 20.25 N   14 11 48.91 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.25 E
+Gråmanstorps kyrka            56 08 59.33 N   13 06 00.4 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
+Gromo  Gromo is a town in Lombardia in Italy.  45 57 51.84 N   9 55 39 E
+Grouse Lake    Kenai Peninsula, Alaska 60 12 05 N      149 22 29 W
+Gulkana Glacier        Eastern Alaska Range, Alaska    63 14 26 N      145 28 03 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
+Haage  Train station   52 40 37.61 N   12 35 48.46 E
 Hamburger Rathaus              53 33 01 N      9 59 32 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
+Hamburg-Moorburg Sprengung Kraftwerk   Bursting of the 256 meters high chimney of the deactivated HEW power plant in Hamburg-Moorburg, Germany. The chimney was Hamburg's highest massiv building. The HEW power plant Hamburg-Moorburg was in operation from 1974 untill 2001. It was one of the biggest conventional power plant in Germany and was fired on both with natural gas and fuel oil.     53 29 24 N      9 57 06 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
+Hammer (Liebenwalde)   Former town hall        52 52 54.39 N   13 26 32.69 E
+Hansestaden Visby      The Hanseatic town Visby is the description of Visby, Sweden, from the UNESCO World Heritage Committee. 57 38 20 N      18 17 40 E
+Hans Paasche   Hans Paasche (3 April 1881 – 21 May 1920) was a German politician and pacifist.       52 59 48.32 N   15 58 47.1 E
+Harriman Fjord Chugach National Forest, Alaska 61 02 20 N      148 19 17 W
 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
+Heckenstallerstraße   The Heckenstallerstraße is a street in Munich, part of the Mittlerer Ring around the city centre.      48 06 35.64 N   11 31 41.88 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
 HMS Otus       The HMS Otus is a british Oberon class submarine. It serves today as a museum Sassnitz harbour. 54 30 43.13 N   13 38 29.79 E
-Holy Trinity Cathedral in Odessa               46 28 34.36 N   30 44 18.43 E
+Holgate Arm    Kenai Fjords National Park, Alaska      59 49 56 N      149 47 56 W
+Holy Trinity Cathedral in Odessa       Holy Trinity Cathedral in Odessa        46 28 33.96 N   30 44 17.16 E
+Hope Highway, Alaska   Kenai Peninsula, Alaska 60 46 50 N      149 25 50 W
 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
-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 d'Ulmo          43 35 51 N      1 26 59.28 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
+Hübners Mühle in Werder (Havel)      The fire ruin of the windmill of miller and baker named Hübner in Werder (Havel)       52 22 28.38 N   12 55 15.28 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.08 N   15 10 10.92 E
+Igreja de São Martinho de Aldoar              41 10 14.59 N   8 40 13.84 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 Santa María de Cambre               43 17 31.92 N   8 20 34.08 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
 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
-Isfahan اصفهان           32 39 05 N      51 40 45 E
+Isabel Pass    Richardson Highway, Alaska      63 11 15 N      145 33 28 W
+Isarring       The Isarring is a street in Munich, part of the Mittlerer Ring around the city centre.  48 09 36.72 N   11 36 03.96 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
 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
+Jaroměř      Pond    50 21 21.6 N    15 55 15.6 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
+José María Acuña López     José María Acuña López, born in Pontevedra (Spain) on April 4, 1903 and died on 4 June 1991 in Vigo (Spain), was a spanish sculptor.        42 19 46.38 N   8 34 04.43 W
 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
+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
 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
+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
-Kathmandu              27 43 00 N      85 22 00 E
+Kathmandu              27 43 12 N      85 22 12 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
+Korsberga kyrka, Småland              57 18 25.2 N    15 07 25.6 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
@@ -429,32 +575,31 @@ 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 09 35 N       101 42 00 E
+Kuala Lumpur           3 08 52.08 N    101 41 43.08 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
+Lake Louise, Alaska    Borough di Matanuska-Susitna, Alaska    62 19 26 N      146 32 56 W
 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          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
+Large Hadron Collider          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
 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
+Learnard Glacier       Chugach National Forest, Alaska 60 48 44 N      148 42 55 W
+Leiro  Leiro is a municipality in Galicia, in the province of Ourense. 42 22 11.51 N   8 07 27.42 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
+Le Train Bleu          48 50 42 N      2 22 24 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
-Loschwitzer Friedhof   Cemetery "Loschwitzer Friedhof" in Dresden-Loschwitz    51 02 46 N      13 49 18.98 E
+Lima   Lima    12 03 00 S      77 02 00 W
+Loro Parque            28 24 29.88 N   16 33 52.74 W
+Loschwitzer Friedhof   Cemetery „Loschwitzer Friedhof“ in Dresden-Loschwitz        51 02 46 N      13 49 18.98 E
+Lowell Point, Alaska   Kenai Peninsula, Alaska 60 04 18 N      149 26 37 W
 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
@@ -468,7 +613,10 @@ 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
 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
+Margaret Eagan Sullivan Park, Anchorage, Alaska                61 12 31 N      149 55 16 W
+Marienkirche Witzwort          54 23 58.86 N   8 59 06.17 E
 Marín         42 23 31.28 N   8 42 16.58 W
+Mary's Tomb            31 46 48.5 N    35 14 21.41 E
 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
@@ -476,110 +624,112 @@ 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
 Melón Melón is a municipality in Galicia, in the province of Ourense.        42 15 26.97 N   8 13 01.51 W
+Mérida (Spain)                38 54 56.88 N   6 19 59.88 W
 Mii-dera       Mii-dera 三井寺, formally Onjōji 園城寺, is a Tendai Buddhist temple in the city of Otsu, Shiga Prefecture, Japan.       35 00 48.09 N   135 51 10.26 E
 Millennium Town Park   The Millennium Town Park is a public park in Saint Helier, Jersey.      49 11 15.01 N   2 06 06.95 W
-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
+Minden Minden is a German city in North Rhine-Westphalia.      52 17 20.18 N   8 55 04.19 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          38 10 20 S      146 16 04 E
+Möja kyrka            59 24 18.8 N    18 52 53 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
 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.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
+Mońki Railway station 53 24 00 N      22 47 00 E
+Monte Amiata   Il Monte Amiata è un monte situato nella Toscana.      42 53 15.9 N    11 37 27.73 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
-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
+Montevideo     Montevideo is the capital and largest city of Uruguay.  34 52 01 S      56 10 00 W
+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.08 N   2 19 49.08 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.19 W
+Mössingen             48 24 23 N      9 03 27 E
 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
+Mount Muir, Alaska     Chugach Mountains, Alaska       61 06 29 N      148 22 42 W
 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
+Mühlen am Löbauer Wasser             51 12 02.21 N   14 39 21.09 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
+Museo Archeologico Regionale Paolo Orsi        Archaeological Museum Paolo Orsi in Syracuse    37 04 34.36 N   15 17 10.89 E
 Museo Civico d'Arte Antica di Torino   Il Museo Civico d'Arte Antica è un polo museale situato a Torino presso Palazzo Madama.        45 04 15.95 N   7 41 07.72 E
 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
-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
+Naturschutzgebiet „Königsbrücker Heide“          51 20 07.58 N   13 52 06.67 E
 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               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
 Noorderplantsoen       The Noorderplantsoen is a park in the Dutch city of Groningen.  53 13 25 N      6 33 20 E
+Nordwestbahnhof, Vienna        Emergency quarters from Northwest Railway Wagon 13 46.81 N      16 22 58.91 E
+Nulbay Park, Anchorage, Alaska         61 12 57 N      149 54 36 W
 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.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.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
+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.47 W
 Ocean Park, Hong Kong          22 14 45.1 N    114 10 33.3 E
+O Ézaro, Dumbría             42 54 38.2 N    9 07 54.12 W
+O Grove                42 29 34.29 N   8 52 04.9 W
 Ohr Somayach Synagogue Ohr Somayach Synagogue, the main synagogue in Odessa, Ukraine   46 28 40.55 N   30 44 22.13 E
 Oia    Oia is a municipality in Galicia, Spain in the province of Pontevedra.  42 00 06.45 N   8 52 30.54 W
+Oímbra                41 53 07.94 N   7 28 19.78 W
 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
+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
 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
+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
 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.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.       41 55 26.26 N   42 00 33.84 E
-Oímbra                41 53 07.94 N   7 28 19.78 W
 Palace and park of Versailles          48 48 15.85 N   2 07 23.38 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
 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 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
+Palais ducal de Nevers The Palais Ducal in Nevers, France      46 59 18 N      3 09 30 E
 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 Astérix          49 08 07.08 N   2 34 18.12 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 Monceau   Parc Monceau is a public park situated in the 8th and 17th Arrondissements of Paris at the junction of Boulevard de Courcelles, Rue de Prony and Rue Georges Berger.    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
+Passage Canal, Alaska  Chugach National Forest, Alaska 60 48 53 N      148 31 28 W
+Penniman Glaciers      Chugach National Forest, Alaska 61 05 42 N      148 20 24 W
 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
+Pfaffendorf (Rietz-Neuendorf)          52 15 57.79 N   14 09 46.25 E
+Pfarrkirche St. Bartholomäus in Friesach              46 57 04.28 N   14 24 18.19 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
@@ -588,158 +738,182 @@ 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
+Pigot Glacier  Chugach National Forest, Alaska 60 54 12 N      148 28 44 W
 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
+Piode  Piode è un comune della Valsesia, in provincia di Vercelli, Piemonte, Italia.  45 46 09.98 N   8 02 57.28 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
+Place de la Concorde   The Place de la Concorde is one of the major public squares in Paris, France.   48 51 56.16 N   2 19 15.96 E
 Place des Vosges       Place des Vosges in Paris, France       48 51 20 N      2 21 56 E
+Placer River Valley    Kenai Peninsula, Alaska 60 49 25 N      148 59 47 W
 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
 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
+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
 Pontevedra             42 25 50.84 N   8 38 56.27 W
+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       The Pont sur la Laye is an old bridge across the Laye near Mane, Alpes-de-Haute-Provence department, Provence, France.  43 55 48 N      5 45 23.04 E
 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.26 E
 Porto Azzurro          42 46 05.02 N   10 23 48.33 E
+Port of Kobe   Port of Kobe in Kobe, Hyōgo Prefecture, Japan  34 40 39.17 N   135 13 36.97 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
+Potter Marsh Anchorage         61 04 39 N      149 49 37 W
 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
+Puerto de Navacerrada  El puerto de Navacerrada es un puerto de montaña a 1858 msnm de altitud, que alberga una estación de esquí y que está situado en la sierra de Guadarrama (Sistema Central). 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.16 W
 Quartier de Beaugrenelle               48 51 03.87 N   2 17 07.61 E
+Radweg Arnsdorf-Guteborn               51 25 49.66 N   13 53 18.08 E
+Rainy Glacier  Chugach National Forest, Alaska 60 38 39 N      148 32 37 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
 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
+Resurrection Peninsula Kenai Peninsula, Alaska 59 56 53 N      149 16 41 W
 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
-Ribeira        Ribeira is a municipality in the province A Coruña, Galicia, Spain.    42 32 38.31 N   9 00 06.92 W
 Ribeira, Ribeira               42 33 16.32 N   8 59 27.37 W
+Ribeira        Ribeira is a municipality in the province A Coruña, Galicia, Spain.    42 32 38.31 N   9 00 06.92 W
 Ricetto di Candelo             45 32 46.68 N   8 06 48.96 E
 Rio Marina     Rio Marina is a village in Isola d'Elba Toscana in Italy        42 48 44.2 N    10 25 39.87 E
-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
+Río Tambre            42 54 20.32 N   8 41 43.21 W
+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
 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.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
+Roujan Roujan is a commune of the Hérault département in the Region of Languedoc-Roussillon - France.        43 30 32.5 N    3 17 15.15 E
+Rudyerd Bay    Borough di Ketchikan Gateway, Alaska    55 35 23 N      130 44 45 W
+Ruhlsdorf (Teltow)     Former town hall        52 22 28.71 N   13 16 04.56 E
+Russian Jack Springs Park, Anchorage, Alaska           61 12 21 N      149 47 18 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 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
+Safranbolu     Safranbolu is a city and district of Karabük Province, Turkey. 41 14 41 N      32 41 37 E
+Sagrada Família               41 24 13 N      2 10 27.05 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
+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       Saint-Nazaire-de-Ladarez is a commune of the Hérault département, France.     43 30 37 N      3 04 36 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 13.69 N   111 53 31.06 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
+Sandiás       Sandiás is a municipality in Galicia, in the province of Ourense.      42 06 38.53 N   7 45 28.41 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
+Sankt Martini          53 04 30 N      8 48 15 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
+Sant'Anastasia Sant'Anastasia è una chiesa di Roma.   41 53 17.6 N    12 29 03 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
 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
+Schenkenhorst (Stahnsdorf)     Former town hall        52 20 28.05 N   13 11 46.85 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
+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
 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
+Schloss Rosenstein             48 48 26 N      9 11 22 E
+Schönfließ (Mühlenbecker Land)              52 39 23.04 N   13 20 21.84 E
+Serpentine Glacier     Chugach National Forest, Alaska 61 07 41 N      148 16 02 W
+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
-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
+Singapore Zoo  Singapore Zoo / Mandai 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
+Site des missiles Plutons Bourogne-Meroux              47 34 54.94 N   6 54 17.69 E
+Sixmile Creek (Alaska) Kenai Peninsula, Alaska 60 54 18 N      149 25 36 W
 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.        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
+Spreenhagen    Village Pub     52 20 28.62 N   13 52 50.12 E
+Sputendorf     Former town hall        52 20 20.99 N   13 13 11.05 E
+Stadium of Epidaurus           37 35 51.77 N   23 04 27.8 E
+Städtisches Lapidarium Stuttgart              48 46 03.36 N   9 10 04.58 E
 Stadtplatz (Steyr)             48 02 20.42 N   14 25 08.89 E
-Stalis         35 17 47.4 N    25 25 25.9 E
+Staffelde              52 43 47.5 N    12 59 23.57 E
+Stalis Stalis is a small resort village on the island of Crete thirty kilometers from the capital Heraklion, located on the north side of the island.  35 17 47.4 N    25 25 25.9 E
+State Bank of Indiana, Terre Haute branch              39 27 55 N      87 24 52.5 W
 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
+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
 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.     41 00 19.3 N    28 58 36.6 E
-Synchrotron Soleil             48 42 33 N      2 08 41 E
+St. Peter und Paul (Grettstadt)                49 59 06.21 N   10 18 44.87 E
 Süleymaniye camii     The Mosque of Suleiman I in Istanbul.   41 00 58.3 N    28 57 50 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.39 N   28 58 36.57 E
+Synchrotron Soleil             48 42 33 N      2 08 41 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 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
+The Dormition Cathedral in Odessa              46 28 30.98 N   30 43 54.89 E
+The Spice Bazaar, Istanbul             41 01 00.7 N    28 58 15.13 E
+Thunderbird Falls      Chugach Mountains, Alaska       61 26 31 N      149 21 28 W
 Tiberias               32 47 20.04 N   35 31 20.28 E
 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 San Sadurniño                42 30 36 N      8 49 12 W
 Torre de Vilanova dos Infantes         42 09 58.36 N   7 57 16.62 W
+Torreón dos Andrade           43 24 27.5 N    8 10 19.25 W
 Torres de Altamira             42 52 39.02 N   8 41 15.17 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
-Toshkent               41 18 00 N      69 16 00 E
+Tour de la Chaîne     The tour de la Chaîne (XIVth century) is, with the tour Saint-Nicolas and the tour de la Lanterne, one of the three towers located on the seafront of La Rochelle, and one of the two towers that is representative of the city old harbour.   46 09 21.24 N   1 09 15.52 W
+Tour de la Lanterne    The tour de la Lanterne (XVth century) is, with the tour Saint-Nicolas and the tour de la Chaîne, one of the three towers located on the seafront of La Rochelle. It was added to the historical monument list of France in 1879.      46 09 20.88 N   1 09 25.42 W
 Tour Eiffel            48 51 30 N      2 17 39 E
 Tour Goguin            46 59 03.84 N   3 09 19.91 E
+Tour Saint-Jacques     Saint-Jacques Tower is located in the IVe arrondissement of Paris. This gothic tower is all that remains of the former church of Saint-Jacques-de-la-Boucherie. 48 51 27.95 N   2 20 55.6 E
+Tour Saint-Nicolas             46 09 20.88 N   1 09 11.99 W
+Trail Glacier, Alaska  Kenai Peninsula, Alaska 60 33 14 N      148 54 32 W
 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
+Trappentreustraße     The Trappentreustraße is a street in Munich, part of the Mittlerer Ring around the city centre.        48 08 12.48 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.88 N   11 32 03.12 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
+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
+Treskow-Platane        The Treskow-Platane is a old hybrid plane and natural monument in Berlin-Friedrichsfelde, so called in memorial of Johann Carl Sigismund von Treskow.   52 30 23.69 N   13 31 17.47 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
 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
 Université Lille Nord de France               50 36 33.38 N   3 08 29.72 E
+University of Alaska Museum of the North       Fairbanks, Alaska       64 51 30 N      147 50 32 W
 Upper Darby, Pennsylvania              39 57 40.2 N    75 15 32.06 W
 Urgnano                45 35 50 N      9 41 42 E
 Valdoviño     Valdoviño is a municipality in the province A Coruña, Galicia, Spain. 43 36 32.23 N   8 08 30.84 W
@@ -750,9 +924,9 @@ Varigotti   Varigotti è una frazione del comune di Finale Ligure, in provincia di
 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.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
+Via Dolorosa signs     third fall of Jesus (not in the Gospels).       31 46 46.48 N   35 13 56.63 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
 Vilanova de Arousa     Vilanova de Arousa is a municipality in Galicia, Spain in the province of Pontevedra.   42 33 48.42 N   8 49 26.69 W
@@ -764,203 +938,81 @@ Vilassar de Mar  Vilassar de Mar is a village in the county of the Maresme, Catal
 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
+Villa Les Glycines     Villa Les Glycines (1902-1904)  48 40 46 N      6 10 56 E
+Villa Majorelle                48 41 8 N       6 9 50 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
+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
 Villeneuve-d'Ascq      Villeneuve-d'Ascq is a commune within Lille Metropolis, in northern France      50 37 24 N      3 08 42 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
+Westchester Lagoon, Anchorage, Alaska          61 12 16 N      149 54 58 W
 West Lake              30 15 00 N      120 09 00 E
 Whitemarsh Hall                40 04 42 N      75 07 44 W
 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
 Xàbia Xàbia is a village in the province of Alicante.        38 47 21 N      0 09 47 E
+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             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
+Yao    Yao (八尾市, Yao-shi)  is a city in Osaka, Japan.    34 37 36.9 N    135 36 03 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
-Душанбе         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
+Архангельск         3 64 33 00 N    40 31 48 E
+Астана           1 51 07 48 N    71 25 48 E
+Астрахань             46 19 59.88 N   48 01 00.12 E
+Боровск         55 12 27.19 N   36 29 05.05 E
+Душанбе Dushanbe is the capital city of Tajikistan.     38 34 23.16 N   68 47 11.04 E
+Екатеринбург               56 49 59.88 N   60 34 59.88 E
+Москва           55 45 20.88 N   37 37 04.08 E
+Нижний Новгород          56 19 36.84 N   44 00 27 E
+Санкт-Петербург          59 57 00 N      30 19 12 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 28 00 E
-东平 Daqing River    35 54 30 N      116 18 00 E
+চট্টগ্রাম            22 19 12 N      91 49 12 E
+上海         1 31 10 12 N    121 28 12 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.  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 42 N      135 46 05 E
+京都市      Kyoto is a city in Japan. It was the capital of Japan from 794 to 1869. 35 00 42.12 N   135 46 05.16 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
 北京动物园        beijing zoo lies west of Xizhimen and is the western part of the Beijing, China. It is one of the largest zoos in mainland china.       39 56 19 N      116 20 00 E
 北海公园   中文: 北海公园位于中国北京市城区的中偏北部,故宫和景山的西北侧,始建于辽代,是世界上现存建园时间最早的皇家宫苑。 39 55 28 N      116 22 59 E
-南京 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
+南京 Nanjing (南京) is the capital of Jiangsu Province of China. It was the Chinese capital from 1927-1949.        32 03 00 N      118 46 00.12 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
+台南市              22 58 59.88 N   120 10 59.88 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
 哈尔滨      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
+四川 Nature reserve in Aba(阿坝) Tibetan and Qiang Autonomous District, northern Sichuan province, China.  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
 大津市      Ō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
+大阪市              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
 天安门广场        Mausoleum of Mao Zedong 39 54 12 N      116 23 30 E
 宇治市      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
 广东 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.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
+广州         23 07 43.66 N   113 15 32.31 E
+広島市      Hiroshima is one of largest cities in Japan and the capital of Hiroshima prefecture. During World War II, it was the first city in the world to have an atomic bomb dropped on it.      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 city and prefecture-level division of Sichuan Province, in southwestern 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 48.96 N   104 04 00.12 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 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
-深圳 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
+杭州         30 15 00 N      120 10 03 E
+武汉 Wuhan is the capital of the Chinese province of Hubei   30 35 13.92 N   114 17 17.16 E
+滕王阁              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
+福建 Lu You Statue in Ningde中文(简体)‎: 宁德的陆游雕像     25 54 00 N      118 18 00 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
+薩摩川内市        Satsumasendai is a city in Kagoshima prefecture, Japan. 31 48 48.5 N    130 18 14.3 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. Today it is the capital of Shaanxi province.       34 16 00.12 N   108 54 00 E
 逗子市      Zushi (逗子市 Zushi-shi) is a city located in Kanagawa, Japan.       35 17 44.2 N    139 34 49.2 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
+重庆 Chongqing is located in the southwest of China, is China's largest and most populous municipality.      29 33 00 N      106 30 24.84 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
+香港         22 16 48 N      114 09 36 E
+高雄市      Kaohsiung is a city in Taiwan.  22 37 00.01 N   120 18 00 E
+鹿児島市   Kagoshima(鹿児島市; -shi) is a city in Japan and the capital city of Kagoshima Prefecture.      31 35 48.12 N   130 33 25.92 E
index d4490512d8599bd80688081208bc73c8f0cc0796..ca88174011ae7f028c57b71eee4f7cb1a1b134db 100644 (file)
@@ -1,37 +1,24 @@
 package tim.prune.function.settings;
 
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.FlowLayout;
-import java.awt.GridLayout;
 import java.awt.Rectangle;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Properties;
 
-import javax.swing.BorderFactory;
-import javax.swing.JButton;
-import javax.swing.JDialog;
 import javax.swing.JFileChooser;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
 
 import tim.prune.App;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.config.Config;
+import tim.prune.data.DoubleRange;
+import tim.prune.data.Track;
 
 /**
  * Class to provide the function to save the config settings
  */
 public class SaveConfig extends GenericFunction
 {
-       private JDialog _dialog = null;
-
        /**
         * Constructor
         * @param inApp application object for callback
@@ -50,86 +37,6 @@ public class SaveConfig extends GenericFunction
         * Begin the function
         */
        public void begin()
-       {
-               // Make new dialog window (don't reuse it)
-               _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
-               _dialog.setLocationRelativeTo(_parentFrame);
-               _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
-               _dialog.getContentPane().add(makeDialogComponents());
-               _dialog.pack();
-               _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());
-               dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
-               JLabel descLabel = new JLabel(I18nManager.getText("dialog.saveconfig.desc"));
-               dialogPanel.add(descLabel, BorderLayout.NORTH);
-
-               // Grid panel in centre
-               JPanel mainPanel = new JPanel();
-               mainPanel.setLayout(new GridLayout(0, 2, 15, 2));
-               mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15));
-               Properties conf = Config.getAllConfig();
-               Enumeration<Object> keys = conf.keys();
-               while (keys.hasMoreElements())
-               {
-                       String key = keys.nextElement().toString();
-                       String keyLabel = I18nManager.getText("dialog.saveconfig." + key);
-                       if (!keyLabel.equals("dialog.saveconfig." + key))
-                       {
-                               mainPanel.add(new JLabel(keyLabel));
-                               String val = conf.getProperty(key);
-                               String tipText = val;
-                               if (Config.isKeyBoolean(key)) {
-                                       val = Config.getConfigBoolean(key)?I18nManager.getText("dialog.about.yes"):I18nManager.getText("dialog.about.no");
-                               }
-                               else if (val != null && val.length() > 30) {
-                                       val = val.substring(0, 30) + " ...";
-                               }
-                               JLabel label = new JLabel(val);
-                               label.setToolTipText(tipText);
-                               mainPanel.add(label);
-                       }
-               }
-               dialogPanel.add(mainPanel, BorderLayout.CENTER);
-
-               // button panel at bottom
-               JPanel buttonPanel = new JPanel();
-               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
-               JButton okButton = new JButton(I18nManager.getText("button.ok"));
-               okButton.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               finish();
-                       }
-               });
-               buttonPanel.add(okButton);
-               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
-               cancelButton.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _dialog.dispose();
-                               _dialog = null;
-                       }
-               });
-               buttonPanel.add(cancelButton);
-               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
-               return dialogPanel;
-       }
-
-
-       /**
-        * Finish the dialog when OK pressed
-        */
-       private void finish()
        {
                File configFile = Config.getConfigFile();
                if (configFile == null) {configFile = Config.HOME_CONFIG_FILE;}
@@ -141,8 +48,6 @@ public class SaveConfig extends GenericFunction
                        File saveFile = chooser.getSelectedFile();
                        saveConfig(saveFile);
                }
-               _dialog.dispose();
-               _dialog = null;
        }
 
        /**
@@ -153,6 +58,24 @@ public class SaveConfig extends GenericFunction
                saveConfig(Config.getConfigFile());
        }
 
+       /**
+        * Autosave has been turned on or off, so maybe need to save
+        * @param inSaveOn true if autosave was switched on
+        */
+       public void autosaveSwitched(boolean inSaveOn)
+       {
+               File configFile = Config.getConfigFile();
+               if (inSaveOn && configFile == null)
+               {
+                       begin();
+               }
+               else if (!inSaveOn && configFile != null)
+               {
+                       // TODO: Ask whether to save or not?
+                       silentSave();
+               }
+       }
+
        /**
         * Actually save the config file
         * @param inSaveFile file to save to
@@ -165,7 +88,12 @@ public class SaveConfig extends GenericFunction
                        + currBounds.width + "x" + currBounds.height;
                Config.setConfigString(Config.KEY_WINDOW_BOUNDS, windowBounds);
 
-               // TODO: Check for null inSaveFile, then just call finish() ?
+               final String latlonString = createLatLonStringForConfig();
+               if (latlonString != null)
+               {
+                       Config.setConfigString(Config.KEY_LATLON_RANGE, latlonString);
+               }
+
                FileOutputStream outStream = null;
                try
                {
@@ -180,5 +108,33 @@ public class SaveConfig extends GenericFunction
                finally {
                        try {outStream.close();} catch (Exception e) {}
                }
+               // Remember where it was saved to
+               Config.setConfigFile(inSaveFile);
+       }
+
+       /**
+        * @return semicolon-separated string containing the four values, or null
+        */
+       private String createLatLonStringForConfig()
+       {
+               Track track = _app.getTrackInfo().getTrack();
+               if (track.getNumPoints() >= 2)
+               {
+                       final DoubleRange latRange = track.getLatRange();
+                       final DoubleRange lonRange = track.getLonRange();
+                       if (latRange.getRange() > 0.0 && lonRange.getRange() > 0.0)
+                       {
+                               StringBuffer buffer = new StringBuffer();
+                               buffer.append(Double.toString(latRange.getMinimum()));
+                               buffer.append(';');
+                               buffer.append(Double.toString(latRange.getMaximum()));
+                               buffer.append(';');
+                               buffer.append(Double.toString(lonRange.getMinimum()));
+                               buffer.append(';');
+                               buffer.append(Double.toString(lonRange.getMaximum()));
+                               return buffer.toString();
+                       }
+               }
+               return null;
        }
 }
index 63286c5e86dcddce24736afe427a4bfc410bcb97..0a9efd985cd7734b54a8599d0f28ddb57d4e3e57 100644 (file)
@@ -93,8 +93,12 @@ public class SetDisplaySettings extends GenericFunction
        private JCheckBox _antialiasCheckbox = null;
        private JComboBox<Integer> _wpIconCombobox = null;
        private JRadioButton[] _sizeRadioButtons = null;
+       private JRadioButton[] _windowStyleRadios = null;
        private JButton _okButton = null;
 
+       private static final String STYLEKEY_NIMBUS = "javax.swing.plaf.nimbus.NimbusLookAndFeel";
+       private static final String STYLEKEY_GTK = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
+
 
        /**
         * Constructor
@@ -176,6 +180,28 @@ public class SetDisplaySettings extends GenericFunction
                waypointsPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
                midPanel.add(waypointsPanel);
 
+               // Panel for window style
+               JPanel windowStylePanel = new JPanel();
+               windowStylePanel.setBorder(BorderFactory.createCompoundBorder(
+                       BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
+               );
+               windowStylePanel.setLayout(new FlowLayout(FlowLayout.CENTER));
+               windowStylePanel.add(new JLabel(I18nManager.getText("dialog.displaysettings.windowstyle")));
+               windowStylePanel.add(Box.createHorizontalStrut(10));
+               ButtonGroup styleGroup = new ButtonGroup();
+               final String[] styleKeys = {"default", "nimbus", "gtk"};
+               _windowStyleRadios = new JRadioButton[3];
+               for (int i=0; i<3; i++)
+               {
+                       _windowStyleRadios[i] = new JRadioButton(
+                               I18nManager.getText("dialog.displaysettings.windowstyle." + styleKeys[i]));
+                       styleGroup.add(_windowStyleRadios[i]);
+                       if (i != 2 || platformHasPlaf(STYLEKEY_GTK))
+                       {
+                               windowStylePanel.add(_windowStyleRadios[i]);
+                       }
+               }
+               midPanel.add(windowStylePanel);
                mainPanel.add(midPanel, BorderLayout.CENTER);
 
                // button panel at bottom
@@ -201,6 +227,19 @@ public class SetDisplaySettings extends GenericFunction
                return mainPanel;
        }
 
+       /**
+        * @return true if the specified style name is available on this platform
+        */
+       private static boolean platformHasPlaf(String styleName)
+       {
+               for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels())
+               {
+                       if (info.getClassName().equals(styleName)) {
+                               return true;
+                       }
+               }
+               return false;
+       }
 
        /**
         * Show window
@@ -221,6 +260,7 @@ public class SetDisplaySettings extends GenericFunction
                _antialiasCheckbox.setSelected(Config.getConfigBoolean(Config.KEY_ANTIALIAS));
                _wpIconCombobox.setSelectedIndex(Config.getConfigInt(Config.KEY_WAYPOINT_ICONS));
                selectIconSizeRadio(Config.getConfigInt(Config.KEY_WAYPOINT_ICON_SIZE));
+               selectWindowStyleRadio(Config.getConfigString(Config.KEY_WINDOW_STYLE));
                _dialog.setVisible(true);
        }
 
@@ -240,6 +280,24 @@ public class SetDisplaySettings extends GenericFunction
                }
        }
 
+       /**
+        * Select the corresponding radio button according to the selected style
+        * @param inValue style string saved in Config
+        */
+       private void selectWindowStyleRadio(String inValue)
+       {
+               int selectedRadio = 0;
+               if (inValue != null && inValue.equals(STYLEKEY_NIMBUS))
+               {
+                       selectedRadio = 1;
+               }
+               else if (inValue != null && inValue.equals(STYLEKEY_GTK) && _windowStyleRadios[2] != null)
+               {
+                       selectedRadio = 2;
+               }
+               _windowStyleRadios[selectedRadio].setSelected(true);
+       }
+
        /**
         * @return numeric value of selected icon size according to radio buttons
         */
@@ -255,6 +313,20 @@ public class SetDisplaySettings extends GenericFunction
                return 1; // default is medium
        }
 
+       /**
+        * @return the style string according to the selected radio button
+        */
+       private String getSelectedStyleString()
+       {
+               if (_windowStyleRadios[1].isSelected()) {
+                       return STYLEKEY_NIMBUS;
+               }
+               if (_windowStyleRadios[2] != null && _windowStyleRadios[2].isSelected()) {
+                       return STYLEKEY_GTK;
+               }
+               return null;
+       }
+
        /**
         * Save settings and close
         */
@@ -267,6 +339,7 @@ public class SetDisplaySettings extends GenericFunction
                Config.setConfigBoolean(Config.KEY_ANTIALIAS, _antialiasCheckbox.isSelected());
                Config.setConfigInt(Config.KEY_WAYPOINT_ICONS, _wpIconCombobox.getSelectedIndex());
                Config.setConfigInt(Config.KEY_WAYPOINT_ICON_SIZE, getSelectedIconSize());
+               Config.setConfigString(Config.KEY_WINDOW_STYLE, getSelectedStyleString());
                // refresh display
                UpdateMessageBroker.informSubscribers(DataSubscriber.MAPSERVER_CHANGED);
                _dialog.dispose();
index 642e71553834f97064416163ebad2b7ac37a1905..b64f906523ec4c3a5f7964bd0c6a7495ccf797eb 100644 (file)
@@ -43,13 +43,12 @@ 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",
-               "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",
-               "\u0443\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430 \u043c\u043e\u0432\u0430 (ukrainian)"
+               "suomi", "svenska", "\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"
        };
        /** 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", "fi", "ru", "zh", "ja", "ko", "de_ch", "uk"
+               "nl", "pl", "pt", "ro", "fi", "sv", "ru", "zh", "ja", "ko", "de_ch"
        };
 
 
index a5d08711e61841fe21f8763b899870c2d7bd0ba0..e8faeb1e7cf407f95a725e144587a7bbf35cc995 100644 (file)
@@ -95,7 +95,7 @@ public class SetPathsFunction extends GenericFunction
                _installedLabels = new JLabel[NUM_KEYS];
                for (int i=0; i<NUM_KEYS; i++)
                {
-                       JLabel label = new JLabel(I18nManager.getText("dialog.saveconfig.prune." + LABEL_KEYS[i] + "path"));
+                       JLabel label = new JLabel(I18nManager.getText("dialog.paths.prune." + LABEL_KEYS[i] + "path"));
                        label.setHorizontalAlignment(SwingConstants.RIGHT);
                        mainPanel.add(label);
                        String configVal = Config.getConfigString(CONFIG_KEYS[i]);
@@ -167,4 +167,4 @@ public class SetPathsFunction extends GenericFunction
                }
                _dialog.dispose();
        }
-}
\ No newline at end of file
+}
index 6fec0754155f7cb667466234b19434229506633a..282dcb2267cbff2a2267dde65fe7458265b78932 100644 (file)
@@ -19,7 +19,7 @@ public class SplitSegmentsFunction extends DistanceTimeLimitFunction
         * Constructor
         */
        public SplitSegmentsFunction(App inApp) {
-               super(inApp);
+               super(inApp, false);
        }
 
        /**
diff --git a/src/tim/prune/function/srtm/DownloadSrtmFunction.java b/src/tim/prune/function/srtm/DownloadSrtmFunction.java
deleted file mode 100644 (file)
index 0350ce3..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-package tim.prune.function.srtm;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-
-import javax.swing.JOptionPane;
-
-import tim.prune.App;
-import tim.prune.GenericFunction;
-import tim.prune.GpsPrune;
-import tim.prune.I18nManager;
-import tim.prune.config.Config;
-import tim.prune.data.DoubleRange;
-import tim.prune.gui.ProgressDialog;
-
-/**
- * Class to provide a download function for the Space Shuttle's SRTM data files.
- * HGT files are downloaded into memory via HTTP and stored in the map cache.
- */
-public class DownloadSrtmFunction extends GenericFunction implements Runnable
-{
-       /** Progress dialog */
-       private ProgressDialog _progress = null;
-       /** Flag to check whether this function is currently running or not */
-       private boolean _running = false;
-
-
-       /**
-        * Constructor
-        * @param inApp  App object
-        */
-       public DownloadSrtmFunction(App inApp) {
-               super(inApp);
-       }
-
-       /** @return name key */
-       public String getNameKey() {
-               return "function.downloadsrtm";
-       }
-
-       /**
-        * Begin the download
-        */
-       public void begin()
-       {
-               _running = true;
-               if (_progress == null) {
-                       _progress = new ProgressDialog(_parentFrame, getNameKey());
-               }
-               _progress.show();
-               // start new thread for time-consuming part
-               new Thread(this).start();
-       }
-
-       /**
-        * Run method using separate thread
-        */
-       public void run()
-       {
-               // Compile list of tiles to get
-               ArrayList<SrtmTile> tileList = new ArrayList<SrtmTile>();
-
-               // First, loop to see which tiles are needed
-               DoubleRange lonRange = _app.getTrackInfo().getTrack().getLonRange();
-               DoubleRange latRange = _app.getTrackInfo().getTrack().getLatRange();
-               final int minLon = (int) Math.floor(lonRange.getMinimum());
-               final int maxLon = (int) Math.floor(lonRange.getMaximum());
-               final int minLat = (int) Math.floor(latRange.getMinimum());
-               final int maxLat = (int) Math.floor(latRange.getMaximum());
-
-               for (int lon=minLon; lon<= maxLon; lon++)
-               {
-                       for (int lat=minLat; lat <= maxLat; lat++)
-                       {
-                               SrtmTile tile = new SrtmTile(lat, lon);
-                               boolean alreadyGot = false;
-                               for (int t = 0; t < tileList.size(); t++)
-                               {
-                                       if (tileList.get(t).equals(tile)) {
-                                               alreadyGot = true;
-                                       }
-                               }
-                               if (!alreadyGot) {tileList.add(tile);}
-                       }
-               }
-
-               downloadTiles(tileList);
-               // Finished
-               _running = false;
-       }
-
-
-       /**
-        * Download the tiles of SRTM data
-        * @param inTileList list of tiles to get
-        */
-       private void downloadTiles(ArrayList<SrtmTile> inTileList)
-       {
-               // Update progress bar
-               if (_progress != null)
-               {
-                       _progress.setMaximum(inTileList.size());
-                       _progress.setValue(0);
-               }
-
-               String errorMessage = null;
-
-               // Check the cache is ok
-               final String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
-               if (diskCachePath != null)
-               {
-                       File srtmDir = new File(diskCachePath, "srtm");
-                       if (!srtmDir.exists() && !srtmDir.mkdir()) {
-                               // can't create the srtm directory
-                               errorMessage = I18nManager.getText("error.downloadsrtm.nocache");
-                       }
-               }
-               else {
-                       // no cache set up
-                       errorMessage = I18nManager.getText("error.downloadsrtm.nocache");
-               }
-
-               // Get urls for each tile
-               URL[] urls = TileFinder.getUrls(inTileList);
-               int numDownloaded = 0;
-               for (int t=0; t<inTileList.size() && !_progress.isCancelled(); t++)
-               {
-                       if (urls[t] != null)
-                       {
-                               // Define streams
-                               FileOutputStream outStream = null;
-                               InputStream inStream = null;
-                               try
-                               {
-                                       // Set progress
-                                       _progress.setValue(t);
-                                       // See if we've already got this tile or not
-                                       File outputFile = getFileToWrite(urls[t]);
-                                       if (outputFile != null)
-                                       {
-                                               // System.out.println("Download: Need to download: " + urls[t]);
-                                               outStream = new FileOutputStream(outputFile);
-                                               URLConnection conn = urls[t].openConnection();
-                                               conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
-                                               inStream = conn.getInputStream();
-                                               // Copy all the bytes to the file
-                                               int c;
-                                               while ((c = inStream.read()) != -1)
-                                               {
-                                                       outStream.write(c);
-                                               }
-
-                                               numDownloaded++;
-                                       }
-                                       // else System.out.println("Don't need to download: " + urls[t].getFile());
-                               }
-                               catch (IOException ioe) {errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
-                               }
-                               // Make sure streams are closed
-                               try {inStream.close();} catch (Exception e) {}
-                               try {outStream.close();} catch (Exception e) {}
-                       }
-               }
-
-               _progress.dispose();
-               if (_progress.isCancelled()) {
-                       return;
-               }
-
-               if (errorMessage != null) {
-                       _app.showErrorMessageNoLookup(getNameKey(), errorMessage);
-               }
-               else if (numDownloaded == 1)
-               {
-                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getTextWithNumber("confirm.downloadsrtm.1", numDownloaded),
-                               I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
-               }
-               else if (numDownloaded > 1)
-               {
-                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getTextWithNumber("confirm.downloadsrtm", numDownloaded),
-                               I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
-               }
-               else if (inTileList.size() > 0) {
-                       _app.showErrorMessage(getNameKey(), "confirm.downloadsrtm.none");
-               }
-       }
-
-       /**
-        * See whether the SRTM file is already available locally
-        * @param inUrl URL for online resource
-        * @return file object to write to, or null if already there
-        */
-       private static File getFileToWrite(URL inUrl)
-       {
-               String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
-               if (diskCachePath != null)
-               {
-                       File srtmDir = new File(diskCachePath, "srtm");
-                       if (srtmDir.exists() && srtmDir.isDirectory() && srtmDir.canRead())
-                       {
-                               File srtmFile = new File(srtmDir, new File(inUrl.getFile()).getName());
-                               if (!srtmFile.exists() || !srtmFile.canRead() || srtmFile.length() <= 400) {
-                                       return srtmFile;
-                               }
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * @return true if a thread is currently running
-        */
-       public boolean isRunning()
-       {
-               return _running;
-       }
-}
index 15997448e038e6f4bb6e9f4b9437c99491b18a6e..cccc7ede7dca98a6b9fda70d9d484fdbc7b2b876 100644 (file)
@@ -4,7 +4,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.URL;
-import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
@@ -40,6 +40,8 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
        private boolean _normalTrack = true;
        /** Flag set when any tiles had to be downloaded (rather than just loaded locally) */
        private boolean _hadToDownload = false;
+       /** Count the number of tiles downloaded and cached */
+       private int _numCached = 0;
        /** Flag to check whether this function is currently running or not */
        private boolean _running = false;
 
@@ -100,8 +102,6 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
         */
        public void run()
        {
-               // Compile list of tiles to get
-               ArrayList<SrtmTile> tileList = new ArrayList<SrtmTile>();
                boolean hasZeroAltitudePoints = false;
                boolean hasNonZeroAltitudePoints = false;
                // First, loop to see what kind of points we have
@@ -128,64 +128,63 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
                }
 
                // Now loop again to extract the required tiles
+               HashSet<SrtmTile> tileSet = new HashSet<SrtmTile>();
                for (int i = 0; i < _track.getNumPoints(); i++)
                {
                        // Consider points which don't have altitudes or have zero values
                        if (!_track.getPoint(i).hasAltitude()
                                || (overwriteZeros && _track.getPoint(i).getAltitude().getValue() == 0))
                        {
-                               SrtmTile tile = new SrtmTile(_track.getPoint(i));
-                               boolean alreadyGot = false;
-                               for (int t = 0; t < tileList.size(); t++)
-                               {
-                                       if (tileList.get(t).equals(tile)) {
-                                               alreadyGot = true;
-                                       }
-                               }
-                               if (!alreadyGot) {tileList.add(tile);}
+                               tileSet.add(new SrtmTile(_track.getPoint(i)));
                        }
                }
-               lookupValues(tileList, overwriteZeros);
+               lookupValues(tileSet, overwriteZeros);
                // Finished
                _running = false;
                // Show tip if lots of online lookups were necessary
                if (_hadToDownload) {
                        _app.showTip(TipManager.Tip_DownloadSrtm);
                }
+               else if (_numCached > 0) {
+                       showConfirmMessage(_numCached);
+               }
        }
 
 
        /**
         * Lookup the values from SRTM data
-        * @param inTileList list of tiles to get
+        * @param inTileSet set of tiles to get
         * @param inOverwriteZeros true to overwrite zero altitude values
         */
-       private void lookupValues(ArrayList<SrtmTile> inTileList, boolean inOverwriteZeros)
+       private void lookupValues(HashSet<SrtmTile> inTileSet, boolean inOverwriteZeros)
        {
                UndoLookupSrtm undo = new UndoLookupSrtm(_app.getTrackInfo());
                int numAltitudesFound = 0;
+               TileFinder tileFinder = new TileFinder();
+               String errorMessage = null;
+               final int numTiles = inTileSet.size();
+
                // Update progress bar
                if (_progress != null)
                {
-                       _progress.setMaximum(inTileList.size());
+                       _progress.setMaximum(numTiles);
                        _progress.setValue(0);
                }
-               String errorMessage = null;
-               // Get urls for each tile
-               URL[] urls = TileFinder.getUrls(inTileList);
-               for (int t=0; t<inTileList.size() && !_progress.isCancelled(); t++)
+               int currentTileIndex = 0;
+               _numCached = 0;
+               for (SrtmTile tile : inTileSet)
                {
-                       if (urls[t] != null)
+                       URL url = tileFinder.getUrl(tile);
+                       if (url != null)
                        {
-                               SrtmTile tile = inTileList.get(t);
                                try
                                {
                                        // Set progress
-                                       _progress.setValue(t);
+                                       _progress.setValue(currentTileIndex++);
                                        final int ARRLENGTH = 1201 * 1201;
                                        int[] heights = new int[ARRLENGTH];
                                        // Open zipinputstream on url and check size
-                                       ZipInputStream inStream = getStreamToHgtFile(urls[t]);
+                                       ZipInputStream inStream = getStreamToSrtmData(url);
                                        boolean entryOk = false;
                                        if (inStream != null)
                                        {
@@ -209,54 +208,11 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
 
                                        if (entryOk)
                                        {
-                                               // Loop over all points in track, try to apply altitude from array
-                                               for (int p = 0; p < _track.getNumPoints(); p++)
-                                               {
-                                                       DataPoint point = _track.getPoint(p);
-                                                       if (!point.hasAltitude()
-                                                               || (inOverwriteZeros && point.getAltitude().getValue() == 0))
-                                                       {
-                                                               if (new SrtmTile(point).equals(tile))
-                                                               {
-                                                                       double x = (point.getLongitude().getDouble() - tile.getLongitude()) * 1200;
-                                                                       double y = 1201 - (point.getLatitude().getDouble() - tile.getLatitude()) * 1200;
-                                                                       int idx1 = ((int)y)*1201 + (int)x;
-                                                                       try
-                                                                       {
-                                                                               int[] fouralts = {heights[idx1], heights[idx1+1], heights[idx1-1201], heights[idx1-1200]};
-                                                                               int numVoids = (fouralts[0]==VOID_VAL?1:0) + (fouralts[1]==VOID_VAL?1:0)
-                                                                                       + (fouralts[2]==VOID_VAL?1:0) + (fouralts[3]==VOID_VAL?1:0);
-                                                                               // if (numVoids > 0) System.out.println(numVoids + " voids found");
-                                                                               double altitude = 0.0;
-                                                                               switch (numVoids)
-                                                                               {
-                                                                                       case 0: altitude = bilinearInterpolate(fouralts, x, y); break;
-                                                                                       case 1: altitude = bilinearInterpolate(fixVoid(fouralts), x, y); break;
-                                                                                       case 2:
-                                                                                       case 3: altitude = averageNonVoid(fouralts); break;
-                                                                                       default: altitude = VOID_VAL;
-                                                                               }
-                                                                               // Special case for terrain tracks, don't interpolate voids yet
-                                                                               if (!_normalTrack && numVoids > 0) {
-                                                                                       altitude = VOID_VAL;
-                                                                               }
-                                                                               if (altitude != VOID_VAL)
-                                                                               {
-                                                                                       point.setFieldValue(Field.ALTITUDE, ""+altitude, false);
-                                                                                       // depending on settings, this value may have been added as feet, we need to force metres
-                                                                                       point.getAltitude().reset(new Altitude((int)altitude, UnitSetLibrary.UNITS_METRES));
-                                                                                       numAltitudesFound++;
-                                                                               }
-                                                                       }
-                                                                       catch (ArrayIndexOutOfBoundsException obe) {
-                                                                               // System.err.println("lat=" + point.getLatitude().getDouble() + ", x=" + x + ", y=" + y + ", idx=" + idx1);
-                                                                       }
-                                                               }
-                                                       }
-                                               }
+                                               numAltitudesFound += applySrtmTileToWholeTrack(tile, heights, inOverwriteZeros);
                                        }
                                }
-                               catch (IOException ioe) {errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
+                               catch (IOException ioe) {
+                                       errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
                                }
                        }
                }
@@ -281,7 +237,7 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
                else if (errorMessage != null) {
                        _app.showErrorMessageNoLookup(getNameKey(), errorMessage);
                }
-               else if (inTileList.size() > 0) {
+               else if (numTiles > 0) {
                        _app.showErrorMessage(getNameKey(), "error.lookupsrtm.nonefound");
                }
                else {
@@ -294,7 +250,45 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
         * @param inUrl URL for online resource
         * @return ZipInputStream either on the local file or on the downloaded zip file
         */
-       private ZipInputStream getStreamToHgtFile(URL inUrl)
+       private ZipInputStream getStreamToSrtmData(URL inUrl)
+       throws IOException
+       {
+               ZipInputStream localData = null;
+               try {
+                       localData = getStreamToLocalHgtFile(inUrl);
+               }
+               catch (IOException ioe) {
+                       localData = null;
+               }
+               if (localData != null)
+               {
+                       return localData;
+               }
+               // try to download to cache
+               TileDownloader cacher = new TileDownloader();
+               TileDownloader.Result result = cacher.downloadTile(inUrl);
+               // System.out.println("Result: " + result);
+               if (result == TileDownloader.Result.DOWNLOADED)
+               {
+                       _numCached++;
+                       return getStreamToLocalHgtFile(inUrl);
+               }
+               // If we don't have a cache, we may be able to download it temporarily
+               if (result != TileDownloader.Result.DOWNLOAD_FAILED)
+               {
+                       _hadToDownload = true;
+                       return new ZipInputStream(inUrl.openStream());
+               }
+               // everything failed
+               return null;
+       }
+
+       /**
+        * Get the SRTM file from the local cache, if available
+        * @param inUrl URL for online resource
+        * @return ZipInputStream on the local file or null if not there
+        */
+       private ZipInputStream getStreamToLocalHgtFile(URL inUrl)
        throws IOException
        {
                String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
@@ -313,10 +307,66 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
                                }
                        }
                }
-               // System.out.println("Lookup: Trying online: " + inUrl.toString());
-               _hadToDownload = true;
-               // MAYBE: Only download if we're in online mode?
-               return new ZipInputStream(inUrl.openStream());
+               return null;
+       }
+
+       /**
+        * Given the height data read in from file, apply the given tile to all points
+        * in the track with missing altitude
+        * @param inTile tile being applied
+        * @param inHeights height data read in from file
+        * @param inOverwriteZeros true to overwrite zero altitude values
+        * @return number of altitudes found
+        */
+       private int applySrtmTileToWholeTrack(SrtmTile inTile, int[] inHeights, boolean inOverwriteZeros)
+       {
+               int numAltitudesFound = 0;
+               // Loop over all points in track, try to apply altitude from array
+               for (int p = 0; p < _track.getNumPoints(); p++)
+               {
+                       DataPoint point = _track.getPoint(p);
+                       if (!point.hasAltitude()
+                               || (inOverwriteZeros && point.getAltitude().getValue() == 0))
+                       {
+                               if (new SrtmTile(point).equals(inTile))
+                               {
+                                       double x = (point.getLongitude().getDouble() - inTile.getLongitude()) * 1200;
+                                       double y = 1201 - (point.getLatitude().getDouble() - inTile.getLatitude()) * 1200;
+                                       int idx1 = ((int)y)*1201 + (int)x;
+                                       try
+                                       {
+                                               int[] fouralts = {inHeights[idx1], inHeights[idx1+1], inHeights[idx1-1201], inHeights[idx1-1200]};
+                                               int numVoids = (fouralts[0]==VOID_VAL?1:0) + (fouralts[1]==VOID_VAL?1:0)
+                                                       + (fouralts[2]==VOID_VAL?1:0) + (fouralts[3]==VOID_VAL?1:0);
+                                               // if (numVoids > 0) System.out.println(numVoids + " voids found");
+                                               double altitude = 0.0;
+                                               switch (numVoids)
+                                               {
+                                                       case 0: altitude = bilinearInterpolate(fouralts, x, y); break;
+                                                       case 1: altitude = bilinearInterpolate(fixVoid(fouralts), x, y); break;
+                                                       case 2:
+                                                       case 3: altitude = averageNonVoid(fouralts); break;
+                                                       default: altitude = VOID_VAL;
+                                               }
+                                               // Special case for terrain tracks, don't interpolate voids yet
+                                               if (!_normalTrack && numVoids > 0) {
+                                                       altitude = VOID_VAL;
+                                               }
+                                               if (altitude != VOID_VAL)
+                                               {
+                                                       point.setFieldValue(Field.ALTITUDE, ""+altitude, false);
+                                                       // depending on settings, this value may have been added as feet, we need to force metres
+                                                       point.getAltitude().reset(new Altitude((int)altitude, UnitSetLibrary.UNITS_METRES));
+                                                       numAltitudesFound++;
+                                               }
+                                       }
+                                       catch (ArrayIndexOutOfBoundsException obe) {
+                                               // System.err.println("lat=" + point.getLatitude().getDouble() + ", x=" + x + ", y=" + y + ", idx=" + idx1);
+                                       }
+                               }
+                       }
+               }
+               return numAltitudesFound;
        }
 
        /**
@@ -383,4 +433,18 @@ public class LookupSrtmFunction extends GenericFunction implements Runnable
        {
                return _running;
        }
+
+       private void showConfirmMessage(int numDownloaded)
+       {
+               if (numDownloaded == 1)
+               {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getTextWithNumber("confirm.downloadsrtm.1", numDownloaded),
+                               I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+               }
+               else if (numDownloaded > 1)
+               {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getTextWithNumber("confirm.downloadsrtm", numDownloaded),
+                               I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+               }
+       }
 }
index 301bbaf2ccb7849f1378a6f910999c188965a12a..058132c9470b9721f82b058f822d4ab602f99020 100644 (file)
@@ -36,14 +36,25 @@ public class SrtmTile
                _longitude = inLongitude;
        }
 
+       @Override
+       public int hashCode()
+       {
+               return _latitude * 1000 + _longitude;
+       }
+
        /**
         * Check for equality
         * @param inOther other tile object
         * @return true if both represent same tile
         */
-       public boolean equals(SrtmTile inOther)
+       @Override
+       public boolean equals(Object inOther)
        {
-               return (_latitude == inOther._latitude) && (_longitude == inOther._longitude);
+               if (inOther == null || inOther.getClass() != getClass()) {
+                       return false;
+               }
+               SrtmTile otherTile = (SrtmTile) inOther;
+               return (_latitude == otherTile._latitude) && (_longitude == otherTile._longitude);
        }
 
        /** @return latitude as int */
diff --git a/src/tim/prune/function/srtm/TileDownloader.java b/src/tim/prune/function/srtm/TileDownloader.java
new file mode 100644 (file)
index 0000000..831d4dd
--- /dev/null
@@ -0,0 +1,107 @@
+package tim.prune.function.srtm;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import tim.prune.GpsPrune;
+import tim.prune.config.Config;
+
+/**
+ * Class to provide a download function for the Space Shuttle's SRTM data files.
+ * HGT files are downloaded into memory via HTTP and stored in the map cache.
+ */
+public class TileDownloader
+{
+       /** Possible results of the download */
+       public enum Result {DOWNLOADED, NOTHING_TO_DO, DOWNLOAD_FAILED, CACHE_FAILED};
+
+       /**
+        * Download a single tile of SRTM data
+        * @param inUrl remote URL to get
+        */
+       public Result downloadTile(URL inUrl)
+       {
+               if (inUrl == null) {
+                       return Result.NOTHING_TO_DO;
+               }
+
+               // Check the cache is ok
+               final String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
+               if (diskCachePath != null)
+               {
+                       File srtmDir = new File(diskCachePath, "srtm");
+                       if (srtmDir.exists() && !srtmDir.isDirectory()) {
+                               // exists but isn't a directory - can't be used
+                               return Result.CACHE_FAILED;
+                       }
+                       if (!srtmDir.exists() && !srtmDir.mkdir()) {
+                               // can't create the srtm directory
+                               return Result.CACHE_FAILED;
+                       }
+               }
+               else {
+                       // no cache set up
+                       return Result.CACHE_FAILED;
+               }
+
+               // Define streams
+               FileOutputStream outStream = null;
+               InputStream inStream = null;
+               Result result = Result.NOTHING_TO_DO;
+               try
+               {
+                       // See if we've already got this tile or not
+                       File outputFile = getFileToWrite(inUrl);
+                       if (outputFile != null)
+                       {
+                               System.out.println("Download: Need to download: " + inUrl);
+                               outStream = new FileOutputStream(outputFile);
+                               URLConnection conn = inUrl.openConnection();
+                               conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+                               inStream = conn.getInputStream();
+                               // Copy all the bytes to the file
+                               int c;
+                               while ((c = inStream.read()) != -1)
+                               {
+                                       outStream.write(c);
+                               }
+                               result = Result.DOWNLOADED;
+                       }
+               }
+               catch (IOException ioe) {
+                       System.err.println(ioe.getClass().getName() + " - " + ioe.getMessage());
+                       result = Result.DOWNLOAD_FAILED;
+               }
+               // Make sure streams are closed
+               try {inStream.close();} catch (Exception e) {}
+               try {outStream.close();} catch (Exception e) {}
+
+               return result;
+       }
+
+       /**
+        * See whether the SRTM file is already available locally
+        * @param inUrl URL for online resource
+        * @return file object to write to, or null if already there
+        */
+       private static File getFileToWrite(URL inUrl)
+       {
+               String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
+               if (diskCachePath != null)
+               {
+                       File srtmDir = new File(diskCachePath, "srtm");
+                       if (srtmDir.exists() && srtmDir.isDirectory() && srtmDir.canRead())
+                       {
+                               File srtmFile = new File(srtmDir, new File(inUrl.getFile()).getName());
+                               if (!srtmFile.exists() || !srtmFile.canRead() || srtmFile.length() <= 400) {
+                                       return srtmFile;
+                               }
+                       }
+               }
+               return null;
+       }
+}
index 60a9479d1289faae0523e0fd7d025b56e816d37e..d237971eb05f38de1a2b8a7c7ae24f0ffa55e8fe 100644 (file)
@@ -3,17 +3,19 @@ package tim.prune.function.srtm;
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.util.ArrayList;
 
 
 /**
  * Class to get the URLs of the SRTM tiles
  * using the srtmtiles.dat file
  */
-public abstract class TileFinder
+public class TileFinder
 {
+       /** tile data loaded from file */
+       private byte[] _tileData = null;
+
        /** URL prefix for all tiles */
-       private static final String URL_PREFIX = "https://dds.cr.usgs.gov/srtm/version2_1/SRTM3/";
+       private static final String URL_PREFIX = "https://srtm.kurviger.de/SRTM3/";
        /** Directory names for each continent */
        private static final String[] CONTINENTS = {"", "Eurasia", "North_America", "Australia",
                "Islands", "South_America", "Africa"};
@@ -24,28 +26,33 @@ public abstract class TileFinder
         * @param inTiles list of Tiles to get
         * @return array of URLs
         */
-       public static URL[] getUrls(ArrayList<SrtmTile> inTiles)
+       public URL getUrl(SrtmTile inTile)
        {
-               if (inTiles == null || inTiles.size() < 1) {return null;}
-               URL[] urls = new URL[inTiles.size()];
-               // Read dat file into array
-               byte[] lookup = readDatFile();
-               for (int t=0; t<inTiles.size(); t++)
+               if (inTile == null) {return null;}
+               if (_tileData == null)
                {
-                       SrtmTile tile = inTiles.get(t);
-                       // Get byte from lookup array
-                       int idx = (tile.getLatitude() + 59)*360 + (tile.getLongitude() + 180);
-                       try
+                       _tileData = readDatFile();
+                       if (_tileData == null)
                        {
-                               int dir = lookup[idx];
-                               if (dir > 0) {
-                                       try {
-                                               urls[t] = new URL(URL_PREFIX + CONTINENTS[dir] + "/" + tile.getTileName());
-                                       } catch (MalformedURLException e) {} // ignore error, url stays null
-                               }
-                       } catch (ArrayIndexOutOfBoundsException e) {} // ignore error, url stays null
+                               System.err.println("Build error: resource srtmtiles.dat missing!");
+                               return null;
+                       }
                }
-               return urls;
+
+               URL url = null;
+               // Get byte from lookup array
+               int idx = (inTile.getLatitude() + 59)*360 + (inTile.getLongitude() + 180);
+               try
+               {
+                       int dir = _tileData[idx];
+                       if (dir > 0) {
+                               try {
+                                       url = new URL(URL_PREFIX + CONTINENTS[dir] + "/" + inTile.getTileName());
+                               } catch (MalformedURLException e) {} // ignore error, url stays null
+                       }
+               } catch (ArrayIndexOutOfBoundsException e) {} // ignore error, url stays null
+
+               return url;
        }
 
        /**
diff --git a/src/tim/prune/gui/CoordDisplay.java b/src/tim/prune/gui/CoordDisplay.java
new file mode 100644 (file)
index 0000000..45a749a
--- /dev/null
@@ -0,0 +1,58 @@
+package tim.prune.gui;
+
+import tim.prune.data.Coordinate;
+
+/**
+ * Functions for display of coordinates in gui
+ */
+public abstract class CoordDisplay
+{
+
+       /**
+        * Construct an appropriate coordinate label using the selected format
+        * @param inCoordinate coordinate
+        * @param inFormat selected display format
+        * @return language-sensitive string
+        */
+       public static String makeCoordinateLabel(Coordinate inCoordinate, int inFormat)
+       {
+               String coord = inCoordinate.output(inFormat);
+               // Fix broken degree signs (due to unicode mangling)
+               final char brokenDeg = 65533;
+               if (coord.indexOf(brokenDeg) >= 0)
+               {
+                       coord = coord.replaceAll(String.valueOf(brokenDeg), "\u00B0");
+               }
+               return restrictDP(coord);
+       }
+
+
+       /**
+        * Restrict the given coordinate to a limited number of decimal places for display
+        * @param inCoord coordinate string
+        * @return chopped string
+        */
+       private static String restrictDP(String inCoord)
+       {
+               final int DECIMAL_PLACES = 7;
+               if (inCoord == null) return "";
+               String result = inCoord;
+               final int dotPos = Math.max(inCoord.lastIndexOf('.'), inCoord.lastIndexOf(','));
+               if (dotPos >= 0)
+               {
+                       final int chopPos = dotPos + DECIMAL_PLACES;
+                       if (chopPos < (inCoord.length()-1))
+                       {
+                               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);
+                               if (expPos > 0 && expPos < (inCoord.length()-1))
+                               {
+                                       result += inCoord.substring(expPos);
+                               }
+                       }
+               }
+               return result;
+       }
+
+}
index 2fa98dc9c0907d52e19489372c15eff227f5e37a..1b8148d5d925f43991901ea8f58c4673223bd80c 100644 (file)
@@ -306,6 +306,9 @@ public class DetailsDisplay extends GenericDisplay
                if (_timezone == null || (inUpdateType | UNITS_CHANGED) > 0) {
                        _timezone = TimezoneHelper.getSelectedTimezone();
                }
+               if ((inUpdateType | UNITS_CHANGED) > 0) {
+                       Config.setConfigString(Config.KEY_COORD_DISPLAY_FORMAT, "" + getSelectedCoordFormat());
+               }
 
                if (_track == null || currentPoint == null)
                {
@@ -327,8 +330,10 @@ public class DetailsDisplay extends GenericDisplay
                        _indexLabel.setText(LABEL_POINT_SELECTED
                                + (currentPointIndex+1) + " " + I18nManager.getText("details.index.of")
                                + " " + _track.getNumPoints());
-                       _latLabel.setText(makeCoordinateLabel(LABEL_POINT_LATITUDE, currentPoint.getLatitude(), _coordFormatDropdown.getSelectedIndex()));
-                       _longLabel.setText(makeCoordinateLabel(LABEL_POINT_LONGITUDE, currentPoint.getLongitude(), _coordFormatDropdown.getSelectedIndex()));
+                       _latLabel.setText(LABEL_POINT_LATITUDE
+                               + CoordDisplay.makeCoordinateLabel(currentPoint.getLatitude(), getSelectedCoordFormat()));
+                       _longLabel.setText(LABEL_POINT_LONGITUDE
+                               + CoordDisplay.makeCoordinateLabel(currentPoint.getLongitude(), getSelectedCoordFormat()));
                        Unit altUnit = Config.getUnitSet().getAltitudeUnit();
                        _altLabel.setText(currentPoint.hasAltitude()?
                                (LABEL_POINT_ALTITUDE + currentPoint.getAltitude().getValue(altUnit) + " " +
@@ -346,7 +351,8 @@ public class DetailsDisplay extends GenericDisplay
                        }
                        // Maybe the point has a description?
                        String pointDesc = currentPoint.getFieldValue(Field.DESCRIPTION);
-                       if (pointDesc == null || pointDesc.equals("") || currentPoint.hasMedia()) {
+                       if (pointDesc == null || pointDesc.equals("") || currentPoint.hasMedia())
+                       {
                                _descLabel.setText("");
                                _descLabel.setToolTipText("");
                        }
@@ -409,11 +415,13 @@ public class DetailsDisplay extends GenericDisplay
                                        filename = info.getName();
                                }
                        }
-                       if (filename != null) {
+                       if (filename != null)
+                       {
                                _filenameLabel.setText(LABEL_POINT_FILENAME + filename);
                                _filenameLabel.setToolTipText(filename);
                        }
-                       else {
+                       else
+                       {
                                _filenameLabel.setText("");
                                _filenameLabel.setToolTipText("");
                        }
@@ -442,7 +450,8 @@ public class DetailsDisplay extends GenericDisplay
                                _aveSpeedLabel.setText(I18nManager.getText("details.range.avespeed") + ": "
                                        + DisplayUtils.roundedNumber(selection.getMovingDistance()/numMovingSeconds*3600.0) + " " + speedUnitsStr);
                        }
-                       else {
+                       else
+                       {
                                _durationLabel.setText("");
                                _aveSpeedLabel.setText("");
                        }
@@ -502,7 +511,9 @@ public class DetailsDisplay extends GenericDisplay
                        _photoThumbnail.setVisible(true);
                        _photoThumbnail.setPhoto(currentPhoto);
                        _rotationButtons.setVisible(true);
-                       if ((inUpdateType & DataSubscriber.PHOTOS_MODIFIED) > 0) {_photoThumbnail.refresh();}
+                       if ((inUpdateType & DataSubscriber.PHOTOS_MODIFIED) > 0) {
+                               _photoThumbnail.refresh();
+                       }
                }
                _photoThumbnail.repaint();
 
@@ -538,63 +549,6 @@ public class DetailsDisplay extends GenericDisplay
        }
 
 
-       /**
-        * Construct an appropriate coordinate label using the selected format
-        * @param inPrefix prefix of label
-        * @param inCoordinate coordinate
-        * @param inFormat index of format selection dropdown
-        * @return language-sensitive string
-        */
-       private static String makeCoordinateLabel(String inPrefix, Coordinate inCoordinate, int inFormat)
-       {
-               String coord = null;
-               switch (inFormat) {
-                       case 1: // degminsec
-                               coord = inCoordinate.output(Coordinate.FORMAT_DEG_MIN_SEC); break;
-                       case 2: // degmin
-                               coord = inCoordinate.output(Coordinate.FORMAT_DEG_MIN); break;
-                       case 3: // degrees
-                               coord = inCoordinate.output(Coordinate.FORMAT_DEG); break;
-                       default: // just as it was
-                               coord = inCoordinate.output(Coordinate.FORMAT_NONE);
-               }
-               // Fix broken degree signs (due to unicode mangling)
-               final char brokenDeg = 65533;
-               if (coord.indexOf(brokenDeg) >= 0) {
-                       coord = coord.replaceAll(String.valueOf(brokenDeg), "\u00B0");
-               }
-               return inPrefix + restrictDP(coord);
-       }
-
-
-       /**
-        * Restrict the given coordinate to a limited number of decimal places for display
-        * @param inCoord coordinate string
-        * @return chopped string
-        */
-       private static String restrictDP(String inCoord)
-       {
-               final int DECIMAL_PLACES = 7;
-               if (inCoord == null) return "";
-               String result = inCoord;
-               final int dotPos = Math.max(inCoord.lastIndexOf('.'), inCoord.lastIndexOf(','));
-               if (dotPos >= 0)
-               {
-                       final int chopPos = dotPos + DECIMAL_PLACES;
-                       if (chopPos < (inCoord.length()-1))
-                       {
-                               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);
-                               if (expPos > 0 && expPos < (inCoord.length()-1))
-                               {
-                                       result += inCoord.substring(expPos);
-                               }
-                       }
-               }
-               return result;
-       }
-
        /**
         * Make a details subpanel
         * @param inNameKey key to use for top label
@@ -657,4 +611,22 @@ public class DetailsDisplay extends GenericDisplay
                // string is too long
                return inString.substring(0, 20) + "...";
        }
+
+       /**
+        * @return the currently selected coordinate display format
+        */
+       private int getSelectedCoordFormat()
+       {
+               switch (_coordFormatDropdown.getSelectedIndex())
+               {
+                       case 1: // degminsec
+                               return Coordinate.FORMAT_DEG_MIN_SEC;
+                       case 2: // degmin
+                               return Coordinate.FORMAT_DEG_MIN;
+                       case 3: // degrees
+                               return Coordinate.FORMAT_DEG;
+                       default: // just as it was
+                               return Coordinate.FORMAT_NONE;
+               }
+       }
 }
index 25640d8e5072bfc2e70acfc3e26c7de208a494e3..2300296760fa1febfeb8f2dc59d393ca35bb639a 100644 (file)
@@ -36,7 +36,8 @@ public abstract class DisplayUtils
                if (inNumSecs < 86400L) return "" + (inNumSecs / 60 / 60) + I18nManager.getText("display.range.time.hours")
                        + " " + ((inNumSecs / 60) % 60) + I18nManager.getText("display.range.time.mins");
                if (inNumSecs < 432000L) return "" + (inNumSecs / 86400L) + I18nManager.getText("display.range.time.days")
-                       + " " + (inNumSecs / 60 / 60) % 24 + I18nManager.getText("display.range.time.hours");
+                       + " " + (inNumSecs / 60 / 60) % 24 + I18nManager.getText("display.range.time.hours")
+                       + " " + ((inNumSecs / 60) % 60) + I18nManager.getText("display.range.time.mins");
                if (inNumSecs < 86400000L) return "" + (inNumSecs / 86400L) + I18nManager.getText("display.range.time.days");
                return "big";
        }
index bce3b4d67b0a6df980b66882f175245af1134155..d87f459851fe62f3906c508a374fad6438e10c73 100644 (file)
@@ -30,10 +30,14 @@ import tim.prune.data.Selection;
 import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;
 import tim.prune.function.ChooseSingleParameter;
+import tim.prune.function.PasteCoordinateList;
+import tim.prune.function.PasteCoordinates;
+import tim.prune.function.PlusCodeFunction;
 import tim.prune.function.SearchOpenCachingDeFunction;
 import tim.prune.function.browser.UrlGenerator;
 import tim.prune.function.browser.WebMapFunction;
 import tim.prune.function.search.SearchMapillaryFunction;
+import tim.prune.function.settings.SaveConfig;
 
 /**
  * Class to manage the menu bar and tool bar,
@@ -74,6 +78,7 @@ public class MenuManager implements DataSubscriber
        private JMenuItem _selectEndItem = null;
        private JMenuItem _findWaypointItem = null;
        private JMenuItem _duplicatePointItem = null;
+       private JMenuItem _projectPointItem = null;
        private JMenuItem _reverseItem = null;
        private JMenuItem _addTimeOffsetItem = null;
        private JMenuItem _addAltitudeOffsetItem = null;
@@ -90,10 +95,7 @@ public class MenuManager implements DataSubscriber
        private JMenu     _browserMapMenu = null;
        private JMenuItem _routingGraphHopperItem = null;
        private JMenuItem _chartItem = null;
-       private JMenuItem _getGpsiesItem = null;
-       private JMenuItem _uploadGpsiesItem = null;
        private JMenuItem _lookupSrtmItem = null;
-       private JMenuItem _downloadSrtmItem = null;
        private JMenuItem _nearbyWikipediaItem = null;
        private JMenuItem _nearbyOsmPoiItem = null;
        private JMenuItem _showPeakfinderItem = null;
@@ -103,7 +105,7 @@ public class MenuManager implements DataSubscriber
        private JMenuItem _downloadOsmItem = null;
        private JMenuItem _getWeatherItem = null;
        private JMenuItem _distanceItem = null;
-       private JMenuItem _fullRangeDetailsItem = null;
+       private JMenuItem _viewFullDetailsItem = null;
        private JMenuItem _estimateTimeItem = null;
        private JMenuItem _learnEstimationParams = null;
        private JMenuItem _autoplayTrack = null;
@@ -257,14 +259,6 @@ public class MenuManager implements DataSubscriber
                // SRTM
                _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false);
                onlineMenu.add(_lookupSrtmItem);
-               _downloadSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_SRTM, false);
-               onlineMenu.add(_downloadSrtmItem);
-               // Get gpsies tracks
-               _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false);
-               onlineMenu.add(_getGpsiesItem);
-               // Upload to gpsies
-               _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES, false);
-               onlineMenu.add(_uploadGpsiesItem);
 
                onlineMenu.addSeparator();
                // browser submenu
@@ -491,9 +485,17 @@ public class MenuManager implements DataSubscriber
                // duplicate current point
                _duplicatePointItem = makeMenuItem(FunctionLibrary.FUNCTION_DUPLICATE_POINT, false);
                pointMenu.add(_duplicatePointItem);
+               // project current point
+               _projectPointItem = makeMenuItem(FunctionLibrary.FUNCTION_PROJECT_POINT, false);
+               pointMenu.add(_projectPointItem);
                // paste coordinates function
-               JMenuItem pasteCoordsItem = makeMenuItem(FunctionLibrary.FUNCTION_PASTE_COORDINATES);
+               JMenuItem pasteCoordsItem = makeMenuItem(new PasteCoordinates(_app));
                pointMenu.add(pasteCoordsItem);
+               JMenuItem pasteCoordsListItem = makeMenuItem(new PasteCoordinateList(_app));
+               pointMenu.add(pasteCoordsListItem);
+               // pluscodes function
+               JMenuItem plusCodeItem = makeMenuItem(new PlusCodeFunction(_app));
+               pointMenu.add(plusCodeItem);
                menubar.add(pointMenu);
 
                // Add view menu
@@ -530,8 +532,8 @@ public class MenuManager implements DataSubscriber
                _distanceItem = makeMenuItem(FunctionLibrary.FUNCTION_DISTANCES, false);
                viewMenu.add(_distanceItem);
                // full range details
-               _fullRangeDetailsItem = makeMenuItem(FunctionLibrary.FUNCTION_FULL_RANGE_DETAILS, false);
-               viewMenu.add(_fullRangeDetailsItem);
+               _viewFullDetailsItem = makeMenuItem(FunctionLibrary.FUNCTION_FULL_DETAILS, false);
+               viewMenu.add(_viewFullDetailsItem);
                // estimate time
                _estimateTimeItem = makeMenuItem(FunctionLibrary.FUNCTION_ESTIMATE_TIME, false);
                viewMenu.add(_estimateTimeItem);
@@ -659,7 +661,10 @@ public class MenuManager implements DataSubscriber
                _autosaveSettingsCheckbox.setSelected(Config.getConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS));
                _autosaveSettingsCheckbox.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
-                               Config.setConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS, _autosaveSettingsCheckbox.isSelected());
+                               final boolean autosaveOn = _autosaveSettingsCheckbox.isSelected();
+                               Config.setConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS, autosaveOn);
+                               // Maybe want to save config?
+                               new SaveConfig(_app).autosaveSwitched(autosaveOn);
                        }
                });
                settingsMenu.add(_autosaveSettingsCheckbox);
@@ -868,7 +873,7 @@ public class MenuManager implements DataSubscriber
                _markRectangleItem.setEnabled(hasData);
                _markUphillLiftsItem.setEnabled(hasData && _track.hasAltitudeData());
                _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints());
-               _rearrangeWaypointsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
+               _rearrangeWaypointsItem.setEnabled(hasData && _track.hasWaypoints() && _track.getNumPoints() > 1);
                final boolean hasSeveralTrackPoints = hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3;
                _splitSegmentsItem.setEnabled(hasSeveralTrackPoints);
                _sewSegmentsItem.setEnabled(hasSeveralTrackPoints);
@@ -880,16 +885,12 @@ public class MenuManager implements DataSubscriber
                _browserMapMenu.setEnabled(hasData);
                _distanceItem.setEnabled(hasData);
                _autoplayTrack.setEnabled(hasData && _track.getNumPoints() > 3);
-               _getGpsiesItem.setEnabled(hasData);
-               _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());
-               // have we got a cache?
-               _downloadSrtmItem.setEnabled(hasData && Config.getConfigString(Config.KEY_DISK_CACHE) != null);
                // have we got any timestamps?
                _deleteByDateItem.setEnabled(hasData && _track.hasData(Field.TIMESTAMP));
 
@@ -911,6 +912,7 @@ public class MenuManager implements DataSubscriber
                _selectEndItem.setEnabled(hasPoint);
                _selectEndButton.setEnabled(hasPoint);
                _duplicatePointItem.setEnabled(hasPoint);
+               _projectPointItem.setEnabled(hasPoint);
                _showPeakfinderItem.setEnabled(hasPoint);
                _showGeohackItem.setEnabled(hasPoint);
                _searchOpencachingDeItem.setEnabled(hasPoint);
@@ -958,7 +960,7 @@ public class MenuManager implements DataSubscriber
                _addAltitudeOffsetItem.setEnabled(hasRange);
                _convertNamesToTimesItem.setEnabled(hasRange && _track.hasWaypoints());
                _deleteFieldValuesItem.setEnabled(hasRange);
-               _fullRangeDetailsItem.setEnabled(hasRange);
+               _viewFullDetailsItem.setEnabled(hasRange || hasPoint);
                _estimateTimeItem.setEnabled(hasRange);
                _learnEstimationParams.setEnabled(hasData && _track.hasTrackPoints() && _track.hasData(Field.TIMESTAMP)
                        && _track.hasAltitudeData());
@@ -996,6 +998,7 @@ public class MenuManager implements DataSubscriber
                        }
                        else
                        {
+                               rfl.verifyAll();
                                // Rebuild menus
                                _recentFileMenu.removeAll();
                                for (int i=0; i<rfl.getSize(); i++)
index a5462ed1bd078db252167b18aab5116bd9c78d29..1839d87bfe8801310980e49259f145f122c05c6c 100644 (file)
@@ -59,7 +59,8 @@ public class StatusBar extends JPanel implements Runnable, DataSubscriber
                _label.setText(" " + inMessage);
                _timer = System.currentTimeMillis() + DEFAULT_CLEAR_INTERVAL;
                // If necessary, start a new checker thread
-               if (_thread == null || !_thread.isAlive()) {
+               if (_thread == null || !_thread.isAlive())
+               {
                        _thread = new Thread(this);
                        _thread.start();
                }
index 25eba2fb1847306d41aed63021c7cbe3e4ed2cf0..38620e474ce6b78b7d72a06e2ce35303d7acbd3d 100644 (file)
@@ -7,7 +7,7 @@ import tim.prune.gui.map.MapUtils;
 /**
  * Class to provide access to current viewport
  * The point of this class is to decouple the view from the MapCanvas object
- * so that when the GetGpsies function needs to know the area currently viewed, it doesn't
+ * so that when a search function needs to know the area currently viewed, it doesn't
  * need to have a direct connection to the MapCanvas.  Instead it asks the App for the viewport,
  * which is then able to get the map position from the MapCanvas.
  * I'm still not sure whether this is ugly or not, but it's more efficient than constantly listening.
index 767456eead95c533c1bf772cb9811ae3b57256a1..8c8829fd8a38d5dc2792d5a5f12238bbcbc7d9aa 100644 (file)
@@ -130,7 +130,7 @@ public abstract class ColourerFactory
         * @param inColourer PointColourer object
         * @return string describing object (for later re-creation) or null
         */
-       public static String PointColourerToString(PointColourer inColourer)
+       public static String pointColourerToString(PointColourer inColourer)
        {
                if (inColourer != null)
                {
index 2a0e8cb9903c205d3703cbdf6da6e5453a96a9ed..4f0891843dea2632127a1ef9ae2dbab2aa9d0b30 100644 (file)
@@ -26,13 +26,13 @@ public class FileColourer extends DiscretePointColourer
 
        /**
         * Calculate the colours for each of the points in the given track
-        * @param inTrack track object
+        * @param inTrackInfo track info object
         */
        @Override
        public void calculateColours(TrackInfo inTrackInfo)
        {
                // initialise the array to the right size
-               final int numPoints = inTrackInfo == null ? 0 : inTrackInfo.getTrack().getNumPoints();
+               final int numPoints = inTrackInfo.getTrack().getNumPoints();
                init(numPoints);
 
                // loop over track points
index 66b190d576a491b8f4d94a908bdd179490de4bc7..c26d5842c33b5fa3fe39c4f109437c56b85fde13 100644 (file)
Binary files a/src/tim/prune/gui/images/wpicon_ring_l.png and b/src/tim/prune/gui/images/wpicon_ring_l.png differ
index 3ae352b757942ac9f3b5187cdb875f55d0ef4f3e..9e7f1713c3ec8a235432115f5deb6cc596fcea2c 100644 (file)
Binary files a/src/tim/prune/gui/images/wpicon_ring_m.png and b/src/tim/prune/gui/images/wpicon_ring_m.png differ
diff --git a/src/tim/prune/gui/map/CacheFailure.java b/src/tim/prune/gui/map/CacheFailure.java
new file mode 100644 (file)
index 0000000..6f9ac28
--- /dev/null
@@ -0,0 +1,8 @@
+package tim.prune.gui.map;
+
+/**
+ * Exception thrown to indicate a failure to cache map tiles
+ */
+public class CacheFailure extends Exception
+{
+}
index fd2042a7714acfe2ca1f5b47693ce9df06b0a79f..9be7841372299894abd9a26ca85ea149ee9ad8fa 100644 (file)
@@ -25,10 +25,22 @@ public class DiskTileCacher implements Runnable
        private File _file = null;
        /** Observer to be notified */
        private ImageObserver _observer = null;
+       /** True if cacher is active, false if blocked */
+       private boolean _active = false;
+
        /** Time limit to cache images for */
        private static final long CACHE_TIME_LIMIT = 20 * 24 * 60 * 60 * 1000; // 20 days in ms
        /** Hashset of all blocked / 404 tiles to avoid requesting them again */
        private static final HashSet<String> BLOCKED_URLS = new HashSet<String>();
+       /**Hashset of files which are currently being processed */
+       private static final HashSet<String> DOWNLOADING_FILES = new HashSet<String>();
+       /** Number of currently active threads */
+       private static int NUMBER_ACTIVE_THREADS = 0;
+       /** Flag to remember whether any server connection is possible */
+       private static boolean CONNECTION_ACTIVE = true;
+       /** Flag to remember whether we have already tried to create the base path */
+       private static boolean TRIED_TO_CREATE_BASEPATH = false;
+
 
        /**
         * Private constructor
@@ -40,6 +52,7 @@ public class DiskTileCacher implements Runnable
                _url = inUrl;
                _file = inFile;
                _observer = inObserver;
+               _active = registerCacher(inFile.getAbsolutePath());
        }
 
        /**
@@ -75,69 +88,119 @@ public class DiskTileCacher implements Runnable
         * @param inBasePath base path to disk cache
         * @param inTilePath relative path to this tile
         * @param inObserver observer to inform when load complete
+        * @throws CacheFailure if tile could not be saved
         */
-       public static void saveTile(URL inUrl, String inBasePath, String inTilePath, ImageObserver inObserver)
+       public static void saveTile(URL inUrl, String inBasePath, String inTilePath, ImageObserver inObserver) throws CacheFailure
        {
                if (inBasePath == null || inTilePath == null) {return;}
                // save file if possible
                File basePath = new File(inBasePath);
-               if (!basePath.exists() || !basePath.isDirectory() || !basePath.canWrite()) {
+               if (!checkBasePath(basePath))
+               {
                        // Can't write to base path
-                       return;
+                       throw new CacheFailure();
                }
                File tileFile = new File(basePath, inTilePath);
-               // Check if this file is already being loaded
-               if (isBeingLoaded(tileFile)) {return;}
+
                // Check if it has already failed
-               if (BLOCKED_URLS.contains(inUrl.toString())) {return;}
+               if (BLOCKED_URLS.contains(inUrl.toString())) {
+                       return;
+               }
 
                File dir = tileFile.getParentFile();
-               // Start a new thread to load the image if necessary
+               // Construct a cacher to load the image if necessary
                if ((dir.exists() || dir.mkdirs()) && dir.canWrite())
                {
-                       new Thread(new DiskTileCacher(inUrl, tileFile, inObserver)).start();
+                       DiskTileCacher cacher = new DiskTileCacher(inUrl, tileFile, inObserver);
+                       cacher.startDownloading();
                }
        }
 
        /**
-        * Check whether the given tile is already being loaded
-        * @param inFile desired file
-        * @return true if temporary file with this name exists
+        * Check the given base path, and try (once) to create it if necessary
+        * @return true if base path can be written to
         */
-       private static boolean isBeingLoaded(File inFile)
+       private static boolean checkBasePath(File inBasePath)
        {
-               File tempFile = new File(inFile.getAbsolutePath() + ".temp");
-               if (!tempFile.exists()) {
-                       return false;
+               if (!inBasePath.exists() && !TRIED_TO_CREATE_BASEPATH)
+               {
+                       TRIED_TO_CREATE_BASEPATH = true;
+                       System.out.println("Base path '" + inBasePath.getAbsolutePath() + "' does not exist, trying to create");
+                       return inBasePath.mkdirs();
+               }
+               return inBasePath.exists() && inBasePath.isDirectory() && inBasePath.canWrite();
+       }
+
+       /**
+        * Start downloading the configured tile
+        */
+       private void startDownloading()
+       {
+               if (_active)
+               {
+                       new Thread(this).start();
                }
-               // File exists, so check if it was created recently
-               final long fileAge = System.currentTimeMillis() - tempFile.lastModified();
-               return fileAge < 1000000L; // overwrite if the temp file is still there after 1000s
        }
 
        /**
         * Run method for loading URL asynchronously and saving to file
         */
        public void run()
+       {
+               waitUntilAllowedToRun();
+               if (doDownload())
+               {
+                       if (!CONNECTION_ACTIVE)
+                       {
+                               // wasn't active before but this download worked - we've come back online
+                               BLOCKED_URLS.clear();
+                               CONNECTION_ACTIVE = true;
+                       }
+               }
+               // Release file and thread
+               unregisterCacher(_file.getAbsolutePath());
+               threadFinished();
+       }
+
+       /**
+        * Blocks (in separate thread) until allowed by concurrent thread limit
+        */
+       private void waitUntilAllowedToRun()
+       {
+               while (!canStartNewThread())
+               {
+                       try {
+                               Thread.sleep(400);
+                       }
+                       catch (InterruptedException e) {}
+               }
+       }
+
+       /**
+        * @return true if download was successful
+        */
+       private boolean doDownload()
        {
                boolean finished = false;
                InputStream in = null;
                FileOutputStream out = null;
                File tempFile = new File(_file.getAbsolutePath() + ".temp");
-               // Use a synchronized block across all threads to make sure this url is only fetched once
-               synchronized (DiskTileCacher.class)
+
+               if (tempFile.exists())
                {
-                       if (tempFile.exists()) {tempFile.delete();}
-                       try {
-                               if (!tempFile.createNewFile()) {return;}
-                       }
-                       catch (Exception e) {return;}
+                       tempFile.delete();
                }
+               try
+               {
+                       if (!tempFile.createNewFile()) {return false;}
+               }
+               catch (Exception e) {return false;}
+
                try
                {
                        // Open streams from URL and to file
                        out = new FileOutputStream(tempFile);
-                       //System.out.println("Opening URL: " + _url.toString());
+                       //System.out.println("DiskTileCacher opening URL: " + _url.toString());
                        // Set http user agent on connection
                        URLConnection conn = _url.openConnection();
                        conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
@@ -148,27 +211,99 @@ public class DiskTileCacher implements Runnable
                                out.write(d);
                        }
                        finished = true;
-               } catch (IOException e) {
+               }
+               catch (IOException e)
+               {
                        System.err.println("ioe: " + e.getClass().getName() + " - " + e.getMessage());
                        BLOCKED_URLS.add(_url.toString());
+                       CONNECTION_ACTIVE = false;
                }
                finally
                {
                        // clean up files
                        try {in.close();} catch (Exception e) {} // ignore
                        try {out.close();} catch (Exception e) {} // ignore
-                       if (!finished) {
+                       if (!finished)
+                       {
                                tempFile.delete();
                        }
                }
+               boolean success = false;
                // Move temp file to desired file location
-               if (tempFile.exists() && !tempFile.renameTo(_file))
+               if (tempFile.exists() && tempFile.length() > 0L)
                {
-                       // File couldn't be moved - delete both to be sure
-                       tempFile.delete();
-                       _file.delete();
+                       if (tempFile.renameTo(_file))
+                       {
+                               success = true;
+                       }
+                       else if (_file.delete() && tempFile.renameTo(_file))
+                       {
+                               success = true;
+                       }
+                       else
+                       {
+                               System.out.println("Failed to rename temp file: " + tempFile.getAbsolutePath());
+                               tempFile.delete();
+                       }
                }
+
                // Tell parent that load is finished (parameters ignored)
                _observer.imageUpdate(null, ImageObserver.ALLBITS, 0, 0, 0, 0);
+               return success;
+       }
+
+       // Blocking of cachers working on same file
+
+       /**
+        * Register a cacher writing to the specified file path
+        * @param inFilePath destination path to tile file
+        * @return true if nobody else has claimed this file yet
+        */
+       private synchronized static boolean registerCacher(String inFilePath)
+       {
+               if (DOWNLOADING_FILES.contains(inFilePath))
+               {
+                       return false;
+               }
+               // Nobody has claimed this file yet
+               DOWNLOADING_FILES.add(inFilePath);
+               return true;
+       }
+
+       /**
+        * Cacher has finished dealing with the specified file
+        * @param inFilePath destination path to tile file
+        */
+       private synchronized static void unregisterCacher(String inFilePath)
+       {
+               DOWNLOADING_FILES.remove(inFilePath);
+       }
+
+       // Limiting of active threads
+
+       /**
+        * @return true if another thread is allowed to become active
+        */
+       private synchronized static boolean canStartNewThread()
+       {
+               final int MAXIMUM_NUM_THREADS = 8;
+               if (NUMBER_ACTIVE_THREADS < MAXIMUM_NUM_THREADS)
+               {
+                       NUMBER_ACTIVE_THREADS++;
+                       return true;
+               }
+               // Already too many threads active
+               return false;
+       }
+
+       /**
+        * Inform that one of the previously active threads has now completed
+        */
+       private synchronized static void threadFinished()
+       {
+               if (NUMBER_ACTIVE_THREADS > 0)
+               {
+                       NUMBER_ACTIVE_THREADS--;
+               }
        }
 }
index aa25dbcd939e39b96a6642f188b68665d4c74c4f..17918924f92e793f8100a8df5771aedbe39bd880 100644 (file)
@@ -85,7 +85,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        /** coordinates of popup menu */
        private int _popupMenuX = -1, _popupMenuY = -1;
        /** Flag to prevent showing too often the error message about loading maps */
-       private boolean _shownOsmErrorAlready = false;
+       private boolean _shownMapLoadErrorAlready = false;
        /** Current drawing mode */
        private int _drawMode = MODE_DEFAULT;
        /** Current waypoint icon definition */
@@ -339,8 +339,16 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
         */
        private void zoomToFit()
        {
-               _latRange = _track.getLatRange();
-               _lonRange = _track.getLonRange();
+               if (_track.getNumPoints() > 0)
+               {
+                       _latRange = _track.getLatRange();
+                       _lonRange = _track.getLonRange();
+               }
+               if (_latRange == null || _lonRange == null
+                       || !_latRange.hasData() || !_lonRange.hasData())
+               {
+                       setDefaultLatLonRange();
+               }
                _xRange = new DoubleRange(MapUtils.getXFromLongitude(_lonRange.getMinimum()),
                        MapUtils.getXFromLongitude(_lonRange.getMaximum()));
                _yRange = new DoubleRange(MapUtils.getYFromLatitude(_latRange.getMinimum()),
@@ -349,6 +357,37 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        getWidth(), getHeight());
        }
 
+       /**
+        * Track data is empty, so find a default area on the map to show
+        */
+       private void setDefaultLatLonRange()
+       {
+               String storedRange = Config.getConfigString(Config.KEY_LATLON_RANGE);
+               // Parse it into four latlon values
+               try
+               {
+                       String[] values = storedRange.split(";");
+                       if (values.length == 4)
+                       {
+                               final double lat1 = Double.valueOf(values[0]);
+                               final double lat2 = Double.valueOf(values[1]);
+                               if (lat1 >= -90.0 && lat1 <= 90.0 && lat2 >= -90.0 && lat2 <= 90.0 && lat1 != lat2)
+                               {
+                                       _latRange = new DoubleRange(lat1, lat2);
+                                       final double lon1 = Double.valueOf(values[2]);
+                                       final double lon2 = Double.valueOf(values[3]);
+                                       if (lon1 >= -180.0 && lon1 <= 180.0 && lon2 >= -180.0 && lon2 <= 180.0 && lon1 != lon2)
+                                       {
+                                               _lonRange = new DoubleRange(lon1, lon2);
+                                               return;
+                                       }
+                               }
+                       }
+               }
+               catch (Exception e) {}
+               _latRange = new DoubleRange(45.8, 47.9);
+               _lonRange = new DoubleRange(5.9, 10.6);
+       }
 
        /**
         * Paint method
@@ -360,7 +399,9 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                if (_mapImage != null && (_mapImage.getWidth() != getWidth() || _mapImage.getHeight() != getHeight())) {
                        _mapImage = null;
                }
-               if (_track.getNumPoints() > 0)
+               final boolean showMap = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
+               final boolean showSomething = _track.getNumPoints() > 0 || showMap;
+               if (showSomething)
                {
                        // Check for autopan if enabled / necessary
                        if (_autopanCheckBox.isSelected())
@@ -373,6 +414,14 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                _prevSelectedPoint = selectedPoint;
                        }
 
+                       // Recognise empty map position, if no data has been loaded
+                       if (_mapPosition.isEmpty())
+                       {
+                               // Set to some default area
+                               zoomToFit();
+                               _recalculate = true;
+                       }
+
                        // Draw the map contents if necessary
                        if (_mapImage == null || _recalculate)
                        {
@@ -425,6 +474,9 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        inG.drawString(I18nManager.getText("display.nodata"), 50, getHeight()/2);
                        _scaleBar.updateScale(-1, 0);
                }
+               // enable or disable panels
+               _topPanel.setVisible(showSomething);
+               _sidePanel.setVisible(showSomething);
                // Draw slider etc on top
                paintChildren(inG);
        }
@@ -503,7 +555,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                }
 
                // reset error message
-               if (!showMap) {_shownOsmErrorAlready = false;}
+               if (!showMap) {_shownMapLoadErrorAlready = false;}
                _recalculate = false;
                // Only get map tiles if selected
                if (showMap)
@@ -639,8 +691,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 pointSeparationForArrowsSqd = 400;
                final int pointSeparation1dForArrows = (int) (Math.sqrt(pointSeparationForArrowsSqd) * 0.7);
+               final int hugePointSeparationForArrows = 120;
 
                // try to set line width for painting
                if (inG instanceof Graphics2D)
@@ -713,35 +766,38 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                        pointsPainted = true;
 
                                        // Now consider whether we need to draw an arrow as well
-                                       if (drawArrows
-                                        && !drawnLastArrow
-                                        && (Math.abs(prevX-px) > pointSeparation1dForArrows || Math.abs(prevY-py) > pointSeparation1dForArrows))
+                                       if (drawArrows)
                                        {
-                                               final double pointSeparationSqd = (prevX-px) * (prevX-px) + (prevY-py) * (prevY-py);
-                                               if (pointSeparationSqd > pointSeparationForArrowsSqd)
+                                               final double pointDist = Math.max(Math.abs(prevX - px), Math.abs(prevY - py));
+                                               final int separationLimit = (drawnLastArrow ? hugePointSeparationForArrows : pointSeparation1dForArrows);
+                                               if (pointDist > separationLimit)
                                                {
-                                                       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 pointSeparationSqd = (prevX-px) * (prevX-px) + (prevY-py) * (prevY-py);
+                                                       if (pointSeparationSqd > pointSeparationForArrowsSqd)
                                                        {
-                                                               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);
+                                                               final double midX = (prevX + px) / 2.0;
+                                                               final double midY = (prevY + py) / 2.0;
+                                                               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;
                                                        }
-                                                       drawnLastArrow = midPointVisible;
                                                }
-                                       }
-                                       else
-                                       {
-                                               drawnLastArrow = false;
+                                               else
+                                               {
+                                                       drawnLastArrow = false;
+                                               }
                                        }
                                }
                                prevX = px; prevY = py;
@@ -941,9 +997,9 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                synchronized(this)
                {
                        // Show message if loading failed (but not too many times)
-                       if (!inIsOk && !_shownOsmErrorAlready && _mapCheckBox.isSelected())
+                       if (!inIsOk && !_shownMapLoadErrorAlready && _mapCheckBox.isSelected())
                        {
-                               _shownOsmErrorAlready = true;
+                               _shownMapLoadErrorAlready = true;
                                // use separate thread to show message about failing to load osm images
                                new Thread(new Runnable() {
                                        public void run() {
@@ -957,6 +1013,15 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                }
        }
 
+       /**
+        * Inform that a cache failure occurred
+        */
+       public void reportCacheFailure()
+       {
+               // Cache can't be used, so disable it - user will be reminded to set it up by the tips
+               Config.setConfigString(Config.KEY_DISK_CACHE, null);
+       }
+
        /**
         * Zoom out, if not already at minimum zoom
         */
@@ -1074,15 +1139,17 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
         */
        public void mouseClicked(MouseEvent inE)
        {
-               if (_track != null && _track.getNumPoints() > 0)
+               final boolean showMap = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
+               final boolean hasPoints = _track != null && _track.getNumPoints() > 0;
+               if (showMap || hasPoints)
                {
                        // select point if it's a left-click
                        if (!inE.isMetaDown())
                        {
                                if (inE.getClickCount() == 1)
                                {
-                                       // single click
-                                       if (_drawMode == MODE_DEFAULT)
+                                       // single left click
+                                       if (_drawMode == MODE_DEFAULT && hasPoints)
                                        {
                                                int pointIndex = _clickedPoint;
                                                if (pointIndex == INDEX_UNKNOWN)
@@ -1408,10 +1475,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        }
                }
                repaint();
-               // enable or disable components
-               boolean hasData = _track.getNumPoints() > 0;
-               _topPanel.setVisible(hasData);
-               _sidePanel.setVisible(hasData);
                // grab focus for the key presses
                this.requestFocus();
        }
index 25f05fc1a2a238370d8888ea4b807b7d3f5f8adb..a62d7821c6b1d54db906b73949449b4ef26a39a2 100644 (file)
@@ -17,6 +17,8 @@ public class MapPosition
        private int _zoom = 12;
        /** Factor to zoom by, 2 to the power of zoom */
        private int _zoomFactor = 1 << _zoom;
+       /** Flag to mark if this position has ever been set */
+       private boolean _empty = true;
        /** Maximum zoom level */
        private static final int MAX_ZOOM = 21;
 
@@ -47,10 +49,12 @@ public class MapPosition
                        }
                }
                if (requiredZoom < 2) requiredZoom = 2;
+               else if (requiredZoom > 18) requiredZoom = 18;
                // Set position
                setZoom(requiredZoom);
                _xPosition = transformToPixels((inMinX + inMaxX) / 2.0);
                _yPosition = transformToPixels((inMinY + inMaxY) / 2.0);
+               _empty = false;
        }
 
        /**
@@ -61,6 +65,7 @@ public class MapPosition
        {
                _zoom = inZoom;
                _zoomFactor = 1 << _zoom;
+               _empty = false;
        }
 
        /**
@@ -93,6 +98,7 @@ public class MapPosition
                // Set position
                _xPosition = (_xPosition - inWidth/2 + (inMinX + inMaxX) / 2) * multFactor;
                _yPosition = (_yPosition - inHeight/2 + (inMinY + inMaxY) / 2) * multFactor;
+               _empty = false;
        }
 
        /**
@@ -277,4 +283,12 @@ public class MapPosition
                _xPosition += inDeltaX;
                _yPosition += inDeltaY;
        }
+
+       /**
+        * @return true if this position has never been set
+        */
+       public boolean isEmpty()
+       {
+               return _empty;
+       }
 }
index c4e294665e128def7d1fcbfb048cb516624d5d00..aa7127dee7020a70c9adb42f3a077e99075b6b8b 100644 (file)
@@ -2,8 +2,7 @@ package tim.prune.gui.map;
 
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+
 
 /**
  * Class to represent any map source, whether an OsmMapSource
@@ -16,9 +15,6 @@ public abstract class MapSource
        /** File extensions */
        protected String[] _extensions = null;
 
-       /** Regular expression for catching server wildcards */
-       protected static final Pattern WILD_PATTERN = Pattern.compile("^(.*)\\[(.*)\\](.*)$");
-
 
        /**
         * @return the number of layers used in this source
@@ -120,32 +116,6 @@ public abstract class MapSource
                return urlstr;
        }
 
-       /**
-        * Fix the site name by stripping off protocol and www.
-        * This is used to create the file path for disk caching
-        * @param inUrl url to strip
-        * @return stripped url
-        */
-       protected static String fixSiteName(String inUrl)
-       {
-               if (inUrl == null || inUrl.equals("")) {return null;}
-               String url = inUrl.toLowerCase();
-               int idx = url.indexOf("://");
-               if (idx >= 0) {url = url.substring(idx + 3);}
-               if (url.startsWith("www.")) {url = url.substring(4);}
-               // Strip out any "[.*]" as well
-               if (url.indexOf('[') >= 0)
-               {
-                       Matcher matcher = WILD_PATTERN.matcher(url);
-                       if (matcher.matches()) {
-                               url = matcher.group(1) + matcher.group(3);
-                               if (url.length() > 1 && url.charAt(0) == '.') {
-                                       url = url.substring(1);
-                               }
-                       }
-               }
-               return url;
-       }
 
        /**
         * @return string which can be written to the Config
index 331d8fe94007b27045cc53b15920cd3270be8df3..08ab4bcf6e42dc7baf430edf27a45ac3574bfbd8 100644 (file)
@@ -42,10 +42,10 @@ public abstract class MapSourceLibrary
                _sourceList.add(new OsmMapSource("Cycling Trails", "https://[abc].tile.openstreetmap.org/", "png",
                        "https://tile.waymarkedtrails.org/cycling/", "png", 18));
                _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 MffMapSource("Mapsforfree", "https://maps-for-free.com/layer/relief/", "jpg",
+                       "https://maps-for-free.com/layer/water/", "gif", 11));
+               _sourceList.add(new OsmMapSource("Hikebikemap", "https://tiles.wmflabs.org/hikebike/",
+                       "https://tiles.wmflabs.org/hillshading/", 18));
                _sourceList.add(new OsmMapSource("OpenSeaMap", "http://tile.openstreetmap.org/",
                        "http://tiles.openseamap.org/seamark/", 18));
        }
index f7370c5c90c833e7f053fc786eafdae37ab7f843..03b364aa66ca919fe4557ae75c19fee495cf3e91 100644 (file)
@@ -162,6 +162,7 @@ public class MapTileManager implements ImageObserver
                        tempCache = _tempCaches[inLayer]; // Should probably guard array indexes here
                        tileImage = tempCache.getTile(inX, inY);
                        if (tileImage != null) {
+                               //System.out.println("Got tile from memory: " + inX + ", " + inY);
                                return tileImage;
                        }
                }
@@ -195,7 +196,6 @@ public class MapTileManager implements ImageObserver
                        try
                        {
                                URL tileUrl = new URL(_mapSource.makeURL(inLayer, _zoom, inX, inY));
-                               //System.out.println("Trying to fetch: " + tileUrl);
                                if (useDisk)
                                {
                                        DiskTileCacher.saveTile(tileUrl, diskCachePath,
@@ -210,6 +210,9 @@ public class MapTileManager implements ImageObserver
                                }
                        }
                        catch (MalformedURLException urle) {} // ignore
+                       catch (CacheFailure cf) {
+                               _consumer.reportCacheFailure();
+                       }
                }
                return tileImage;
        }
index 42806fc75e7e104c8fa2c3140fc9ad9d0deec459..fe3e5a6b45ef79d1a971b7d728536245b889c067 100644 (file)
@@ -32,8 +32,8 @@ public class MffMapSource extends MapSource
                _baseUrls[0] = fixBaseUrl(inUrl1);
                _baseUrls[1] = fixBaseUrl(inUrl2);
                _siteNames = new String[2];
-               _siteNames[0] = fixSiteName(_baseUrls[0]);
-               _siteNames[1] = fixSiteName(_baseUrls[1]);
+               _siteNames[0] = SiteNameUtils.convertUrlToDirectory(_baseUrls[0]);
+               _siteNames[1] = SiteNameUtils.convertUrlToDirectory(_baseUrls[1]);
                _extensions = new String[2];
                _extensions[0] = inExt1;
                _extensions[1] = inExt2;
index 925fcf623ea456154c512c5e5e89454eb5b46bf8..0cb579dfe271731073665397f8072ec92c1cba65 100644 (file)
@@ -1,6 +1,5 @@
 package tim.prune.gui.map;
 
-import java.util.regex.Matcher;
 import tim.prune.I18nManager;
 
 /**
@@ -84,8 +83,8 @@ public class OsmMapSource extends MapSource
                _extensions[0] = inExt1;
                _extensions[1] = inExt2;
                _siteNames = new String[2];
-               _siteNames[0] = fixSiteName(_baseUrls[0]);
-               _siteNames[1] = fixSiteName(_baseUrls[1]);
+               _siteNames[0] = SiteNameUtils.convertUrlToDirectory(_baseUrls[0]);
+               _siteNames[1] = SiteNameUtils.convertUrlToDirectory(_baseUrls[1]);
                // Swap layers if second layer given without first
                if (_baseUrls[0] == null && _baseUrls[1] != null)
                {
@@ -157,7 +156,7 @@ public class OsmMapSource extends MapSource
        {
                // Check if the base url has a [1234], if so replace at random
                StringBuffer url = new StringBuffer();
-               url.append(pickServerUrl(_baseUrls[inLayerNum]));
+               url.append(SiteNameUtils.pickServerUrl(_baseUrls[inLayerNum]));
                url.append(inZoom).append('/').append(inX).append('/').append(inY);
                url.append('.').append(getFileExtension(inLayerNum));
                if (_apiKey != null)
@@ -175,36 +174,6 @@ public class OsmMapSource extends MapSource
                return _maxZoom;
        }
 
-       /**
-        * If the base url contains something like [1234], then pick a server
-        * @param inBaseUrl base url
-        * @return modified base url
-        */
-       protected static final String pickServerUrl(String inBaseUrl)
-       {
-               if (inBaseUrl == null || inBaseUrl.indexOf('[') < 0) {
-                       return inBaseUrl;
-               }
-               // Check for [.*] (once only)
-               // Only need to support one, make things a bit easier
-               final Matcher matcher = WILD_PATTERN.matcher(inBaseUrl);
-               // if not, return base url unchanged
-               if (!matcher.matches()) {
-                       return inBaseUrl;
-               }
-               // if so, pick one at random and replace in the String
-               final String match = matcher.group(2);
-               final int numMatches = match.length();
-               String server = null;
-               if (numMatches > 0)
-               {
-                       int matchNum = (int) Math.floor(Math.random() * numMatches);
-                       server = "" + match.charAt(matchNum);
-               }
-               final String result = matcher.group(1) + (server==null?"":server) + matcher.group(3);
-               return result;
-       }
-
        /**
         * @return semicolon-separated list of all fields
         */
diff --git a/src/tim/prune/gui/map/SiteNameUtils.java b/src/tim/prune/gui/map/SiteNameUtils.java
new file mode 100644 (file)
index 0000000..cf53f72
--- /dev/null
@@ -0,0 +1,73 @@
+package tim.prune.gui.map;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Helper functions for manipulating tile site names
+ */
+public abstract class SiteNameUtils
+{
+       /** Regular expression for catching server wildcards */
+       private static final Pattern WILD_PATTERN = Pattern.compile("^(.*)\\[(.*)\\](.*)$");
+
+
+       /**
+        * If the base url contains something like [1234], then pick a server
+        * @param inBaseUrl base url
+        * @return modified base url
+        */
+       public static String pickServerUrl(String inBaseUrl)
+       {
+               if (inBaseUrl == null || inBaseUrl.indexOf('[') < 0) {
+                       return inBaseUrl;
+               }
+               // Check for [.*] (once only)
+               // Only need to support one, make things a bit easier
+               final Matcher matcher = WILD_PATTERN.matcher(inBaseUrl);
+               // if not, return base url unchanged
+               if (!matcher.matches()) {
+                       return inBaseUrl;
+               }
+               // if so, pick one at random and replace in the String
+               final String match = matcher.group(2);
+               final int numMatches = match.length();
+               String server = null;
+               if (numMatches > 0)
+               {
+                       int matchNum = (int) Math.floor(Math.random() * numMatches);
+                       server = "" + match.charAt(matchNum);
+               }
+               final String result = matcher.group(1) + (server==null?"":server) + matcher.group(3);
+               return result;
+       }
+
+
+       /**
+        * Fix the site name by stripping off protocol and www.
+        * This is used to create the file path for disk caching
+        * @param inUrl url to strip
+        * @return stripped url
+        */
+       public static String convertUrlToDirectory(String inUrl)
+       {
+               if (inUrl == null || inUrl.equals("")) {return null;}
+               String url = inUrl.toLowerCase();
+               int idx = url.indexOf("://");
+               if (idx >= 0) {url = url.substring(idx + 3);}
+               if (url.startsWith("www.")) {url = url.substring(4);}
+               // Strip out any "[.*]" as well
+               if (url.indexOf('[') >= 0)
+               {
+                       Matcher matcher = WILD_PATTERN.matcher(url);
+                       if (matcher.matches())
+                       {
+                               url = matcher.group(1) + matcher.group(3);
+                               if (url.length() > 1 && url.charAt(0) == '.') {
+                                       url = url.substring(1);
+                               }
+                       }
+               }
+               return url;
+       }
+}
index 35a557e5556bf5582f88971bfe911276f660dc84..a2de40a78bce3d62c2e143f33ff0d92b4fdae901 100644 (file)
@@ -7,4 +7,7 @@ public interface TileConsumer
 {
        /** Let the consumer know that the tiles have been updated */
        public void tilesUpdated(boolean inIsOk);
+
+       /** Let the consume know that a cache failure occurred */
+       public void reportCacheFailure();
 }
index 7c7540066c839d86105f71a0a95b3af1398d17de..16466f3e8631b4051fca5415719972413df8ecb7 100644 (file)
@@ -21,10 +21,13 @@ public class TileDownloader implements Runnable
        private int _layer = 0;
        private int _x = 0, _y = 0;
        private int _zoom = 0;
+
        /** Hashset of all blocked / 404 tiles to avoid requesting them again */
        private static final HashSet<String> BLOCKED_URLS = new HashSet<String>();
        /** Hashset of all currently loading tiles to avoid requesting them again */
        private static final HashSet<String> LOADING_URLS = new HashSet<String>();
+       /** Flag to maintain whether connection is active or not */
+       private static boolean CONNECTION_ACTIVE = true;
 
 
        /**
@@ -60,10 +63,12 @@ public class TileDownloader implements Runnable
                if (inManager != null && inUrl != null)
                {
                        String url = inUrl.toString();
-                       // System.out.println("Trigger load: " + url);
-                       if (!BLOCKED_URLS.contains(url) && !LOADING_URLS.contains(url))
+                       if (BLOCKED_URLS.contains(url))
+                       {
+                               System.out.println("Already blocked: " + url);
+                       }
+                       else if (!LOADING_URLS.contains(url))
                        {
-                               // System.out.println("Not blocked: " + url);
                                LOADING_URLS.add(url);
                                new Thread(new TileDownloader(inManager, inUrl, inLayer, inX, inY, inZoom)).start();
                        }
@@ -98,6 +103,17 @@ public class TileDownloader implements Runnable
 
                                // Pass back to manager so it can be stored in its memory cache
                                _manager.notifyImageLoaded(tile, _layer, _x, _y, _zoom);
+
+                               if (!CONNECTION_ACTIVE)
+                               {
+                                       // We've just come back online, so forget which tiles gave 404 before
+                                       System.out.println("Deleting blocked urls, currently holds " + BLOCKED_URLS.size());
+                                       synchronized(this.getClass())
+                                       {
+                                               BLOCKED_URLS.clear();
+                                       }
+                                       CONNECTION_ACTIVE = true;
+                               }
                        }
                }
                catch (IOException e)
@@ -108,6 +124,7 @@ public class TileDownloader implements Runnable
                                BLOCKED_URLS.add(_url.toString());
                        }
                        try {in.close();} catch (Exception e2) {}
+                       CONNECTION_ACTIVE = false;      // lost connection?
                }
                LOADING_URLS.remove(_url.toString());
        }
index 466fda2175de2418569297c0722c6cfb1a2fd3c0..95d800420f9d89231e15c2045a7411eb8acbe859 100644 (file)
@@ -61,7 +61,7 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                        if (!other.hasValue) {return;}
                        if (!hasValue) {
                                index = other.index;
-                               hasValue = other.hasValue;
+                               hasValue = true;
                        }
                        else {
                                index = Math.min(index, other.index);
@@ -73,7 +73,7 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                        if (!other.hasValue) {return;}
                        if (!hasValue) {
                                index = other.index;
-                               hasValue = other.hasValue;
+                               hasValue = true;
                        }
                        else {
                                index = Math.max(index, other.index);
@@ -239,7 +239,8 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                int selectedPoint = _trackInfo.getSelection().getCurrentPointIndex();
                // selection start, end
                int selectionStart = -1, selectionEnd = -1;
-               if (_trackInfo.getSelection().hasRangeSelected()) {
+               if (_trackInfo.getSelection().hasRangeSelected())
+               {
                        selectionStart = _trackInfo.getSelection().getStart();
                        selectionEnd = _trackInfo.getSelection().getEnd();
                }
@@ -289,12 +290,14 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                                                y = (int) (yScaleFactor * (value - minValue));
                                                g.fillRect(BORDER_WIDTH+x, height-BORDER_WIDTH - y, barWidth, y);
                                        }
-                                       else if (value >= 0.0) {
+                                       else if (value >= 0.0)
+                                       {
                                                // Bar upwards from the zero line
                                                y = height-BORDER_WIDTH - (int) (yScaleFactor * (value - minValue));
                                                g.fillRect(BORDER_WIDTH+x, y, barWidth, zeroY - y);
                                        }
-                                       else {
+                                       else
+                                       {
                                                // Bar downwards from the zero line
                                                int barHeight = (int) (yScaleFactor * value);
                                                g.fillRect(BORDER_WIDTH+x, zeroY, barWidth, -barHeight);
@@ -317,7 +320,8 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                                }
                        }
                }
-               catch (NullPointerException npe) { // ignore, probably due to data being changed
+               catch (NullPointerException npe)
+               { // ignore, probably due to data being changed
                }
                // Draw numbers on top of the graph to mark scale
                if (lineScale >= 1)
@@ -378,6 +382,11 @@ public class ProfileChart extends GenericDisplay implements MouseListener
         */
        private synchronized void makePopup()
        {
+               if (_track.getNumPoints() < 1)
+               {
+                       _popup = null;
+                       return;
+               }
                _popup = new JPopupMenu();
                JMenuItem altItem = new JMenuItem(I18nManager.getText("fieldname.altitude"));
                altItem.addActionListener(new ActionListener() {
@@ -458,29 +467,32 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                if ((inUpdateType & DATA_ADDED_OR_REMOVED) > 0) {
                        makePopup();
                }
-               if (inUpdateType == SELECTION_CHANGED) {
-                       triggerPartialRepaint();
+
+               ChartParameters currentParameters = new ChartParameters();
+               currentParameters.selectedPoint.set(_trackInfo.getSelection().getCurrentPointIndex());
+               if (_trackInfo.getSelection().hasRangeSelected())
+               {
+                       currentParameters.rangeStart.set(_trackInfo.getSelection().getStart());
+                       currentParameters.rangeEnd.set(_trackInfo.getSelection().getEnd());
+               }
+               if (inUpdateType == SELECTION_CHANGED)
+               {
+                       triggerPartialRepaint(currentParameters);
                }
                else
                {
                        repaint();
                }
+               _previousParameters = currentParameters;
        }
 
        /**
         * For performance reasons, only repaint the part of the graphics affected by
         * the change in selection
+        * @param currentParameters - contains the current selected point, range
         */
-       private void triggerPartialRepaint()
+       private void triggerPartialRepaint(ChartParameters currentParameters)
        {
-               ChartParameters currentParameters = new ChartParameters();
-               currentParameters.selectedPoint.set(_trackInfo.getSelection().getCurrentPointIndex());
-               if (_trackInfo.getSelection().hasRangeSelected())
-               {
-                       currentParameters.rangeStart.set(_trackInfo.getSelection().getStart());
-                       currentParameters.rangeEnd.set(_trackInfo.getSelection().getEnd());
-               }
-
                int minPointIndex = currentParameters.getMinChangedIndex(_previousParameters);
                minPointIndex = Math.max(minPointIndex, 0);
                int maxPointIndex = currentParameters.getMaxChangedIndex(_previousParameters);
@@ -488,10 +500,10 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                        maxPointIndex = _trackInfo.getTrack().getNumPoints() - 1;
                }
                // System.out.println("Redraw from index: " + minPointIndex + " to " + maxPointIndex);
-               _previousParameters = currentParameters;
-               final int region_x = (int) (_xScaleFactor * minPointIndex) + BORDER_WIDTH;
-               final int region_width = (int) (_xScaleFactor * (maxPointIndex-minPointIndex+2)) + 2;
+               final int region_x = (int) (_xScaleFactor * minPointIndex) + BORDER_WIDTH - 2;
+               final int region_width = (int) (_xScaleFactor * (maxPointIndex-minPointIndex+2)) + 6;
                repaint(region_x, 0, region_width, getHeight());
+               // System.out.println("Partial repaint, x=" + region_x + ", region_width=" + region_width);
        }
 
        /**
@@ -520,7 +532,8 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                                }
                        }
                }
-               else {
+               else if (_popup != null)
+               {
                        // right clicks
                        _popup.show(this, e.getX(), e.getY());
                }
@@ -538,12 +551,14 @@ public class ProfileChart extends GenericDisplay implements MouseListener
                                _data = new AltitudeData(_track);
                        }
                }
-               else if (inField == Field.SPEED) {
+               else if (inField == Field.SPEED)
+               {
                        if (!(_data instanceof SpeedData)) {
                                _data = new SpeedData(_track);
                        }
                }
-               else if (inField == Field.VERTICAL_SPEED) {
+               else if (inField == Field.VERTICAL_SPEED)
+               {
                        if (!(_data instanceof VerticalSpeedData)) {
                                _data = new VerticalSpeedData(_track);
                        }
index a98e720328939c27dcc13666938333200247b602..130a78a7ab42fe3a881e54852c2dc5423bca9bd6 100644 (file)
@@ -21,9 +21,9 @@ public class ExifTiffHandler
        private long _thumbnailOffset = -1L, _thumbnailLength = -1L;
 
        /** This tag is a pointer to the Exif SubIFD. */
-       final int DIR_EXIF_SUB_IFD_OFFSET = 0x8769;
+       private static 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 DIR_GPS_INFO_OFFSET = 0x8825;
 
        private static final int TAG_GPS_LATITUDE_REF  = 0x0001;
        private static final int TAG_GPS_LATITUDE      = 0x0002;
@@ -142,6 +142,9 @@ public class ExifTiffHandler
                        case TAG_THUMBNAIL_LENGTH:
                                _thumbnailLength = intVal;
                                break;
+                       case TAG_GPS_BEARING:
+                               _jpegData.setBearing(intVal);
+                               break;
                }
        }
 
index 54c713a96c696034603bac7b96562cc5d574e7ad..8fab157828119bb6f028bd31b8fe17eac04d321f 100644 (file)
@@ -100,7 +100,7 @@ function.pastecoordinates=Verskaf nuwe koordinate
 function.charts=Grafieke
 function.show3d=Vertoon 3D
 function.distances=Afstande
-function.fullrangedetails=Vol reeks besonderhede
+function.viewfulldetails=Vol besonderhede
 function.estimatetime=Skat tyd
 function.learnestimationparams=Leer tyd skatings paramters
 function.setmapbg=Stel kaart agtergrond
@@ -108,10 +108,7 @@ function.setpaths=Stel program paaie
 function.selectsegment=Selekteer huidige segment
 function.splitsegments=Verdeel baan in segmente
 function.sewsegments=Naai baan segmente aanmekaar
-function.getgpsies=Kry Spsies spore
-function.uploadgpsies=Laai baan op na Gpsies
 function.lookupsrtm=Kry hoogtes vanaf SRTM
-function.downloadsrtm=Laai SRTM te\u00ebls af
 function.getwikipedia=Kry nabye Wikipedia artikels
 function.searchwikipedianames=Soek Wikiepdia volgens naam
 function.searchopencachingde=Soek OpenCaching.de
@@ -360,18 +357,6 @@ dialog.gpsies.column.length=Lengte
 dialog.gpsies.description=Beskrywing
 dialog.gpsies.nodescription=Geen beskrywing
 dialog.gpsies.nonefound=Geen bane gevind
-dialog.gpsies.username=Gpsies gebruikersnaam
-dialog.gpsies.password=Spsies wagwoord
-dialog.gpsies.keepprivate=Hou baan privaat
-dialog.gpsies.confirmopenpage=Maak web blad oop vir opgelaaide baan
-dialog.gpsies.activities=Aktiwiteits tipes
-dialog.gpsies.activity.trekking=Stap
-dialog.gpsies.activity.walking=Loop
-dialog.gpsies.activity.jogging=Hardloop
-dialog.gpsies.activity.biking=Fietsry
-dialog.gpsies.activity.motorbiking=Moterfietsry
-dialog.gpsies.activity.sailing=Seiljagwedvaart
-dialog.gpsies.activity.skating=Skaats
 dialog.wikipedia.column.name=Artikel naam
 dialog.wikipedia.column.distance=Afstand
 dialog.wikipedia.nonefound=Geen wikipedia insetsels gevind
@@ -472,25 +457,9 @@ dialog.keys.intro=Jy kan die volgende kortpadsleutels gebruik in plaas van om di
 dialog.keys.keylist=<table><tr><td>Pylkie sleutels</td><td>Skuif kaart links regs, op, af</td></tr><tr><td>Ctrl + links, regs pylkie</td><td>Selekteer vorige of volgende punt</td></tr><tr><td>Ctrl + op, af pylkie</td><td>Vergroot in of uit</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Selekteer vorige, volgende segment</td></tr><tr><td>Ctrl + Home, Einde</td><td>Selekteer eerste, laaste punt</td></tr><tr><td>Del</td><td>Verwyder huidige punt</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=Die volgende stelligns kan gestoor word na 'n konfigurasie le\u00ear
-dialog.saveconfig.prune.trackdirectory=Baan gids
-dialog.saveconfig.prune.photodirectory=foto gids
-dialog.saveconfig.prune.languagecode=Taal kode (AF)
-dialog.saveconfig.prune.languagefile=Taal le\u00ear
-dialog.saveconfig.prune.gpsdevice=GPS apparaat
-dialog.saveconfig.prune.gpsformat=GPS formaat
-dialog.saveconfig.prune.povrayfont=Pocray font
-dialog.saveconfig.prune.gnuplotpath=Pad na gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Pad na gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Pad na exitool
-dialog.saveconfig.prune.mapsource=Geselekteerde kaart bron
-dialog.saveconfig.prune.mapsourcelist=Kaarte bronne
-dialog.saveconfig.prune.diskcache=Kaart stoorarea
-dialog.saveconfig.prune.kmzimagewidth=KMZ beeld groote
-dialog.saveconfig.prune.colourscheme=Kleur skema
-dialog.saveconfig.prune.linewidth=Lyn dikte
-dialog.saveconfig.prune.kmltrackcolour=KML baan kleur
-dialog.saveconfig.prune.autosavesettings=Autostoor stellings
+dialog.paths.prune.gnuplotpath=Pad na gnuplot
+dialog.paths.prune.gpsbabelpath=Pad na gpsbabel
+dialog.paths.prune.exiftoolpath=Pad na exitool
 dialog.setpaths.intro=As jy wil, kan jy die paie kies na die eksterne programme:
 dialog.setpaths.found=Pad gefind?
 dialog.addaltitude.noaltitudes=Die geselekteerde reeks bevat nie hoogtes nie
@@ -593,7 +562,7 @@ confirm.rearrangephotos=Fotos ge-herrangskik
 confirm.splitsegments=%d segment verdelings is gemaak
 confirm.sewsegments=%d segment las is gemaak
 confirm.cutandmove=Seleksie geskuif
-confirm.interpolate=Punte bygevoeg
+confirm.pointsadded=%d punte bygevoeg
 confirm.convertnamestotimes=Baken name aangepas
 confirm.saveexif.ok=%d fotos gestoor
 confirm.undo.single=operasie ongedaan
@@ -612,7 +581,6 @@ confirm.running=Besig om te loop...
 confirm.lookupsrtm=%d hoogte waardes gevind
 confirm.downloadsrtm=%d leers afgelaai na data stoor
 confirm.downloadsrtm.1=%d leer afgelaai na data stoor
-confirm.downloadsrtm.none=Geen leers afgelaai, hulle was alreeds in the data stoor
 confirm.deletefieldvalues=Veld waardes verwyder
 confirm.audioload=Klank leers bygevoeg
 confirm.correlateaudios.single=klankgreep was gekorreleer
@@ -645,7 +613,6 @@ button.selectall=Selekteer almal
 button.selectnone=Selekter geen
 button.preview=Voorskou
 button.load=Laai
-button.upload=Oplaai
 button.guessfields=Raai velde
 button.showwebpage=Wys webblad
 button.check=Kontroleer
@@ -847,8 +814,6 @@ error.language.wrongfile=Die geselekteerde leer lyk nie soos 'n taal leer vir Gp
 error.convertnamestotimes.nonames=Geen name kon in tye omgeskakel word
 error.lookupsrtm.nonefound=Geen hoogte waardes beskikbaar vir die punte
 error.lookupsrtm.nonerequired=Al die punte het klaar hoogtes, so daar is niks meer om te soek
-error.gpsies.uploadnotok=Die gpsies server het die boodskap terug gestuur
-error.gpsies.uploadfailed=Die oplaai het misluk met die volgende fout boodskap
 error.showphoto.failed=Foto het nie gelaai nie
 error.playaudiofailed=Klankgreep het nie gespeel nie
 error.cache.notthere=Die te\u00ebl stoorarea gids kon nie opgespoor word nie
diff --git a/src/tim/prune/lang/prune-texts_cy.properties b/src/tim/prune/lang/prune-texts_cy.properties
new file mode 100644 (file)
index 0000000..12a2b17
--- /dev/null
@@ -0,0 +1,113 @@
+# Text entries for the GpsPrune application
+# Welsh entries
+
+# Menu entries
+menu.file=Ffeil
+menu.file.exit=Gadael
+menu.online=Rhyngrwyd
+menu.track=Wysg
+menu.track.undo=Dadwneud
+menu.point=Pwynt
+menu.photo=Llun
+menu.photo.saveexif=Cadw Exif
+menu.audio=Awdio
+menu.view=Golygu
+menu.view.browser.google=Mapiau Google
+menu.view.browser.yahoo=Mapiau Yahoo
+menu.view.browser.bing=Mapiau Bing
+menu.settings=Dewisiadau
+menu.help=Cymorth
+# Popup menu for map
+menu.map.zoomin=Chwyddo i mewn
+menu.map.zoomout=Chwyddo allan
+menu.map.showmap=Dangoswch y map
+menu.map.showscalebar=Dangoswch y bar graddfa
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.online=R
+altkey.menu.range=N
+altkey.menu.track=T
+altkey.menu.point=P
+altkey.menu.view=G
+altkey.menu.photo=L
+altkey.menu.audio=A
+altkey.menu.settings=D
+altkey.menu.help=C
+
+# Functions
+function.open=Agor ffeil
+function.exportkml=Cadw KML
+function.exportgpx=Cadw GPX
+function.exportpov=Cadw POV
+function.exportimage=Cadw llun
+function.charts=Siartiau
+function.distances=Pellterau
+function.viewfulldetails=Manylion
+function.help=Cymorth
+function.about=Ynghylych GpsPrune
+
+# Dialogs
+dialog.exit.confirm.title=Gadael GpsPrune
+dialog.delimiter.comma=Coma ,
+dialog.delimiter.semicolon=Hannercolon ;
+dialog.gpsload.format=Fformat
+dialog.gpsload.save=Cadw ffeil
+dialog.saveoptions.title=Cadwch y ffeil
+dialog.save.table.field=Maes
+dialog.save.table.save=Cadw
+dialog.exportgpx.name=Enw
+dialog.exportgpx.encoding.system=System
+dialog.exportgpx.encoding.utf8=UTF-8
+dialog.exportpov.camerax=Camera X
+dialog.exportpov.cameray=Camera Y
+dialog.exportpov.cameraz=Camera Z
+dialog.saveexif.title=Cadw Exif
+dialog.saveexif.table.save=Cadw
+dialog.estimatetime.results=Canlyniadau
+dialog.wikipedia.column.distance=Pellter
+dialog.osmpois.column.name=Enw
+dialog.about.version=Fersiwn
+dialog.about.translatedby=Testunau yn Cymraeg ...
+dialog.about.credits.thanks=Diolch a
+dialog.setcolours.text=Testun
+dialog.colourchooser.red=Coch
+dialog.colourchooser.green=Gwyrdd
+dialog.colourchooser.blue=Glas
+dialog.colourer.type.none=Dim
+dialog.setlanguage.language=Iaith
+dialog.diskcache.table.megabytes=Megabeitiau
+dialog.displaysettings.size.small=Bach
+dialog.displaysettings.size.large=Mawr
+dialog.weather.day.today=Heddiw
+dialog.weather.day.tomorrow=Yfory
+
+# Buttons
+button.ok=Iawn
+button.cancel=Diddymu
+button.exit=Gadael
+button.yes=Ie
+button.no=Na
+
+# Display components
+details.track.file=Ffeil
+details.lists.audio=Awdio
+
+# Field names
+fieldname.latitude=Lledred
+fieldname.longitude=Hydred
+fieldname.coordinates=Cyfesurynnau
+fieldname.waypointname=Enw
+fieldname.distance=Pellter
+fieldname.speed=Cyflymder
+fieldname.description=Disgrifiad
+fieldname.comment=Sylw
+
+# How to combine conditions, such as filters
+logic.and=a
+logic.or=neu
+
+# External urls
+url.googlemaps=maps.google.co.uk
+wikipedia.lang=cy
+openweathermap.lang=en
index 6957b1d5ed8a40921354a36cfada74cf1b3b9cb6..39f177e2486a8d6eac80394399c46e2bc8ff1c1b 100644 (file)
@@ -1,5 +1,5 @@
 # Text entries for the GpsPrune application
-# Czech entries thanks to prot_d
+# Czech entries thanks to prot_d, jirislaby
 
 # Menu entries
 menu.file=Soubor
@@ -12,8 +12,6 @@ menu.track=Stopa
 menu.track.undo=Undo
 menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo
 menu.track.markrectangle=Ozna\u010dit body v obd\u00e9ln\u00edku
-function.deletemarked=Smazat ozna\u010den\u00e9 body
-function.rearrangewaypoints=P\u0159euspo\u0159\u00e1dat z\u00e1jmov\u00e9 body
 menu.range=Rozmez\u00ed
 menu.range.all=Vybrat v\u0161e
 menu.range.none=Zru\u0161it v\u00fdb\u011br
@@ -39,7 +37,6 @@ 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
-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
@@ -90,26 +87,26 @@ function.compress=Komprimovat stopu
 function.deleterange=Smazat rozmez\u00ed
 function.croptrack=O\u0159\u00edznout stopu
 function.interpolate=Interpolovat body
+function.deletebydate=Smazat body podle data
 function.addtimeoffset=P\u0159idat \u010dasov\u00fd posun
 function.addaltitudeoffset=P\u0159idat v\u00fd\u0161kov\u00fd posun
+function.rearrangewaypoints=P\u0159euspo\u0159\u00e1dat z\u00e1jmov\u00e9 body
 function.convertnamestotimes=P\u0159ev\u00e9st n\u00e1zvy v\u00fdzna\u010dn\u00fdch bod\u016f na \u010dasy
 function.deletefieldvalues=Smazat hodnoty pole
 function.findwaypoint=Hledat bod
 function.pastecoordinates=Zadat sou\u0159adnice
+function.enterpluscode=Zadat plus k\u00f3d
 function.charts=Grafy
 function.show3d=Trojrozm\u011brn\u011b
 function.distances=Vzd\u00e1lenosti
-function.fullrangedetails=Detaily rozmez\u00ed
 function.estimatetime=Odhad \u010dasu
 function.learnestimationparams=Anal\u00fdza stopy pro odhad \u010dasu
 function.setmapbg=Nastavit pozad\u00ed
 function.setpaths=Nastavit cestu k program\u016fm
+function.selectsegment=Vybrat aktu\u00e1ln\u00ed \u010d\u00e1st
 function.splitsegments=Rozd\u011blit stopu na \u010d\u00e1sti
 function.sewsegments=Spojit \u010d\u00e1sti stopy
-function.getgpsies=St\u00e1hnout stopy z Gpsies
-function.uploadgpsies=Nahr\u00e1t stopu na Gpsies
 function.lookupsrtm=Na\u010d\u00edst nadm. v\u00fd\u0161ku ze SRTM
-function.downloadsrtm=St\u00e1hnout dla\u017edice ze SRTM
 function.getwikipedia=Hledat na Wikipedii podle vzd\u00e1lenosti
 function.searchwikipedianames=Hledat na Wikipedii podle jm\u00e9na
 function.searchopencachingde=Hledat na OpenCaching.de
@@ -140,6 +137,7 @@ function.saveconfig=Ulo\u017eit nastaven\u00ed
 function.diskcache=Ulo\u017eit mapy na disk
 function.managetilecache=Upravit cache map
 function.getweatherforecast=St\u00e1hnout p\u0159edpov\u011b\u010f po\u010das\u00ed
+function.setaltitudetolerance=Nastavit toleranci v\u00fd\u0161ky
 
 # Dialogs
 dialog.exit.confirm.title=Ukon\u010dit GpsPrune
@@ -359,23 +357,10 @@ dialog.gpsies.column.length=D\u00e9lka
 dialog.gpsies.description=Popis
 dialog.gpsies.nodescription=Bez popisu
 dialog.gpsies.nonefound=Nenalezeny \u017e\u00e1dn\u00e9 stopy
-dialog.gpsies.username=U\u017eiv. jm\u00e9no k gpsies
-dialog.gpsies.password=Heslo k gpsies
-dialog.gpsies.keepprivate=Stopu nezve\u0159ej\u0148ovat
-dialog.gpsies.confirmopenpage=Otev\u0159\u00edt nahranou stopu v internetov\u00e9m prohl\u00ed\u017ee\u010di?
-dialog.gpsies.activities=Aktivita
-dialog.gpsies.activity.trekking=Turistika
-dialog.gpsies.activity.walking=Ch\u016fze
-dialog.gpsies.activity.jogging=B\u011bh
-dialog.gpsies.activity.biking=Kolo
-dialog.gpsies.activity.motorbiking=Motorka
-dialog.gpsies.activity.snowshoe=Sn\u011b\u017enice
-dialog.gpsies.activity.sailing=Lo\u010f
-dialog.gpsies.activity.skating=Bruslen\u00ed
 dialog.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.nonefound=Nebyly nalezeny \u017e\u00e1dn\u00e9 z\u00e1znamy na wikipedii
 dialog.wikipedia.gallery=Galerie
 dialog.osmpois.column.name=N\u00e1zev
 dialog.osmpois.column.type=Typ
@@ -413,6 +398,7 @@ dialog.correlate.timestamp.end=Konec
 dialog.correlate.audioselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fdch nahr\u00e1vek pro ur\u010den\u00ed \u010dasov\u00e9ho posunu
 dialog.correlate.select.audioname=N\u00e1zev audionahr\u00e1vky
 dialog.correlate.select.audiolater=Audio pozd\u011bj\u0161\u00ed
+dialog.rearrangewaypoints.desc=Zvolte c\u00edl a po\u0159ad\u00ed \u0159azen\u00ed bod\u016f
 dialog.rearrangephotos.desc=Vyberte um\u00edst\u011bn\u00ed a uspo\u0159\u00e1d\u00e1n\u00ed bod\u016f fotografi\u00ed
 dialog.rearrange.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek
 dialog.rearrange.toend=P\u0159en\u00e9st na konec
@@ -476,25 +462,9 @@ dialog.keys.intro=M\u00edsto my\u0161i m\u016f\u017eete pou\u017e\u00edvat n\u00
 dialog.keys.keylist=<table><tr><td>\u0160ipky</td><td>Posunout mapu vlevo, vpravo, nahoru, dol\u016f</td></tr><tr><td>Ctrl + \u0161ipka vlevo, vpravo</td><td>Vybrat p\u0159edchoz\u00ed nebo n\u00e1sleduj\u00edc\u00ed bod</td></tr><tr><td>Ctrl + \u0161ipka nahoru, dol\u016f</td><td>P\u0159ibl\u00ed\u017eit, odd\u00e1lit</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Vybrat p\u0159edchoz\u00ed, n\u00e1sleduj\u00edc\u00ed \u010d\u00e1st stopy</td></tr><tr><td>Ctrl + Home, End</td><td>Vybrat prvn\u00ed, posledn\u00ed bod</td></tr><tr><td>Del</td><td>Smazat aktu\u00e1ln\u00ed bod</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=N\u00e1sleduj\u00edc\u00ed volby mohou b\u00fdt ulo\u017eeny do konfigura\u010dn\u00edho souboru :
-dialog.saveconfig.prune.trackdirectory=Adres\u00e1\u0159 s stopami
-dialog.saveconfig.prune.photodirectory=Ad\u0159es\u00e1\u0159 s fotografiemi
-dialog.saveconfig.prune.languagecode=K\u00f3d jazyka
-dialog.saveconfig.prune.languagefile=Soubor jazyka
-dialog.saveconfig.prune.gpsdevice=Za\u0159\u00edzen\u00ed GPS
-dialog.saveconfig.prune.gpsformat=Form\u00e1t GPS
-dialog.saveconfig.prune.povrayfont=Font Povray
-dialog.saveconfig.prune.gnuplotpath=Cesta k gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Cesta k gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Cesta k exiftool
-dialog.saveconfig.prune.mapsource=Vybran\u00fd zdroj map
-dialog.saveconfig.prune.mapsourcelist=Zdroje map
-dialog.saveconfig.prune.diskcache=Cache s mapami
-dialog.saveconfig.prune.kmzimagewidth=\u0160\u00ed\u0159ka bitmapy KMZ
-dialog.saveconfig.prune.colourscheme=Barevn\u00e9 sch\u00e9ma
-dialog.saveconfig.prune.linewidth=Tlou\u0161\u0165ka \u010d\u00e1ry
-dialog.saveconfig.prune.kmltrackcolour=Barva stopy v KML
-dialog.saveconfig.prune.autosavesettings=Mo\u017enosti ukl\u00e1d\u00e1n\u00ed
+dialog.paths.prune.gnuplotpath=Cesta k gnuplot
+dialog.paths.prune.gpsbabelpath=Cesta k gpsbabel
+dialog.paths.prune.exiftoolpath=Cesta k exiftool
 dialog.setpaths.intro=Je-li to t\u0159eba, m\u016f\u017eete nastavit cesty k extern\u00edm aplikac\u00edm:
 dialog.setpaths.found=Cesta nalezena?
 dialog.addaltitude.noaltitudes=Vybran\u00e9 rozmez\u00ed neobsahuje nadmo\u0159skou v\u00fd\u0161ku
@@ -538,6 +508,7 @@ 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.displaysettings.linewidth=Zvolte tlou\u0161\u0165ku \u010d\u00e1ry, kterou se nakresl\u00ed stopa (1-4)
+dialog.displaysettings.antialias=Pou\u017e\u00edt antialiasing
 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
@@ -585,7 +556,7 @@ confirm.rearrangephotos=Fotografie p\u0159euspo\u0159\u00e1d\u00e1ny
 confirm.splitsegments=\u00dasp\u011b\u0161n\u011b rozd\u011bleno v %d bodech
 confirm.sewsegments=\u00dasp\u011b\u0161n\u011b spojeno v %d bodech
 confirm.cutandmove=V\u00fdb\u011br p\u0159esunut
-confirm.interpolate=Body p\u0159id\u00e1ny
+confirm.pointsadded=%d body p\u0159id\u00e1ny
 confirm.convertnamestotimes=N\u00e1zvy bod\u016f p\u0159evedeny
 confirm.saveexif.ok=Ulo\u017eeno %d fotografi\u00ed
 confirm.undo.single=operace vr\u00e1cena
@@ -604,7 +575,6 @@ confirm.running=Prob\u00edh\u00e1 ...
 confirm.lookupsrtm=Nalezeno %d v\u00fd\u0161kov\u00fdch hodnot
 confirm.downloadsrtm=Do cache bylo sta\u017eeno %d soubor\u016f
 confirm.downloadsrtm.1=Do cache bylo sta\u017eeno %d soubor\u016f
-confirm.downloadsrtm.none=\u017d\u00e1dn\u00fd soubor nebylo t\u0159eba stahovat, v\u0161e u\u017e je v cachi.
 confirm.deletefieldvalues=Hodnoty pole smaz\u00e1ny
 confirm.audioload=Audionahr\u00e1vky p\u0159id\u00e1ny
 confirm.correlateaudios.single=Audionahr\u00e1vka slad\u011bna
@@ -614,7 +584,7 @@ confirm.correlateaudios.multi=Audionahr\u00e1vky slad\u011bny
 tip.title=Tip
 tip.useamapcache=Kdy\u017e nastav\u00edte odkl\u00e1dac\u00ed prostor na disku \u010dili cache (Nastaven\u00ed -> Ulo\u017eit mapy na disk),\nzrychl\u00ed se zobrazov\u00e1n\u00ed a zmen\u0161\u00ed se mno\u017estv\u00ed p\u0159en\u00e1\u0161en\u00fdch dat.
 tip.learntimeparams=V\u00fdsledky budou p\u0159esn\u011bj\u0161\u00ed, kdy\u017e na na\u010dten\u00e9 stopy pou\u017eijete funkci\nAnal\u00fdza stopy pro odhad \u010dasu.
-tip.downloadsrtm=Na\u010d\u00edt\u00e1n\u00ed nadmo\u0159sk\u00fdch v\u00fd\u0161ek bude rychlej\u0161\u00ed, pokud st\u00e1hnete dla\u017edice do cache pomoc\u00ed\nOnline -> St\u00e1hnout dla\u017edice ze SRTM.
+tip.downloadsrtm=Na\u010d\u00edt\u00e1n\u00ed nadmo\u0159sk\u00fdch v\u00fd\u0161ek bude rychlej\u0161\u00ed,\npokud st\u00e1hnete dla\u017edice do cache.
 tip.usesrtmfor3d=Tato stopa neobsahuje informaci o nadmo\u0159sk\u00e9 v\u00fd\u0161ce.\nPro na\u010dten\u00ed p\u0159ibli\u017en\u00fdch nadmo\u0159sk\u00fdch v\u00fd\u0161ek pot\u0159ebn\u00fdch\nna trojrozm\u011brn\u00e9 zobrazen\u00ed m\u016f\u017eete pou\u017e\u00edt funkce SRTM.
 tip.manuallycorrelateone=Kdy\u017e ru\u010dn\u011b slad\u00edte aspo\u0148 jednu fotografii, \u010dasov\u00fd posun bude vypo\u010d\u00edtat za v\u00e1s.
 
@@ -641,7 +611,6 @@ button.selectall=Vybrat v\u0161e
 button.selectnone=Zru\u0161it v\u00fdb\u011br
 button.preview=N\u00e1hled
 button.load=Na\u010d\u00edst
-button.upload=Upload
 button.guessfields=Odhadnout pole
 button.showwebpage=Zobrazit str\u00e1nku
 button.check=Zkontrolovat
@@ -767,7 +736,7 @@ units.degreesfahrenheit.short=\u00b0F
 logic.and=a
 logic.or=nebo
 
-# External urls
+# External urls and services
 url.googlemaps=maps.google.cz
 wikipedia.lang=cs
 openweathermap.lang=en
@@ -846,8 +815,6 @@ error.language.wrongfile=Vybran\u00fd soubor nevypad\u00e1 jako jazykov\u00fd so
 error.convertnamestotimes.nonames=N\u00e1zvy nemohou b\u00fdt p\u0159evedeny na \u010dasov\u00e9 zna\u010dky
 error.lookupsrtm.nonefound=Pro tyto body nen\u00ed k dispozici informace o nadmo\u0159sk\u00e9 v\u00fd\u0161ce
 error.lookupsrtm.nonerequired=U v\u0161ech bod\u016f u\u017e je informaci o v\u00fd\u0161ce, tak\u017ee nen\u00ed co dohled\u00e1vat
-error.gpsies.uploadnotok=Server gpsies vr\u00e1til hl\u00e1\u0161en\u00ed
-error.gpsies.uploadfailed=Chyba - nepoda\u0159ilo se nahr\u00e1t data.
 error.showphoto.failed=Nepoda\u0159ilo se na\u010d\u00edst fotografii
 error.playaudiofailed=Nepoda\u0159ilo se p\u0159ehr\u00e1t zvukov\u00fd soubor.
 error.cache.notthere=Nepoda\u0159ilo se nal\u00e9zt adres\u00e1\u0159 s cache map.
index e975e29317d5d167a07ecac6b3022df95a89df6c..2b8e26cd9a503551d824fa1e041ad9d737bc3f38 100644 (file)
@@ -77,7 +77,6 @@ function.pastecoordinates=Indf\u00f8j nye koordinater
 function.charts=Kort
 function.show3d=3-D view
 function.distances=Afstande
-function.fullrangedetails=Vis alle detaljer
+function.viewfulldetails=Vis alle detaljer
 function.setmapbg=V\u00e6lg kort som baggrund
 function.setpaths=V\u00e6lg sti til programmer
-function.getgpsies=Se liste af GPS-spor
index 1c35d2557ced81caf09564c570a4b5436aa1818d..10642117cf4752013fc6fdd7b864a3a9f472eec4 100644 (file)
@@ -96,11 +96,13 @@ function.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
 function.convertnamestotimes=Namen der Wegpunkte in Zeitstempel umwandeln
 function.deletefieldvalues=Werte eines Feldes l\u00f6schen
 function.findwaypoint=Wegpunkt finden
-function.pastecoordinates=Neuen Wegpunkt anlegen
+function.pastecoordinates=Koordinaten eingeben
+function.pastecoordinatelist=Koordinatenliste eingeben
+function.enterpluscode=Pluscode eingeben
 function.charts=Diagramme
 function.show3d=3D Ansicht
 function.distances=Entfernungen
-function.fullrangedetails=Zus\u00e4tzliche Bereichdetails
+function.viewfulldetails=Zus\u00e4tzliche Details
 function.estimatetime=Zeit absch\u00e4tzen
 function.learnestimationparams=Zeitparameter erlernen
 function.autoplay=Track abspielen
@@ -110,10 +112,7 @@ 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
@@ -121,6 +120,7 @@ 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.projectpoint=Punkt projizieren
 function.setcolours=Farben einstellen
 function.setdisplaysettings=Darstellungsoptionen
 function.setlanguage=Sprache einstellen
@@ -367,19 +367,6 @@ dialog.gpsies.column.length=L\u00e4nge
 dialog.gpsies.description=Beschreibung
 dialog.gpsies.nodescription=Keine Beschreibung
 dialog.gpsies.nonefound=Keine Tracks gefunden
-dialog.gpsies.username=Username bei GPSies.com
-dialog.gpsies.password=Passwort bei GPSies.com
-dialog.gpsies.keepprivate=Track privat halten
-dialog.gpsies.confirmopenpage=Webseite f\u00fcr den hochgeladenen Track \u00f6ffnen?
-dialog.gpsies.activities=Aktivit\u00e4ten
-dialog.gpsies.activity.trekking=Wandern
-dialog.gpsies.activity.walking=Walking
-dialog.gpsies.activity.jogging=Laufen
-dialog.gpsies.activity.biking=Fahrradfahren
-dialog.gpsies.activity.motorbiking=Motorradfahren
-dialog.gpsies.activity.snowshoe=Schneeschuhwandern
-dialog.gpsies.activity.sailing=Segeln
-dialog.gpsies.activity.skating=Inline-Skating
 dialog.mapillary.nonefound=Keine Fotos gefunden
 dialog.wikipedia.column.name=Artikelname
 dialog.wikipedia.column.distance=Entfernung
@@ -447,6 +434,10 @@ 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.pastecoordinatelist.desc=Liste von Koordinaten eingeben mit einem Punkt pro Zeile
+dialog.pluscode.desc=Pluscode hier eingeben
+dialog.pluscode.code=Pluscode
+dialog.pluscode.nothingfound=Bitte pr\u00fcfen Sie den Code und versuchen Sie es nochmals
 dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n https://gpsprune.activityworkshop.net/
 dialog.about.version=Version
 dialog.about.build=Build
@@ -486,25 +477,9 @@ dialog.keys.intro=Anstelle der Maus k\u00f6nnen Sie folgende Tastenkombinationen
 dialog.keys.keylist=<table><tr><td>Pfeil Tasten</td><td>Karte verschieben</td></tr><tr><td>Strg + Links-, Rechts-Pfeil</td><td>Vorherigen oder n\u00e4chsten Punkt markieren</td></tr><tr><td>Strg + Auf-, Abw\u00e4rts-Pfeil</td><td>Ein- oder Auszoomen</td></tr><tr><td>Strg + Bild auf, ab</td><td>Zum vorherigen oder n\u00e4chsten Abschnitt</td></tr><tr><td>Strg + Pos1, Ende</td><td>Ersten oder letzten Punkt markieren</td></tr><tr><td>Entf</td><td>Aktuellen Punkt entfernen</td></tr></table>
 dialog.keys.normalmodifier=Strg
 dialog.keys.macmodifier=Kommando
-dialog.saveconfig.desc=Die folgende Einstellungen k\u00f6nnen gespeichert werden:
-dialog.saveconfig.prune.trackdirectory=Datenverzeichnis
-dialog.saveconfig.prune.photodirectory=Fotoverzeichnis
-dialog.saveconfig.prune.languagecode=Sprachcode (DE)
-dialog.saveconfig.prune.languagefile=Sprachdatei
-dialog.saveconfig.prune.gpsdevice=GPS-Ger\u00e4tename
-dialog.saveconfig.prune.gpsformat=GPS-Format
-dialog.saveconfig.prune.povrayfont=Povray-Font
-dialog.saveconfig.prune.gnuplotpath=Gnuplot-Pfad
-dialog.saveconfig.prune.gpsbabelpath=GPSBabel-Pfad
-dialog.saveconfig.prune.exiftoolpath=ExifTool-Pfad
-dialog.saveconfig.prune.mapsource=Kartenserver-Index
-dialog.saveconfig.prune.mapsourcelist=Kartenserver
-dialog.saveconfig.prune.diskcache=Kartenordner
-dialog.saveconfig.prune.kmzimagewidth=Bildgr\u00f6\u00dfe in KMZ
-dialog.saveconfig.prune.colourscheme=Farbschema
-dialog.saveconfig.prune.linewidth=Liniedicke
-dialog.saveconfig.prune.kmltrackcolour=KML-Trackfarbe
-dialog.saveconfig.prune.autosavesettings=Einstellungen speichern
+dialog.paths.prune.gnuplotpath=Gnuplot-Pfad
+dialog.paths.prune.gpsbabelpath=GPSBabel-Pfad
+dialog.paths.prune.exiftoolpath=ExifTool-Pfad
 dialog.setpaths.intro=Sie k\u00f6nnen hier die Pfade f\u00fcr externe Programme setzen:
 dialog.setpaths.found=Pfad gefunden?
 dialog.addaltitude.noaltitudes=Der markierte Bereich enth\u00e4lt keine H\u00f6henangaben
@@ -571,6 +546,9 @@ dialog.displaysettings.wpicon.pin=Sto\u00dfnadel
 dialog.displaysettings.size.small=Klein
 dialog.displaysettings.size.medium=Mittel
 dialog.displaysettings.size.large=Gro\u00df
+dialog.displaysettings.windowstyle=Fensterstil (Neustart n\u00f6tig)
+dialog.displaysettings.windowstyle.default=Standart
+dialog.displaysettings.windowstyle.nimbus=Nimbus
 dialog.downloadosm.desc=Die OpenStreetMap-Daten f\u00fcr das folgende Gebiet werden heruntergeladen (.osm-Datei):
 dialog.searchwikipedianames.search=Suche nach:
 dialog.weather.location=Ort
@@ -613,6 +591,12 @@ dialog.autoplay.usetimestamps=Zeitstempeln verwenden
 dialog.autoplay.rewind=Zum Anfang
 dialog.autoplay.pause=Pause
 dialog.autoplay.play=Abspielen
+dialog.markers.halves=Punkte auf halben Weg
+dialog.markers.half.distance=Halbe Distanz
+dialog.markers.half.climb=Halber Aufstieg
+dialog.markers.half.descent=Halber Abstieg
+dialog.projectpoint.desc=Geben Sie die Azimut und Distanz f\u00fcr die Projizierung
+dialog.projectpoint.bearing=Azimut (Grad von N)
 
 # 3d window
 dialog.3d.title=GpsPrune-3D-Ansicht
@@ -634,7 +618,7 @@ confirm.rearrangephotos=Fotos neu angeordnet
 confirm.splitsegments=Es wurden %d Schnitte gemacht
 confirm.sewsegments=Es wurden %d Verbindungen gemacht
 confirm.cutandmove=Bereich verschoben
-confirm.interpolate=Punkte eingef\u00fcgt
+confirm.pointsadded=%d Punkte eingef\u00fcgt
 confirm.convertnamestotimes=Wegpunktnamen umgewandelt
 confirm.saveexif.ok=Es wurden %d Fotodateien geschrieben
 confirm.undo.single=Operation r\u00fcckg\u00e4ngig gemacht
@@ -653,7 +637,6 @@ confirm.running=In Bearbeitung ...
 confirm.lookupsrtm=Es wurden %d H\u00f6henwerte gefunden
 confirm.downloadsrtm=Es wurden %d Dateien heruntergeladen
 confirm.downloadsrtm.1=Es wurde %d Datei heruntergeladen
-confirm.downloadsrtm.none=Keine Dateien heruntergeladen, alle waren schon gespeichert.
 confirm.deletefieldvalues=Feldwerte gel\u00f6scht
 confirm.audioload=Audiodateien geladen
 confirm.correlateaudios.single=Audio wurde korreliert
@@ -663,7 +646,7 @@ confirm.correlateaudios.multi=Audios wurden korreliert
 tip.title=Tipp
 tip.useamapcache=Mit lokal-gespeicherten Kartenkacheln (Einstellungen -> Karten auf Festplatte speichern)\nk\u00f6nnen Sie die Darstellung beschleunigen und Netzwerkverkehr reduzieren.
 tip.learntimeparams=Wenn Sie Track -> Zeitparameter erlernen mit Ihren Tracks benutzen\ndann werden die berechneten Werte genauer.
-tip.downloadsrtm=Sie k\u00f6nnen diese Funktion beschleunigen indem Sie\nOnline -> SRTM Dateien herunterladen aufrufen\num die Daten lokal zu speichern.
+tip.downloadsrtm=Sie k\u00f6nnen diese Funktion beschleunigen indem Sie\ndie SRTM Daten lokal im Kartenspeicher speichern.
 tip.usesrtmfor3d=Dieser Track hat keine H\u00f6heninformation.\nSie k\u00f6nnen die SRTM Funktionen verwenden, um\nH\u00f6henwerte abzusch\u00e4tzen.
 tip.manuallycorrelateone=Mit mindestens einem manuell verbundenen Element kann die Zeitdifferenz automatisch berechnet werden.
 
@@ -690,7 +673,6 @@ button.selectall=Alle ausw\u00e4hlen
 button.selectnone=Nichts ausw\u00e4hlen
 button.preview=Vorschau
 button.load=Laden
-button.upload=Hochladen
 button.guessfields=Felder erraten
 button.showwebpage=Webseite anzeigen
 button.check=Pr\u00fcfen
@@ -764,6 +746,7 @@ map.overzoom=Keine Karten f\u00fcr diesen Zoomfaktor verf\u00fcgbar
 # Field names
 fieldname.latitude=Breitengrad
 fieldname.longitude=L\u00e4ngengrad
+fieldname.coordinates=Koordinaten
 fieldname.altitude=H\u00f6he
 fieldname.timestamp=Zeitstempel
 fieldname.time=Zeit
@@ -778,6 +761,7 @@ fieldname.duration=Zeitdauer
 fieldname.speed=Geschwindigkeit
 fieldname.verticalspeed=Vertikale Geschwindigkeit
 fieldname.description=Beschreibung
+fieldname.comment=Anmerkung
 fieldname.mediafilename=Foto- / Audioname
 
 # Measurement units
@@ -871,6 +855,7 @@ error.load.nopoints=Keine g\u00fcltigen Daten in Datei gefunden
 error.load.unknownxml=Unbekanntes XML-Format:
 error.load.noxmlinzip=Keine XML-Datei in Zip-Datei gefunden
 error.load.othererror=Fehler beim Lesen der Datei:
+error.load.nopointsintext=Keine g\u00fcltigen Daten gefunden
 error.jpegload.dialogtitle=Fehler beim Laden von Fotos
 error.jpegload.nofilesfound=Keine Dateien gefunden
 error.jpegload.nojpegsfound=Keine JPG-Dateien gefunden
@@ -891,8 +876,6 @@ error.language.wrongfile=Die ausgew\u00e4hlte Datei scheint keine Sprachdatei f\
 error.convertnamestotimes.nonames=Es konnten keine Namen umgewandelt werden
 error.lookupsrtm.nonefound=Keine H\u00f6hendaten verf\u00fcgbar f\u00fcr diese Punkte
 error.lookupsrtm.nonerequired=Alle Punkte haben schon H\u00f6hendaten
-error.gpsies.uploadnotok=Der Gpsies Server hat geantwortet
-error.gpsies.uploadfailed=Das Hochladen ist fehlgeschlagen
 error.showphoto.failed=Das Foto konnte nicht geladen werden
 error.playaudiofailed=Das Abspielen der Audiodatei ist fehlgeschlagen
 error.cache.notthere=Der Ordner wurde nicht gefunden
index acd393a781eab96cbdd8f34c87926201cba98ccb..76771e016b18c2c48766c02b8a250cbfbf85b08f 100644 (file)
@@ -95,11 +95,13 @@ function.addaltitudeoffset=H\u00f6chiverschiebig zutue
 function.findwaypoint=Waypoint suech\u00e4
 function.convertnamestotimes=Waypointname ins Ziitst\u00e4mple verwondle
 function.deletefieldvalues=Werte von nem F\u00e4ld l\u00f6sche
-function.pastecoordinates=Noii Koordinaten iigebe
+function.pastecoordinates=Noii Koordinate iigeh
+function.pastecoordinatelist=Liste vo Koordinatene iigeh
+function.enterpluscode=Pluscode iigeh
 function.charts=Diagramme
 function.show3d=Dr\u00fc\u00fc-D Aasicht
 function.distances=Entf\u00e4rnige
-function.fullrangedetails=Zues\u00e4tzlichi Beriichinfos
+function.viewfulldetails=No meh Infos
 function.estimatetime=Ziit absch\u00e4tze
 function.learnestimationparams=Ziitparameter erlerne
 function.autoplay=Track abschpiel\u00e4
@@ -108,10 +110,7 @@ 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
@@ -119,6 +118,7 @@ function.searchopencachingde=OpenCaching.de durasueche
 function.mapillary=Mapillary na F\u00f6telis durasueche
 function.downloadosm=OSM-Date f\u00fcr dere Gebiet abalad\u00e4
 function.duplicatepoint=Punkt verdoppl\u00e4
+function.projectpoint=Punkt projizier\u00e4
 function.correlatephotos=F\u00f6telis korrelier\u00e4
 function.rearrangephotos=F\u00f6telis reorganisier\u00e4
 function.rotatephotoleft=F\u00f6teli nach Links dr\u00e4y\u00e4
@@ -363,19 +363,6 @@ dialog.gpsies.column.length=L\u00e4nge
 dialog.gpsies.description=Beschriebig
 dialog.gpsies.nodescription=Kei Beschriebig
 dialog.gpsies.nonefound=Kei Tracks gfunde
-dialog.gpsies.username=Gpsies Username
-dialog.gpsies.password=Gpsies Passwort
-dialog.gpsies.keepprivate=Track privat halte
-dialog.gpsies.confirmopenpage=Websiite f\u00fcrn uufagladenen Track \u00f6ffne?
-dialog.gpsies.activities=Aktivit\u00e4ten
-dialog.gpsies.activity.trekking=Wandere
-dialog.gpsies.activity.walking=Z'Fuess gah
-dialog.gpsies.activity.jogging=Seggle
-dialog.gpsies.activity.biking=Velotour
-dialog.gpsies.activity.motorbiking=Motorrad
-dialog.gpsies.activity.snowshoe=Schneeschuh
-dialog.gpsies.activity.sailing=Segle
-dialog.gpsies.activity.skating=Inline-Skate
 dialog.mapillary.nonefound=Kei F\u00f6telis gfunde
 dialog.wikipedia.column.name=Artikelname
 dialog.wikipedia.column.distance=Entf\u00e4rnig
@@ -443,6 +430,10 @@ 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.pastecoordinatelist.desc=G\u00e4bet Sie hier die Koordinaten inn\u00e4, j\u00e4de Ziile git n Punkt
+dialog.pluscode.desc=G\u00e4bet Sie hier den Code ii
+dialog.pluscode.code=Pluscode
+dialog.pluscode.nothingfound=Tut mer leid, n\u00fc\u00fct gfunde
 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
@@ -482,25 +473,9 @@ dialog.keys.intro=Aastatt d'Muus k\u00f6nnet Sie diese Tastekombinationen nutze
 dialog.keys.keylist=<table><tr><td>Pfiil Taste</td><td>Karte verschiebe</td></tr><tr><td>Strg + links, r\u00e4chts Pfiil</td><td>Vorherigi oder n\u00f6chsti Punkt markiere</td></tr><tr><td>Strg + uuf, aba Pfiil</td><td>Ii- oder Uusezoome</td></tr><tr><td>Strg + Bild uuf, ab</td><td>Vorherigi oder n\u00f6chsti Segm\u00e4nt markiere</td></tr><tr><td>Strg + Pos1, \u00c4nde</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt l\u00f6sche</td></tr></table>
 dialog.keys.normalmodifier=Strg
 dialog.keys.macmodifier=Kommando
-dialog.saveconfig.desc=Die folgendi Iistellige k\u00f6nne gspeicheret werde :
-dialog.saveconfig.prune.trackdirectory=Trackverzeichnis
-dialog.saveconfig.prune.photodirectory=F\u00f6teliverzeichnis
-dialog.saveconfig.prune.languagecode=Sprochecode (DE_ch)
-dialog.saveconfig.prune.languagefile=Sprochedatei
-dialog.saveconfig.prune.gpsdevice=GPS Ger\u00e4tename
-dialog.saveconfig.prune.gpsformat=GPS Format
-dialog.saveconfig.prune.povrayfont=Povray Font
-dialog.saveconfig.prune.gnuplotpath=Gnuplot Pfad
-dialog.saveconfig.prune.gpsbabelpath=Gpsbabel Pfad
-dialog.saveconfig.prune.exiftoolpath=Exiftool Pfad
-dialog.saveconfig.prune.mapsource=Kartenserver Index
-dialog.saveconfig.prune.mapsourcelist=Kartenservers
-dialog.saveconfig.prune.diskcache=Kartenordner
-dialog.saveconfig.prune.kmzimagewidth=Bildr\u00f6sse im KMZ
-dialog.saveconfig.prune.colourscheme=Farbeschema
-dialog.saveconfig.prune.linewidth=Liniedicke
-dialog.saveconfig.prune.kmltrackcolour=KML Trackfarb
-dialog.saveconfig.prune.autosavesettings=Iistellige speichere
+dialog.paths.prune.gnuplotpath=Gnuplot Pfad
+dialog.paths.prune.gpsbabelpath=Gpsbabel Pfad
+dialog.paths.prune.exiftoolpath=Exiftool Pfad
 dialog.setpaths.intro=Sie k\u00f6nnet dann die Pfade f\u00fcr dia Applikatione setz\u00e4:
 dialog.setpaths.found=Pfad gfunde?
 dialog.addaltitude.noaltitudes=Dr sel\u00e4ktierte Beriich h\u00e4t keini H\u00f6chiinformation
@@ -566,6 +541,9 @@ dialog.displaysettings.wpicon.pin=Stossnadeli
 dialog.displaysettings.size.small=Chli
 dialog.displaysettings.size.medium=Mittel
 dialog.displaysettings.size.large=Gross
+dialog.displaysettings.windowstyle=F\u00e4nsterstil (Neustart n\u00f6tig)
+dialog.displaysettings.windowstyle.default=Standart
+dialog.displaysettings.windowstyle.nimbus=Nimbus
 dialog.downloadosm.desc=Best\u00e4tige um rohi OSM Date f\u00fcrn Gebiet aba zlade:
 dialog.searchwikipedianames.search=Sueche na:
 dialog.weather.location=Ort
@@ -608,6 +586,12 @@ dialog.autoplay.usetimestamps=Ziitst\u00e4mple verw\u00e4nde
 dialog.autoplay.rewind=Zum Aafang
 dialog.autoplay.pause=Pause
 dialog.autoplay.play=Abschpiele
+dialog.markers.halves=P\u00fcnkt ufem halben Weg
+dialog.markers.half.distance=Halbe Distanz
+dialog.markers.half.climb=Halber Uufstieg
+dialog.markers.half.descent=Halber Abstieg
+dialog.projectpoint.desc=Richtig und Distanz iigeh, mit dene s zu projizieren isch
+dialog.projectpoint.bearing=Azimut (Grad vo N)
 
 # 3d window
 dialog.3d.title=GpsPrune Dr\u00fc\u00fc-d Aasicht
@@ -629,7 +613,7 @@ confirm.rearrangephotos=Fotos umorganisiert
 confirm.splitsegments=Es sin %d Schnitte gmacht worde
 confirm.sewsegments=Es sin %d Verbindige gemacht worde
 confirm.cutandmove=Beriich gmoved
-confirm.interpolate=P\u00fcnkte iigf\u00fcgt worde
+confirm.pointsadded=%d P\u00fcnkte iigf\u00fcgt worde
 confirm.convertnamestotimes=Waypointname verwondlet
 confirm.saveexif.ok=Es sin %d F\u00f6telis gschriebe worde
 confirm.undo.single=Operation r\u00fcckg\u00e4ngig gmacht worde.
@@ -648,7 +632,6 @@ confirm.running=Am Laufe ...
 confirm.lookupsrtm=Es sin %d H\u00f6henwerte gfunde
 confirm.downloadsrtm=Es sin %d Files abeglade
 confirm.downloadsrtm.1=Sisch %d File abeglade
-confirm.downloadsrtm.none=Kei Files abeglade, die sin alli scho da gsi.
 confirm.deletefieldvalues=Feldw\u00e4rte gl\u00f6scht worde
 confirm.audioload=Audiofiles glade worde
 confirm.media.removed=entf\u00e4rnt
@@ -659,7 +642,7 @@ confirm.correlateaudios.multi=Audiofiles sin korreliert worde
 tip.title=Tipp
 tip.useamapcache=Mit lokali Kartekachle (Iistellige -> Karten uufem Disk speichere)\nk\u00f6nnet Sie d Darstellig bschleunige und Netzwerkverkehr reduziere.
 tip.learntimeparams=Wenn Sie Track -> Ziitparameter erlerne mit Ihren Tracks benutze\ndann werdet d ber\u00e4chneti Werte gnauer.
-tip.downloadsrtm=Sie k\u00f6nnet d Funktion beschleunige indem Sie\nOnline -> SRTM Files abalade uufrufe\num d Date lokal z'speichere.
+tip.downloadsrtm=Sie k\u00f6nnet d Funktion beschluunige\nindem Sie Kartekachle lokal speichere\nund d H\u00f6chidate au d\u00f6t speichere.
 tip.usesrtmfor3d=Dere Track h\u00e4t kei H\u00f6chiinformation.\nSie k\u00f6nnet d SRTM Funktione verw\u00e4nde, um\nH\u00f6chiwerte abz'sch\u00e4tze.
 tip.manuallycorrelateone=Mit mindeschtens einem verbundenen Elem\u00e4nt chann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
 
@@ -686,7 +669,6 @@ button.selectall=Alli uusw\u00e4hle
 button.selectnone=N\u00fc\u00fct uusw\u00e4hle
 button.preview=Vorschau\u00e4
 button.load=Lad\u00e4
-button.upload=Uufalad\u00e4
 button.guessfields=F\u00e4lde errat\u00e4
 button.showwebpage=Websiite aazeig\u00e4
 button.check=Pr\u00fcefa
@@ -759,6 +741,7 @@ map.overzoom=Kei Karte mit diesem Zoom
 # Field names
 fieldname.latitude=Breitegrad
 fieldname.longitude=L\u00e4ngegrad
+fieldname.coordinates=Koordinate
 fieldname.altitude=H\u00f6chi
 fieldname.timestamp=Ziitst\u00e4mpel
 fieldname.time=Ziit
@@ -773,6 +756,7 @@ fieldname.duration=Ziitl\u00e4ngi
 fieldname.speed=Gschwindikeit
 fieldname.verticalspeed=Uf/Ab Gschwindikeit
 fieldname.description=Bschriibig
+fieldname.comment=Aamerkig
 fieldname.mediafilename=F\u00f6teli- / Audioname
 
 # Measurement units
@@ -866,6 +850,7 @@ error.load.nopoints=Kei g\u00fcltigi Information inem File gfunde
 error.load.unknownxml=Unbekanntes xml Format:
 error.load.noxmlinzip=Kei xml im Zip File gfunde
 error.load.othererror=F\u00e4hle bim L\u00e4se:
+error.load.nopointsintext=Kei g\u00fcltigi Koordinate gfunde
 error.jpegload.dialogtitle=F\u00e4hle bim Lade von F\u00f6telis
 error.jpegload.nofilesfound=Kei Files gfunde
 error.jpegload.nojpegsfound=Kei Jpegs gfunde
@@ -886,8 +871,6 @@ error.language.wrongfile=Die uusgew\u00e4hlti Datei scheint kei Sproch-Datei f\u
 error.convertnamestotimes.nonames=Kei Namen han k\u00f6nnet verwondlet werde
 error.lookupsrtm.nonefound=Kei H\u00f6hendate verf\u00fcegbar f\u00fcr d'P\u00fcnkte
 error.lookupsrtm.nonerequired=Alle P\u00fcnkte han die H\u00f6hendate scho.  N\u00fc\u00fct z'tue.
-error.gpsies.uploadnotok=Der Gpsies Server h\u00e4t gseit gha
-error.gpsies.uploadfailed=S Uufalade isch fehlgschlage
 error.showphoto.failed=S F\u00f6teli han i n\u00f6d k\u00f6nne lade
 error.playaudiofailed=S Abschpiele vonem File isch fehlgschlage
 error.cache.notthere=D Ordner isch n\u00f6d gfunde worde
index 76939263c9d8af07fa36db1f1686cf99550180dd..f2910cfbc6b3f18c77c928d763a8dc671156b570 100644 (file)
@@ -98,11 +98,13 @@ function.findwaypoint=Find waypoint
 function.rearrangewaypoints=Rearrange waypoints
 function.convertnamestotimes=Convert waypoint names to times
 function.deletefieldvalues=Delete field values
-function.pastecoordinates=Enter new coordinates
+function.pastecoordinates=Enter point coordinates
+function.pastecoordinatelist=Enter list of coordinates
+function.enterpluscode=Enter pluscode
 function.charts=Charts
 function.show3d=Three-D view
 function.distances=Distances
-function.fullrangedetails=Full range details
+function.viewfulldetails=Full details
 function.estimatetime=Estimate time
 function.learnestimationparams=Learn time estimation parameters
 function.autoplay=Autoplay track
@@ -110,10 +112,7 @@ 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
@@ -121,6 +120,7 @@ function.searchopencachingde=Search OpenCaching.de
 function.mapillary=Search for photos in Mapillary
 function.downloadosm=Download OSM data for area
 function.duplicatepoint=Duplicate point
+function.projectpoint=Project point
 function.connecttopoint=Connect to point
 function.disconnectfrompoint=Disconnect from point
 function.removephoto=Remove photo
@@ -370,19 +370,6 @@ dialog.gpsies.column.length=Length
 dialog.gpsies.description=Description
 dialog.gpsies.nodescription=No description
 dialog.gpsies.nonefound=No tracks found
-dialog.gpsies.username=Gpsies username
-dialog.gpsies.password=Gpsies password
-dialog.gpsies.keepprivate=Keep track private
-dialog.gpsies.confirmopenpage=Open the web page for the uploaded track?
-dialog.gpsies.activities=Activity types
-dialog.gpsies.activity.trekking=Hiking
-dialog.gpsies.activity.walking=Walking
-dialog.gpsies.activity.jogging=Running
-dialog.gpsies.activity.biking=Cycling
-dialog.gpsies.activity.motorbiking=Motorbiking
-dialog.gpsies.activity.snowshoe=Snowshoeing
-dialog.gpsies.activity.sailing=Sailing
-dialog.gpsies.activity.skating=Skating
 dialog.mapillary.nonefound=No photos found
 dialog.wikipedia.column.name=Article name
 dialog.wikipedia.column.distance=Distance
@@ -450,6 +437,10 @@ 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.pastecoordinatelist.desc=Enter the coordinates for the new points with one point per line
+dialog.pluscode.desc=Enter or paste the pluscode here
+dialog.pluscode.code=Pluscode
+dialog.pluscode.nothingfound=Please check the code and try again
 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
@@ -489,25 +480,9 @@ dialog.keys.intro=You can use the following shortcut keys instead of using the m
 dialog.keys.keylist=<table><tr><td>Arrow keys</td><td>Pan map left right, up, down</td></tr><tr><td>Ctrl + left, right arrow</td><td>Select previous or next point</td></tr><tr><td>Ctrl + up, down arrow</td><td>Zoom in or out</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Select previous, next segment</td></tr><tr><td>Ctrl + Home, End</td><td>Select first, last point</td></tr><tr><td>Del</td><td>Delete current point</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=The following settings can be saved to a configuration file :
-dialog.saveconfig.prune.trackdirectory=Track directory
-dialog.saveconfig.prune.photodirectory=Photo directory
-dialog.saveconfig.prune.languagecode=Language code (EN)
-dialog.saveconfig.prune.languagefile=Language file
-dialog.saveconfig.prune.gpsdevice=GPS device
-dialog.saveconfig.prune.gpsformat=GPS format
-dialog.saveconfig.prune.povrayfont=Povray font
-dialog.saveconfig.prune.gnuplotpath=Path to gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Path to gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Path to exiftool
-dialog.saveconfig.prune.mapsource=Selected map source
-dialog.saveconfig.prune.mapsourcelist=Map sources
-dialog.saveconfig.prune.diskcache=Map cache
-dialog.saveconfig.prune.kmzimagewidth=KMZ image size
-dialog.saveconfig.prune.colourscheme=Colour scheme
-dialog.saveconfig.prune.linewidth=Line width
-dialog.saveconfig.prune.kmltrackcolour=KML track colour
-dialog.saveconfig.prune.autosavesettings=Autosave settings
+dialog.paths.prune.gnuplotpath=Path to gnuplot
+dialog.paths.prune.gpsbabelpath=Path to gpsbabel
+dialog.paths.prune.exiftoolpath=Path to exiftool
 dialog.setpaths.intro=If you need to, you can choose the paths to the external applications:
 dialog.setpaths.found=Path found?
 dialog.addaltitude.noaltitudes=The selected range does not contain altitudes
@@ -574,6 +549,10 @@ dialog.displaysettings.wpicon.pin=Board pin
 dialog.displaysettings.size.small=Small
 dialog.displaysettings.size.medium=Medium
 dialog.displaysettings.size.large=Large
+dialog.displaysettings.windowstyle=Window style (requires restart)
+dialog.displaysettings.windowstyle.default=Default
+dialog.displaysettings.windowstyle.nimbus=Nimbus
+dialog.displaysettings.windowstyle.gtk=GTK
 dialog.downloadosm.desc=Confirm to download the raw OSM data for the specified area:
 dialog.searchwikipedianames.search=Search for:
 dialog.weather.location=Location
@@ -616,6 +595,12 @@ dialog.autoplay.usetimestamps=Use point timestamps
 dialog.autoplay.rewind=Back to beginning
 dialog.autoplay.pause=Pause
 dialog.autoplay.play=Play
+dialog.markers.halves=Halfway points
+dialog.markers.half.distance=Half distance
+dialog.markers.half.climb=Half climb
+dialog.markers.half.descent=Half descent
+dialog.projectpoint.desc=Enter the direction and distance to project this point
+dialog.projectpoint.bearing=Bearing (degrees from N)
 
 # 3d window
 dialog.3d.title=GpsPrune Three-d view
@@ -637,7 +622,7 @@ confirm.rearrangephotos=Photos rearranged
 confirm.splitsegments=%d segment splits were made
 confirm.sewsegments=%d segment joins were made
 confirm.cutandmove=Selection moved
-confirm.interpolate=Points added
+confirm.pointsadded=%d points added
 confirm.convertnamestotimes=Waypoint names converted
 confirm.saveexif.ok=Saved %d photo files
 confirm.undo.single=operation undone
@@ -656,7 +641,6 @@ confirm.running=Running ...
 confirm.lookupsrtm=Found %d altitude values
 confirm.downloadsrtm=Downloaded %d files to the cache
 confirm.downloadsrtm.1=Downloaded %d file to the cache
-confirm.downloadsrtm.none=No files downloaded, they were already in the cache
 confirm.deletefieldvalues=Field values deleted
 confirm.audioload=Audio files added
 confirm.correlateaudios.single=audio was correlated
@@ -666,7 +650,7 @@ confirm.correlateaudios.multi=audios were correlated
 tip.title=Tip
 tip.useamapcache=By setting up a disk cache (Settings -> Save maps to disk)\nyou can speed up the display and reduce network traffic.
 tip.learntimeparams=The results will be more accurate if you use\nTrack -> Learn time estimation parameters\non your recorded tracks.
-tip.downloadsrtm=You can speed this up by calling\nOnline -> Download SRTM tiles\nto save the data in your map cache.
+tip.downloadsrtm=You can speed this up by setting up a disk cache\nto save the SRTM data locally.
 tip.usesrtmfor3d=This track doesn't have altitudes.\nYou can use the SRTM functions to get approximate\naltitudes for the 3d view.
 tip.manuallycorrelateone=By manually connecting at least one item, the time offset can be calculated for you.
 
@@ -693,7 +677,6 @@ button.selectall=Select all
 button.selectnone=Select none
 button.preview=Preview
 button.load=Load
-button.upload=Upload
 button.guessfields=Guess fields
 button.showwebpage=Show webpage
 button.check=Check
@@ -767,6 +750,7 @@ map.overzoom=No maps available at this zoom level
 # Field names
 fieldname.latitude=Latitude
 fieldname.longitude=Longitude
+fieldname.coordinates=Coordinates
 fieldname.altitude=Altitude
 fieldname.timestamp=Time
 fieldname.time=Time
@@ -781,6 +765,7 @@ fieldname.duration=Duration
 fieldname.speed=Speed
 fieldname.verticalspeed=Vertical speed
 fieldname.description=Description
+fieldname.comment=Comment
 fieldname.mediafilename=Filename
 
 # Measurement units
@@ -880,6 +865,7 @@ error.load.nopoints=No coordinate information found in the file
 error.load.unknownxml=Unrecognised xml format:
 error.load.noxmlinzip=No xml file found inside zip file
 error.load.othererror=Error reading file:
+error.load.nopointsintext=No coordinate information found
 error.jpegload.dialogtitle=Error loading photos
 error.jpegload.nofilesfound=No files found
 error.jpegload.nojpegsfound=No jpeg files found
@@ -900,8 +886,6 @@ error.language.wrongfile=The selected file doesn't appear to be a language file
 error.convertnamestotimes.nonames=No names could be converted into times
 error.lookupsrtm.nonefound=No altitude values available for these points
 error.lookupsrtm.nonerequired=All points already have altitudes, so there's nothing to lookup
-error.gpsies.uploadnotok=The gpsies server returned the message
-error.gpsies.uploadfailed=The upload failed with the error
 error.showphoto.failed=Failed to load photo
 error.playaudiofailed=Failed to play audio clip
 error.cache.notthere=The tile cache directory was not found
index d6724aba79ac694a595f345e8f84bad3ab3e1a3f..e70c8d38213609e134ec220b0d56985a42aa2b2f 100644 (file)
@@ -6,9 +6,6 @@ function.setcolours=Set colors
 
 # Dialogs
 dialog.exportkml.trackcolour=Track color
-dialog.saveconfig.prune.languagecode=Language code (EN_US)
-dialog.saveconfig.prune.colourscheme=Color scheme
-dialog.saveconfig.prune.kmltrackcolour=KML track color
 dialog.setcolours.intro=Click on a color patch to change the color
 dialog.colourchooser.title=Choose color
 dialog.colourer.intro=A point colorer can give track points different colors
index 07cfeca4ecefde9d59cbbb35de48d5d7133025fa..9885d80fa0d3f7920a22f52947cb3488e8c46114 100644 (file)
@@ -12,7 +12,6 @@ menu.track=Track
 menu.track.undo=Deshacer
 menu.track.clearundo=Despejar la lista de deshacer
 menu.track.markrectangle=Marcar puntos dentro de un rect\u00e1ngulo
-function.deletemarked=Eliminar puntos marcados
 menu.range=Rango
 menu.range.all=Seleccionar todo
 menu.range.none=No seleccionar nada
@@ -36,9 +35,10 @@ menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Mapas Yahoo
 menu.view.browser.bing=Mapas Bing
+menu.view.browser.inlinemap=Mapa en l\u00ednea
+menu.view.browser.graphhopper=GraphHopper
 menu.settings=Preferencias
 menu.settings.onlinemode=Cargar mapas de Internet
-dialog.displaysettings.antialias=Usar antialiasing
 menu.settings.autosave=Auto Guardar
 menu.help=Ayuda
 
@@ -87,6 +87,7 @@ function.exportpov=Exportar POV
 function.exportimage=Exportar imagen
 function.editwaypointname=Editar nombre de waypoint
 function.compress=Comprimir track
+function.deletemarked=Eliminar puntos marcados
 function.marklifts=Marcar telef\u00e9ricos
 function.deleterange=Eliminar rango
 function.croptrack=Truncar track
@@ -98,11 +99,13 @@ function.rearrangewaypoints=Reorganizar waypoints
 function.convertnamestotimes=Convertir los nombres de los "waypoints" a tiempo
 function.deletefieldvalues=Borrar valores del campo
 function.findwaypoint=Buscar waypoint
-function.pastecoordinates=Insertar nuevas coordenadas
+function.pastecoordinates=Insertar coordenadas de un nuevo punto
+function.pastecoordinatelist=Ingrese lista de coordenadas
+function.enterpluscode=Insertar c\u00f3digo plus
 function.charts=Diagramas
 function.show3d=Mostrar en 3-D
 function.distances=Distancias
-function.fullrangedetails=Detalles adicionales de rango
+function.viewfulldetails=Detalles adicionales
 function.estimatetime=Estimar duraci\u00f3n
 function.learnestimationparams=Apprender parametros por la estimaci\u00f3n del tiempo
 function.autoplay=Jugar track
@@ -111,10 +114,8 @@ function.setpaths=Configurar rutas del programas
 function.selectsegment=Seleccionar segmento actual
 function.splitsegments=Segmentar el track
 function.sewsegments=Ensamblar los segmentos
-function.getgpsies=Bajar ruta de Gpsies
-function.uploadgpsies=Subir recorrido a Gpsies
+function.createmarkerwaypoints=Crear waypoints marcadores
 function.lookupsrtm=Obtener altitudes de SRTM
-function.downloadsrtm=Descargar datos de SRTM
 function.getwikipedia=Obtener art\u00edculos de Wikipedia cercanos
 function.searchwikipedianames=Buscar en Wikipedia por nombre
 function.searchosmpois=Buscar en OSM cercanos
@@ -122,7 +123,9 @@ function.searchopencachingde=Buscar en OpenCaching.de
 function.mapillary=Buscar en Mapillary
 function.downloadosm=Descargar datos OSM del \u00e1rea
 function.duplicatepoint=Duplicar punto
+function.projectpoint=Proyectar punto
 function.setcolours=Establecer color
+function.setdisplaysettings=Establecer opciones para la visualizaci\u00f3n
 function.setlanguage=Establecer lenguaje
 function.connecttopoint=Conectar con punto
 function.disconnectfrompoint=Desconectar de punto
@@ -147,6 +150,7 @@ function.diskcache=Guardar mapas en disco
 function.managetilecache=Administrar cache de mapas
 function.getweatherforecast=Obtener pron\u00f3stico del tiempo
 function.setaltitudetolerance=Establecer la tolerancia de altitud
+function.selecttimezone=Seleccionar la zona horaria
 
 ## Dialogs
 dialog.exit.confirm.title=Salir de GpsPrune
@@ -246,10 +250,6 @@ dialog.exportgpx.copysource=Copiar la fuente
 dialog.exportgpx.encoding=Codificaci\u00f3n
 dialog.exportgpx.encoding.system=Sistema
 dialog.exportgpx.encoding.utf8=UTF-8
-dialog.3d.useterrain=Terreno
-dialog.3d.terraingridsize=Dimensi\u00f3n de la cuadr\u00edcula
-dialog.exportpov.cannotmakebaseimage=Fallo al guardar la imagen
-dialog.exportpov.baseimage=Imagen de mapa
 dialog.exportpov.text=Introduca los par\u00e1metros para exportar
 dialog.exportpov.font=Fuente
 dialog.exportpov.camerax=C\u00e1mara X
@@ -270,6 +270,7 @@ dialog.baseimage.zoom=Zoom
 dialog.baseimage.incomplete=Imagen incompleta
 dialog.baseimage.tiles=Recuadros
 dialog.baseimage.size=Tama\u00f1o de la imagen
+dialog.exportimage.noimagepossible=Las im\u00e1genes de mapas deben ser guardadas para poder usarlas para una exportaci\u00f3n.
 dialog.exportimage.drawtrack=Dibujar track
 dialog.exportimage.drawtrackpoints=Dibujar puntos del track
 dialog.exportimage.textscalepercent=Agrandamiento del texto (%)
@@ -347,8 +348,11 @@ dialog.estimatetime.parameters.timefor=Duraci\u00f3n para
 dialog.estimatetime.results=Resultados
 dialog.estimatetime.results.estimatedtime=Duraci\u00f3n estimada
 dialog.estimatetime.results.actualtime=Duraci\u00f3n real
+dialog.estimatetime.error.nodistance=Las estimaciones de tiempo necesitan puntos de v\u00eda conectados, para dar una distancia
 dialog.estimatetime.error.noaltitudes=Los rangos seleccionados no contienen altitudes
+dialog.learnestimationparams.intro=Estos son los par\u00e1metros calculados a partir del track
 dialog.learnestimationparams.averageerror=Error medio
+dialog.learnestimationparams.combine=Estos par\u00e1metros se pueden combinar con los valores actuales
 dialog.learnestimationparams.combinedresults=Resultados combinados
 dialog.learnestimationparams.weight.100pccurrent=Mantener datos actuales
 dialog.learnestimationparams.weight.current=actuales
@@ -367,19 +371,6 @@ dialog.gpsies.column.length=Distancia
 dialog.gpsies.description=Descripci\u00f3n
 dialog.gpsies.nodescription=Sin descripci\u00f3n
 dialog.gpsies.nonefound=No se encontraron pistas
-dialog.gpsies.username=Nombre de usuario en Gpsies
-dialog.gpsies.password=Contrase\u00f1a de Gpsies
-dialog.gpsies.keepprivate=Mantener el recorrido como privado
-dialog.gpsies.confirmopenpage=Abrir la p\u00e1gina web del recorrido subido
-dialog.gpsies.activities=Apto para
-dialog.gpsies.activity.trekking=Excursi\u00f3n
-dialog.gpsies.activity.walking=Caminar
-dialog.gpsies.activity.jogging=Correr
-dialog.gpsies.activity.biking=En bicicleta
-dialog.gpsies.activity.motorbiking=En moto
-dialog.gpsies.activity.snowshoe=Raquetas de nieve
-dialog.gpsies.activity.sailing=Vela
-dialog.gpsies.activity.skating=Patinaje
 dialog.mapillary.nonefound=No se encontraron fotos
 dialog.wikipedia.column.name=Nombre del art\u00edculo
 dialog.wikipedia.column.distance=Distancia
@@ -422,8 +413,8 @@ dialog.correlate.timestamp.end=Final
 dialog.correlate.audioselect.intro=Seleccione uno de estos audios correlacionados para usarlo como margen temporal.
 dialog.correlate.select.audioname=Nombre del audio
 dialog.correlate.select.audiolater=Audio m\u00e1s adelante
-dialog.rearrangephotos.desc=Seleccionar el destino y orden de los puntos de las fotos
 dialog.rearrangewaypoints.desc=Seleccionar el destino y orden de los waypoints
+dialog.rearrangephotos.desc=Seleccionar el destino y orden de los puntos de las fotos
 dialog.rearrange.tostart=Mover al comienzo
 dialog.rearrange.toend=Mover al final
 dialog.rearrange.tonearest=Mover al punto m\u00e1s pr\u00f3ximo
@@ -447,6 +438,10 @@ 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.pastecoordinatelist.desc=Introducir las coordenadas de los nuevos puntos con un punto por l\u00ednea
+dialog.pluscode.desc=Introduzca o pegue el c\u00f3digo plus aqu\u00ed
+dialog.pluscode.code=C\u00f3digo plus
+dialog.pluscode.nothingfound=Por favor, compruebe el c\u00f3digo e int\u00e9ntelo de nuevo
 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
@@ -486,25 +481,9 @@ dialog.keys.intro=Usted puede usar el siguiente atajo en lugar de usar el rat\u0
 dialog.keys.keylist=<table><tr><td>Teclas de cursor</td><td>Desplazar a la izquierde, derecha, arriba, abajo</td></tr><tr><td>Ctrl + cursor izquierda, derecha</td><td>Seleccionar punto siguiente o anterior</td></tr><tr><td>Ctrl + cursor arriba, abajo</td><td>Ampliar o reducir zoom</td></tr><tr><td>Ctrl + Av Pag, Re Pag</td><td>Seleccionar segmento siguiente, anterior</td></tr><tr><td>Ctrl + Inicio, Fin</td><td>Seleccionar primer, \u00faltimo punto</td></tr><tr><td>Supr</td><td>Eliminar punto actual</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=La siguiente configuraci\u00f3n puede ser salvada en un archivo de configuraci\u00f3n
-dialog.saveconfig.prune.trackdirectory=Directorio de pista
-dialog.saveconfig.prune.photodirectory=Directorio de foto
-dialog.saveconfig.prune.languagecode=C\u00f3digo de lenguaje (ES)
-dialog.saveconfig.prune.languagefile=Archivo de lenguaje
-dialog.saveconfig.prune.gpsdevice=Dispositivo GPS
-dialog.saveconfig.prune.gpsformat=Formato GPS
-dialog.saveconfig.prune.povrayfont=Fuente povray
-dialog.saveconfig.prune.gnuplotpath=Ruta a gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Ruta a gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Ruta a exiftool
-dialog.saveconfig.prune.mapsource=Proveedor de mapas seleccionado
-dialog.saveconfig.prune.mapsourcelist=Proveedor de mapas
-dialog.saveconfig.prune.diskcache=Memoria intermedia de mapas
-dialog.saveconfig.prune.kmzimagewidth=Ancho de im\u00e1genes en KMZ
-dialog.saveconfig.prune.colourscheme=Color de esquema
-dialog.saveconfig.prune.linewidth=Ancho de l\u00ednea
-dialog.saveconfig.prune.kmltrackcolour=Color de pista de KML
-dialog.saveconfig.prune.autosavesettings=Guardar preferencias al salir
+dialog.paths.prune.gnuplotpath=Ruta a gnuplot
+dialog.paths.prune.gpsbabelpath=Ruta a gpsbabel
+dialog.paths.prune.exiftoolpath=Ruta a exiftool
 dialog.setpaths.intro=Si usted necesita, puede escoger las rutas a aplicaciones externas
 dialog.setpaths.found=\u00bfRuta encontrada?
 dialog.addaltitude.noaltitudes=Los rangos seleccionados no contienen altitudes
@@ -561,12 +540,20 @@ 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.displaysettings.linewidth=Anchura de las l\u00edneas para los recorridos (1-4)
+dialog.displaysettings.antialias=Usar antialiasing
 dialog.displaysettings.waypointicons=Iconos de los waypoints
+dialog.displaysettings.wpicon.default=Por defecto
+dialog.displaysettings.wpicon.ringpt=Marcador redondeado
 dialog.displaysettings.wpicon.plectrum=Plectro
 dialog.displaysettings.wpicon.ring=Anillo
+dialog.displaysettings.wpicon.pin=Chincheta
 dialog.displaysettings.size.small=Peque\u00f1os
 dialog.displaysettings.size.medium=Medianos
 dialog.displaysettings.size.large=Grandes
+dialog.displaysettings.windowstyle=Estilo de la ventana
+dialog.displaysettings.windowstyle.default=Por defecto
+dialog.displaysettings.windowstyle.nimbus=Nimbus
+dialog.displaysettings.windowstyle.gtk=GTK
 dialog.downloadosm.desc=Confirmar la descarga de datos en bruto de OSM para el \u00e1rea especificada.
 dialog.searchwikipedianames.search=Buscar:
 dialog.weather.location=Localidad
@@ -590,17 +577,31 @@ dialog.weather.day.sunday=Domingo
 dialog.weather.wind=Viento
 dialog.weather.temp=Temp
 dialog.weather.humidity=Humedad
+dialog.weather.creditnotice=Estos datos est\u00e1n disponibles en openweathermap.org. Su sitio web tiene m\u00e1s detalles.
+dialog.deletebydate.onlyonedate=Todos los puntos se registraron en la misma fecha.
+dialog.deletebydate.intro=Para cada fecha del track, puede elegir entre borrar o mantener los puntos
 dialog.deletebydate.nodate=Sin marcas de tiempo
 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.intro=Aqu\u00ed se puede seleccionar la zona horaria para mostrar las marcas de tiempo de los puntos
 dialog.settimezone.system=Zona horaria del sistema
+dialog.settimezone.custom=Usar la siguiente zona horaria:
+dialog.settimezone.list.toomany=Demasiados por elegir
+dialog.settimezone.selectedzone=Zona horaria seleccionada
+dialog.settimezone.offsetfromutc=Compensaci\u00f3n respecto a UTC
 dialog.autoplay.duration=Duraci\u00f3n (seg)
 dialog.autoplay.usetimestamps=Usar informaci\u00f3n de tiempo
 dialog.autoplay.rewind=Rebobinar
 dialog.autoplay.pause=Pausar
 dialog.autoplay.play=Jugar
+dialog.markers.halves=Puntos medios
+dialog.markers.half.distance=Media distancia
+dialog.markers.half.climb=Mitad de cuesta
+dialog.markers.half.descent=Mitad de descenso
+dialog.projectpoint.desc=Introduzca la direcci\u00f3n y la distancia para proyectar este punto
+dialog.projectpoint.bearing=Azimut (grados desde el Norte)
 
 ## 3d window
 dialog.3d.title=GpsPrune vista 3-D
@@ -619,10 +620,10 @@ confirm.addtimeoffset=A\u00f1adido margen de tiempo
 confirm.addaltitudeoffset=A\u00f1adido margen de altitud
 confirm.rearrangewaypoints=Waypoints reorganizados
 confirm.rearrangephotos=Fotos reacomodadas
-confirm.splitsegments=%d escisiones fueron hechas
-confirm.sewsegments=%d conexiones fueron hechas
+confirm.splitsegments=Se han hecho %d divisiones
+confirm.sewsegments=Se han hecho %d uniones
 confirm.cutandmove=Mover selecci\u00f3n
-confirm.interpolate=Puntos insertados
+confirm.pointsadded=%d puntos insertados
 confirm.convertnamestotimes=Nombres de "waypoint" convertidos
 confirm.saveexif.ok=Guardado %d fotos
 confirm.undo.single=operaci\u00f3n deshecha
@@ -648,9 +649,10 @@ confirm.correlateaudios.multi=Los audios fueron correlacionados
 
 ## Tips, shown just once when appropriate
 tip.title=Sugerencia
-tip.useamapcache=By setting up a disk cache (Preferencias -> Guardar mapas en disco)\nyou can speed up the display and reduce network traffic.
-tip.learntimeparams=The results will be more accurate if you use\nTrack -> Apprender parametros por la estimaci\u00f3n del tiempo\non your recorded tracks.
-tip.downloadsrtm=You can speed this up by calling\nOnline -> Descargar datos de SRTM\nto save the data in your map cache.
+tip.useamapcache=Configurando una cach\u00e9 de disco (Preferencias -> Guardar mapas en disco)\npuede acelerar la visualizaci\u00f3n y reducir el tr\u00e1fico de la red.
+tip.learntimeparams=Los resultados ser\u00e1n m\u00e1s precisos si utiliza\nTrack -> Aprender par\u00e1metros de estimaci\u00f3n de tiempo\nen sus pistas grabadas.
+tip.downloadsrtm=Puede acelerar esto si guarda los datos en su cach\u00e9 de mapas.
+tip.usesrtmfor3d=Esta pista no tiene altitudes.\nPuede utilizar las funciones del SRTM\npara obtener altitudes aproximadas para la vista 3d.
 tip.manuallycorrelateone=Correlacionando al menos una foto manualmente, el margen de tiempo se calcula autom\u00e1ticamente.
 
 ## Buttons
@@ -676,7 +678,6 @@ button.selectall=Seleccionar todo
 button.selectnone=Seleccionar nada
 button.preview=Previsualizaci\u00f3n
 button.load=Cargar
-button.upload=Subir
 button.guessfields=Adivinar campos
 button.showwebpage=Mostrar p\u00e1gina web
 button.check=Verificar
@@ -750,6 +751,7 @@ map.overzoom=No existen mapas disponibles con este nivel de enfoque
 ## Field names
 fieldname.latitude=Latitud
 fieldname.longitude=Longitud
+fieldname.coordinates=Coordenadas
 fieldname.altitude=Altitud
 fieldname.timestamp=Informaci\u00f3n de tiempo
 fieldname.time=Tiempo
@@ -764,6 +766,7 @@ fieldname.duration=Duraci\u00f3n
 fieldname.speed=Velocidad
 fieldname.verticalspeed=Velocidad vertical
 fieldname.description=Descripci\u00f3n
+fieldname.comment=Comentario
 fieldname.mediafilename=Archivo
 
 ## Measurement units
@@ -863,6 +866,7 @@ error.load.nopoints=No se encuentra ninguna informaci\u00f3n de coordenadas en e
 error.load.unknownxml=Formato xml no reconocido:
 error.load.noxmlinzip=No se encuentra ning\u00fan archivo xml en el archivo zip
 error.load.othererror=Fallo al cargar datos:
+error.load.nopointsintext=No se encuentra ninguna informaci\u00f3n de coordenadas
 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
@@ -883,13 +887,12 @@ error.language.wrongfile=El archivo seleccionado no parece ser un archivo de len
 error.convertnamestotimes.nonames=Los nombres no pudieron ser convertidos en tiempos
 error.lookupsrtm.nonefound=No se encontraron valores de altitud
 error.lookupsrtm.nonerequired=Todos los puntos tienen altitudes, as\u00ed que no hay nada que buscar.
-error.gpsies.uploadnotok=El servidor de gpsies ha devuelto el mensaje
-error.gpsies.uploadfailed=La carga ha fallado con el error
 error.showphoto.failed=Fallo al cargar la foto
 error.playaudiofailed=Fallo reproduciendo archivo de audio
 error.cache.notthere=No se encontr\u00f3 la carpeta del cache de recuadros
 error.cache.empty=La carpeta del cache de recuadros esta vac\u00edo
 error.cache.cannotdelete=No se pudieron borrar recuadros
+error.learnestimationparams.failed=No puede aprender los par\u00e1metros de esta pista.\nIntente cargar m\u00e1s pistas.
 error.tracksplit.nosplit=Imposible segmentar el track
 error.downloadsrtm.nocache=Imposible guardar los archivos.\nPor favor, compruebe el cache.
 error.sewsegments.nothingdone=Imposible ensamblar los segmentos.\nEl track tiene ahora %d segmentos.
index e6d686078ba049b2642ae823d1117864284c7df3..2b7566ed0c9be8afe0252ee770db8c003042a673 100644 (file)
@@ -100,7 +100,7 @@ function.pastecoordinates=Anna uudet koordinaatit...
 function.charts=Kaaviot
 function.show3d=3D-n\u00e4kym\u00e4
 function.distances=V\u00e4limatkat
-function.fullrangedetails=Koko pistealueen tiedot
+function.viewfulldetails=Kaikki yksityiskohdat
 function.estimatetime=Arvioi aika
 function.learnestimationparams=Opi aika-arvion parametrit
 function.autoplay=Animoi reitti
@@ -108,10 +108,7 @@ 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
@@ -362,18 +359,6 @@ 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.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
@@ -480,25 +465,9 @@ dialog.keys.intro=Voit hiiren asemesta k\u00e4ytt\u00e4\u00e4 seuraavia n\u00e4p
 dialog.keys.keylist=<table><tr><td>Nuolin\u00e4pp\u00e4imill\u00e4</td><td>voit siirt\u00e4\u00e4 kartaa vasemmalle, oikealle, yl\u00f6s ja alas</td></tr><tr><td>Ctrl + vasen/oikea nuoli\u00e4pp\u00e4in</td><td>Valitse edellinen/seuraava reittipiste</td></tr><tr><td>Ctrl + yl\u00f6s/alas nuolin\u00e4pp\u00e4in</td><td>L\u00e4henn\u00e4 tai loitonna</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Valitse edellinen/seuraava pistelohko</td></tr><tr><td>Ctrl + Home, End</td><td>Valitse ensimm\u00e4inen/viimeinen reittipiste</td></tr><tr><td>Del</td><td>Poista valittu reittipiste</td></tr></table>
 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.paths.prune.gnuplotpath=Polku ohjelmaan gnuplot
+dialog.paths.prune.gpsbabelpath=Polku ohjelmaan gpsbabel
+dialog.paths.prune.exiftoolpath=Polku ohjelmaan exiftool
 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
@@ -546,6 +515,7 @@ dialog.diskcache.table.zoom=Zoom
 dialog.diskcache.table.tiles=Tiles
 dialog.diskcache.table.megabytes=Megabytes
 dialog.diskcache.tileset=Tileset
+dialog.diskcache.tileset.multiple=usea
 dialog.diskcache.deleteold=Poista vanhan karttapalat
 dialog.diskcache.maximumage=Enimm\u00e4isik\u00e4 (p\u00e4ivi\u00e4)
 dialog.diskcache.deleteall=Poista kaikki kartapalat
@@ -627,7 +597,6 @@ 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
@@ -637,7 +606,6 @@ confirm.correlateaudios.multi=\u00e4\u00e4net olivat korreloidut
 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.
 
@@ -664,7 +632,6 @@ 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
@@ -737,6 +704,7 @@ map.overzoom=Ei karttoja t\u00e4lle suurennussuhteelle
 # Field names
 fieldname.latitude=Leveysaste (Lat.)
 fieldname.longitude=Pituusaste (Long.)
+fieldname.coordinates=Koordinaatit
 fieldname.altitude=Korkeus
 fieldname.timestamp=Aikaleima
 fieldname.time=Aika
@@ -751,6 +719,7 @@ fieldname.duration=Kesto
 fieldname.speed=Nopeus
 fieldname.verticalspeed=Pystysuora nopeus
 fieldname.description=Kuvaus
+fieldname.comment=Kommentti
 fieldname.mediafilename=Median tiedostonimi
 
 # Measurement units
@@ -869,8 +838,6 @@ error.language.wrongfile=Valittu tiedosto ei n\u00e4yt\u00e4 olevan GpsPrune:n k
 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
index f884c0215623c2c1da16cd9cb8978b22baf7ef31..2d293929d70aea06341e963f006d101d29cc800e 100644 (file)
@@ -37,6 +37,7 @@ menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Cartes Yahoo
 menu.view.browser.bing=Cartes Bing
+menu.view.browser.inlinemap=Carte du roller
 menu.settings=Pr\u00e9f\u00e9rences
 menu.settings.onlinemode=Charger cartes depuis internet
 menu.settings.autosave=Sauver automatiquement en quittant
@@ -86,6 +87,7 @@ function.exportpov=Exporter en POV
 function.exportimage=Exporter une image
 function.editwaypointname=\u00c9diter le nom du point
 function.compress=Compresser la trace
+function.marklifts=Marquer les remont\u00e9es m\u00e9caniques
 function.deleterange=Supprimer l'\u00e9tendue
 function.croptrack=Recadrer l'\u00e9tendue
 function.interpolate=Interpoler les points
@@ -96,11 +98,13 @@ function.rearrangewaypoints=R\u00e9arranger les points de navigation
 function.convertnamestotimes=Convertir les noms de points de navigation en horodatages
 function.deletefieldvalues=Effacer les valeurs du champ
 function.findwaypoint=Trouver un waypoint
-function.pastecoordinates=Coller les coordonn\u00e9es
+function.pastecoordinates=Saisir coordonn\u00e9es d'un point
+function.pastecoordinatelist=Saisir liste de coordonn\u00e9es
+function.enterpluscode=Saisir code plus
 function.charts=Graphiques
 function.show3d=Montrer en 3D
 function.distances=Distances
-function.fullrangedetails=Montrer tous les d\u00e9tails
+function.viewfulldetails=Montrer tous les d\u00e9tails
 function.estimatetime=Temps estim\u00e9
 function.learnestimationparams=Apprentissage de l'estimation
 function.setmapbg=D\u00e9finir le fond de carte
@@ -109,20 +113,19 @@ function.autoplay=Jouer la trace
 function.selectsegment=S\u00e9lectionner le segment courant
 function.splitsegments=S\u00e9pare les segments
 function.sewsegments=R\u00e9unis les segments
-function.getgpsies=R\u00e9cup\u00e9rer les traces Gpsies
-function.uploadgpsies=T\u00e9l\u00e9charger la trace sur Gpsies
+function.createmarkerwaypoints=Cr\u00e9ation de points de rep\u00e8re
 function.lookupsrtm=R\u00e9cup\u00e9rer les altitudes depuis SRTM
-function.downloadsrtm=T\u00e9l\u00e9charger les donn\u00e9es SRTM
 function.getwikipedia=Obtenir les articles de Wikip\u00e9dia \u00e0 proximit\u00e9
 function.searchwikipedianames=Rechercher dans Wikip\u00e9dia par nom
-function.searchosmpois=Rechercher dans OSM \u00e0 proximit\u00e9
+function.searchosmpois=Rechercher de marques OSM \u00e0 proximit\u00e9
 function.searchopencachingde=Rechercher dans OpenCaching.de
 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.setdisplaysettings=Pr\u00e9f\u00e9rences graphiques
+function.setdisplaysettings=Pr\u00e9f\u00e9rences d'affichage
 function.setlanguage=Choisir la langue
+function.projectpoint=Projeter le point
 function.connecttopoint=Relier au point
 function.disconnectfrompoint=D\u00e9tacher du point
 function.removephoto=Retirer la photo
@@ -211,8 +214,10 @@ dialog.gpsbabel.filter.simplify.maxpoints=Nombre de points <
 dialog.gpsbabel.filter.simplify.maxerror=ou erreur <
 dialog.gpsbabel.filter.simplify.crosstrack=d\u00e9viation
 dialog.gpsbabel.filter.simplify.length=changement de longeur
+dialog.gpsbabel.filter.simplify.relative=par rapport \u00e0 la pr\u00e9cision
 dialog.gpsbabel.filter.distance.distance=Si la distance <
 dialog.gpsbabel.filter.distance.time=et difference de temps <
+dialog.gpsbabel.filter.interpolate.intro=Ajouter des points suppl\u00e9mentaires
 dialog.gpsbabel.filter.interpolate.distance=Si la distance >
 dialog.gpsbabel.filter.interpolate.time=ou difference de temps >
 dialog.saveoptions.title=Enregistrer le fichier
@@ -262,9 +267,10 @@ dialog.baseimage.zoom=Zoom
 dialog.baseimage.incomplete=Image incompl\u00e8te
 dialog.baseimage.tiles=Dalles
 dialog.baseimage.size=Taille de l'image
+dialog.exportimage.noimagepossible=Images de la carte doivent \u00eatre sauvegard\u00e9es dans un cache.
 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 (%)
+dialog.exportimage.textscalepercent=Facteur d'\u00e9chelle du texte (%)
 dialog.pointtype.desc=Sauvegarder ces types de points:
 dialog.pointtype.track=Points de la trace
 dialog.pointtype.waypoint=Waypoints
@@ -359,19 +365,6 @@ dialog.gpsies.column.length=Distance
 dialog.gpsies.description=Description
 dialog.gpsies.nodescription=Aucune description
 dialog.gpsies.nonefound=Aucune trace trouv\u00e9e
-dialog.gpsies.username=Nom d'utilisateur Gpsies
-dialog.gpsies.password=Mot de passe Gpsies
-dialog.gpsies.keepprivate=Trace priv\u00e9e
-dialog.gpsies.confirmopenpage=Ouvrir la page web de la trace t\u00e9l\u00e9charg\u00e9e ?
-dialog.gpsies.activities=Activit\u00e9
-dialog.gpsies.activity.trekking=Trekking
-dialog.gpsies.activity.walking=Randonn\u00e9e
-dialog.gpsies.activity.jogging=Jogging
-dialog.gpsies.activity.biking=V\u00e9lo
-dialog.gpsies.activity.motorbiking=Moto
-dialog.gpsies.activity.snowshoe=Raquette
-dialog.gpsies.activity.sailing=Volle
-dialog.gpsies.activity.skating=Skating
 dialog.mapillary.nonefound=Aucun foto trouv\u00e9
 dialog.wikipedia.column.name=Nom de l'article
 dialog.wikipedia.column.distance=Distance
@@ -436,9 +429,13 @@ dialog.compress.summarylabel=Points \u00e0 supprimer
 dialog.compress.confirm=%d point(s) marqu\u00e9(s).\nSupprimer les points?
 dialog.compress.confirmnone=Pas de points marqu\u00e9s
 dialog.deletemarked.nonefound=Pas de donn\u00e9es \u00e0 effacer
-dialog.pastecoordinates.desc=Entrez ou collez les coordonn\u00e9es ici
+dialog.pastecoordinates.desc=Saisissez ou collez les coordonn\u00e9es ici
 dialog.pastecoordinates.coords=Coordonn\u00e9es
-dialog.pastecoordinates.nothingfound=V\u00e9rifier les coordonn\u00e9es et essayez \u00e0 nouveau
+dialog.pastecoordinates.nothingfound=V\u00e9rifiez les coordonn\u00e9es et essayez \u00e0 nouveau
+dialog.pastecoordinatelist.desc=Saisissez les coordonn\u00e9es des nouveaux points avec un point par ligne
+dialog.pluscode.desc=Entrez ou collez le code ici
+dialog.pluscode.code=Code Plus
+dialog.pluscode.nothingfound=V\u00e9rifiez le code et essayez \u00e0 nouveau
 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
@@ -446,7 +443,7 @@ dialog.about.summarytext1=GpsPrune est un programme pour charger, afficher et \u
 dialog.about.summarytext2=Distribu\u00e9 sous license Gnu GPL pour un usage et une am\u00e9lioration libres, ouverts et mondiaux.<br>La copie, la redistribution et la modification sont autoris\u00e9es et encourag\u00e9es<br>selon les conditions d\u00e9taill\u00e9es dans le fichier <code>license.txt</code> inclus.
 dialog.about.summarytext3=Consultez la page <code style="font-weight:bold">https://activityworkshop.net/</code> 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.translatedby=Texte en fran\u00e7ais par Petrovsk, theYinYeti, R\u00e9mi, jmr et Tche333.
 dialog.about.systeminfo=Info Syst\u00e8me
 dialog.about.systeminfo.os=Syst\u00e8me d'exploitation
 dialog.about.systeminfo.java=Java Runtime
@@ -478,25 +475,9 @@ dialog.keys.intro=Vous pouvez utiliser ces raccourcis clavier \u00e0 la place de
 dialog.keys.keylist=<table><tr><td>Touches-fl\u00e8ches</td><td>Faire d\u00e9filer la carte horizontalement et verticalement</td></tr><tr><td>Ctrl + gauche, Ctrl + droite</td><td>Choisir le point pr\u00e9c\u00e9dent ou suivant</td></tr><tr><td>Ctrl + haut, Ctrl + bas</td><td>Zoomer, s'\u00e9loigner</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Choisir le segment pr\u00e9c\u00e9dent ou suivant</td></tr><tr><td>Ctrl + Home, End</td><td>Choisir le point premier, dernier</td></tr><tr><td>Suppr</td><td>Effacer le point courant</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=Les param\u00e8tres suivants peuvent \u00eatre sauvegard\u00e9s dans un fichier de configuration:
-dialog.saveconfig.prune.trackdirectory=Dossier des traces
-dialog.saveconfig.prune.photodirectory=Dossier des Photos
-dialog.saveconfig.prune.languagecode=Code langue (FR)
-dialog.saveconfig.prune.languagefile=Fichier de langue
-dialog.saveconfig.prune.gpsdevice=Chemin du p\u00e9riph\u00e9rique GPS
-dialog.saveconfig.prune.gpsformat=Format GPS
-dialog.saveconfig.prune.povrayfont=Police povray
-dialog.saveconfig.prune.gnuplotpath=Chemin gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Chemin gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Chemin exiftool
-dialog.saveconfig.prune.mapsource=Carte source s\u00e9lectionn\u00e9e
-dialog.saveconfig.prune.mapsourcelist=Sources de cartes
-dialog.saveconfig.prune.diskcache=Cache de carte
-dialog.saveconfig.prune.kmzimagewidth=Largeur de l'image KMZ
-dialog.saveconfig.prune.colourscheme=Mod\u00e8le de couleurs
-dialog.saveconfig.prune.linewidth=Largeur de ligne
-dialog.saveconfig.prune.kmltrackcolour=Couleur de la trace KML
-dialog.saveconfig.prune.autosavesettings=R\u00e9glages de sauvegarde automatique
+dialog.paths.prune.gnuplotpath=Chemin gnuplot
+dialog.paths.prune.gpsbabelpath=Chemin gpsbabel
+dialog.paths.prune.exiftoolpath=Chemin exiftool
 dialog.setpaths.intro=Si vous le souhaitez, vous pouvez d\u00e9finir les chemins des applications externes:
 dialog.setpaths.found=Chemin trouv\u00e9 ?
 dialog.addaltitude.noaltitudes=L'\u00e9tendue s\u00e9lectionn\u00e9e de contient pas d'altitudes
@@ -515,6 +496,7 @@ dialog.colourchooser.title=Choisissez la couleur
 dialog.colourchooser.red=Rouge
 dialog.colourchooser.green=Vert
 dialog.colourchooser.blue=Bleu
+dialog.colourer.intro=Un coloriste peut s\u00e9lectionner les couleurs pour les points de trace
 dialog.colourer.type=Crit\u00e8re de coloriste
 dialog.colourer.type.none=Aucun
 dialog.colourer.type.byfile=Selon fichier
@@ -553,9 +535,17 @@ dialog.deletefieldvalues.intro=Choisir le champ \u00e0 effacer pour l'\u00e9tend
 dialog.deletefieldvalues.nofields=L'\u00e9tendue actuelle n'a pas de champs \u00e0 effacer
 dialog.displaysettings.linewidth=L'\u00e9paisseur des lignes des traces (1-4)
 dialog.displaysettings.antialias=Anticr\u00e9nelage
+dialog.displaysettings.waypointicons=Ic\u00f4nes des waypoints
+dialog.displaysettings.wpicon.default=D\u00e9faut
+dialog.displaysettings.wpicon.ringpt=Disque
+dialog.displaysettings.wpicon.plectrum=Plectre
+dialog.displaysettings.wpicon.ring=Anneau
+dialog.displaysettings.wpicon.pin=Clouer
 dialog.displaysettings.size.small=Petit
 dialog.displaysettings.size.medium=Moyen
 dialog.displaysettings.size.large=Grand
+dialog.displaysettings.windowstyle=Style de fen\u00eatre (apr\u00e8s red\u00e9marrage)
+dialog.displaysettings.windowstyle.default=D\u00e9faut
 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
@@ -581,16 +571,29 @@ dialog.weather.temp=Temp
 dialog.weather.humidity=Humidit\u00e9
 dialog.weather.creditnotice=Ces donn\u00e9es sont fournies par openweathermap.org. Consultez la page pour plus de d\u00e9tails.
 dialog.deletebydate.onlyonedate=Tous les points sont \u00e0 la m\u00eame date.
+dialog.deletebydate.intro=Pour chaque date, vous pouvez choisir de conserver ou de supprimer les points
 dialog.deletebydate.nodate=Sans horodatage
 dialog.deletebydate.column.keep=Garder
 dialog.deletebydate.column.delete=Supprimer
 dialog.setaltitudetolerance.text.metres=Limite (m\u00e8tres) pour les petites diff\u00e9rences d'altitude
 dialog.setaltitudetolerance.text.feet=Limite (pieds) pour les petites diff\u00e9rences d'altitude
+dialog.settimezone.intro=Ce fuseau horaire sera utilis\u00e9 pour afficher les horodatages des points
+dialog.settimezone.system=Utiliser fuseau du syst\u00e8me
+dialog.settimezone.custom=Utiliser le fuseau suivant:
+dialog.settimezone.list.toomany=Beaucoup trop de fuseaux
+dialog.settimezone.selectedzone=Fuseau horaire s\u00e9lectionn\u00e9
+dialog.settimezone.offsetfromutc=D\u00e9calage avec UTC
 dialog.autoplay.duration=Dur\u00e9e (sec)
 dialog.autoplay.usetimestamps=Utiliser information de temps
 dialog.autoplay.rewind=Retour au d\u00e9but
 dialog.autoplay.pause=Pause
 dialog.autoplay.play=Jouer
+dialog.markers.halves=Points \u00e0 mi-chemin
+dialog.markers.half.distance=Demi-distance
+dialog.markers.half.climb=Demi-mont\u00e9e
+dialog.markers.half.descent=Demi-descente
+dialog.projectpoint.desc=Saisissez la direction et la distance de la projection
+dialog.projectpoint.bearing=Azimut (degr\u00e8s du nord)
 
 # 3d window
 dialog.3d.title=Vue 3D de GpsPrune
@@ -609,8 +612,10 @@ 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 de segments ont \u00e9t\u00e9 effectu\u00e9es
+confirm.sewsegments=%d fusions de segments ont \u00e9t\u00e9 effectu\u00e9es
 confirm.cutandmove=S\u00e9lection d\u00e9plac\u00e9e
-confirm.interpolate=Points ajout\u00e9s
+confirm.pointsadded=%d points ajout\u00e9s
 confirm.convertnamestotimes=Noms de waypoints convertis
 confirm.saveexif.ok=Enregistr\u00e9 %d fichiers photo
 confirm.undo.single=op\u00e9ration annul\u00e9e
@@ -629,7 +634,6 @@ confirm.running=En cours...
 confirm.lookupsrtm=Trouv\u00e9 %d valeurs d'altitude
 confirm.downloadsrtm=%d fichiers ont \u00e9t\u00e9 t\u00e9l\u00e9charg\u00e9s
 confirm.downloadsrtm.1=%d fichier a \u00e9t\u00e9 t\u00e9l\u00e9charg\u00e9
-confirm.downloadsrtm.none=Pas de fichiers ont \u00e9t\u00e9 t\u00e9l\u00e9charg\u00e9s, ils sont d\u00e9j\u00e0 l\u00e0
 confirm.deletefieldvalues=Valeurs effac\u00e9es
 confirm.audioload=Fichiers audio ajout\u00e9s
 confirm.correlateaudios.single=fichier audio a \u00e9t\u00e9 corr\u00e9l\u00e9
@@ -637,9 +641,9 @@ confirm.correlateaudios.multi=fichiers audio ont \u00e9t\u00e9 corr\u00e9l\u00e9
 
 # Tips
 tip.title=Astuce
-tip.useamapcache=By setting up a disk cache (Pr\u00e9f\u00e9rences -> Enregistrer les cartes sur le disque)\nyou can speed up the display and reduce network traffic.
-tip.learntimeparams=The results will be more accurate if you use\nTrace -> Apprentissage de l'estimation\non your recorded tracks.
-tip.downloadsrtm=You can speed this up by calling\nEn ligne -> T\u00e9l\u00e9charger les donn\u00e9es SRTM\nto save the data in your map cache.
+tip.useamapcache=Si vous configurez un cache (Pr\u00e9f\u00e9rences -> Enregistrer les cartes sur le disque)\nl'affichage sera plus rapide et les t\u00e9l\u00e9chargements seront r\u00e9duits.
+tip.learntimeparams=Les r\u00e9sultats seront plus pr\u00e9cis si GpsPrune peut\napprender la vitesse de vos traces\n(Trace -> Apprentissage de l'estimation).
+tip.usesrtmfor3d=Cette trace n'a pas d'altitudes.\nEn utilisant le SRTM, il est possible d'obtenir\ndes altitudes approximatives.
 tip.manuallycorrelateone=En corr\u00e9lant manuellement au moins une photo, le d\u00e9calage de temps peut \u00eatre calcul\u00e9 pour vous.
 
 # Buttons
@@ -665,7 +669,6 @@ button.selectall=Tout s\u00e9lectionner
 button.selectnone=Ne rien s\u00e9lectionner
 button.preview=Aper\u00e7u
 button.load=T\u00e9l\u00e9charger
-button.upload=Envoyer
 button.guessfields=Deviner les champs
 button.showwebpage=Montrer page web
 button.check=V\u00e9rifier
@@ -739,6 +742,7 @@ map.overzoom=Aucune carte disponible \u00e0 ce niveau de zoom
 # Field names
 fieldname.latitude=Latitude
 fieldname.longitude=Longitude
+fieldname.coordinates=Coordonn\u00e9es
 fieldname.altitude=Altitude
 fieldname.timestamp=Date et heure
 fieldname.time=Temps
@@ -753,6 +757,7 @@ fieldname.duration=Dur\u00e9e
 fieldname.speed=Vitesse
 fieldname.verticalspeed=Vitesse verticale
 fieldname.description=Description
+fieldname.comment=Commentaire
 fieldname.mediafilename=Nom de fichier
 
 # Measurement units
@@ -852,6 +857,7 @@ error.load.nopoints=Aucune coordonn\u00e9e trouv\u00e9e dans le fichier
 error.load.unknownxml=Format xml non-reconnu :
 error.load.noxmlinzip=Aucune xml fichier trouv\u00e9e dans le fichier
 error.load.othererror=Erreur \u00e0 la lecture du fichier :
+error.load.nopointsintext=Aucune coordonn\u00e9e trouv\u00e9e
 error.jpegload.dialogtitle=Erreur au chargement des photos
 error.jpegload.nofilesfound=Aucun fichier trouv\u00e9
 error.jpegload.nojpegsfound=Aucun fichier jpeg trouv\u00e9
@@ -872,11 +878,11 @@ error.language.wrongfile=Le fichier s\u00e9lectionn\u00e9 n'est pas un fichier d
 error.convertnamestotimes.nonames=Aucun nom n'a pu \u00eatre converti en horaire
 error.lookupsrtm.nonefound=Aucune valeur d'altitude trouv\u00e9e pour les points
 error.lookupsrtm.nonerequired=Tous les points ont d\u00e9j\u00e0 une altitude, il n'y a rien \u00e0 r\u00e9cup\u00e9rer
-error.gpsies.uploadnotok=Le serveur de Gpsies a renvoy\u00e9 le message
-error.gpsies.uploadfailed=L'envoi a \u00e9chou\u00e9 avec l'erreur
 error.showphoto.failed=Impossible de charger la photo
 error.playaudiofailed=\u00c9chec de la lecture du fichier audio
 error.cache.notthere=Le dossier du cache n'a pas \u00e9t\u00e9 trouv\u00e9
 error.cache.empty=Le dossier du cache est vide
 error.cache.cannotdelete=Effacement des dalles impossible
 error.tracksplit.nosplit=Impossible de s\u00e9parer les segments
+error.downloadsrtm.nocache=Les fichiers ne peuvent pas \u00eatre sauvegard\u00e9s.\nV\u00e9rifiez le cache.
+error.sewsegments.nothingdone=Aucune fusion n'a \u00e9t\u00e9 possible.\nIl y a %d segments dans la trace.
index 359ad21537623c10e61c0744bf572b9fe6e11497..c3e9b1b698c2546094a462c726f40286c53f06ea 100644 (file)
@@ -1,5 +1,5 @@
 # Text entries for the GpsPrune application
-# Hungarian entries thanks to Gy\u00f6rgy Ball\u00f3, Peter Bathory and Peter Gervai
+# Hungarian entries thanks to Gy\u00f6rgy Ball\u00f3, P\u00e9ter B\u00e1thory and Peter Gervai
 
 # Menu entries
 menu.file=F\u00e1jl
@@ -12,7 +12,6 @@ menu.track=Nyomvonal
 menu.track.undo=Visszavon\u00e1s
 menu.track.clearundo=Visszavon\u00e1si lista t\u00f6rl\u00e9se
 menu.track.markrectangle=N\u00e9gyzeten bel\u00fcli pontok megjel\u00f6l\u00e9se
-function.deletemarked=Jel\u00f6lt pontok t\u00f6rl\u00e9se
 menu.range=Tartom\u00e1ny
 menu.range.all=Mindet kijel\u00f6l
 menu.range.none=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
@@ -36,9 +35,10 @@ menu.view.browser.openstreetmap=OpenStreetMap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Yahoo! Maps
 menu.view.browser.bing=Bing Maps
+menu.view.browser.inlinemap=Inlinemap.net
+menu.view.browser.graphhopper=GraphHopper
 menu.settings=Be\u00e1ll\u00edt\u00e1sok
 menu.settings.onlinemode=T\u00e9rk\u00e9pek bet\u00f6lt\u00e9se internetr\u0151l
-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
 
@@ -73,6 +73,7 @@ shortcut.menu.file.save=S
 shortcut.menu.track.undo=Z
 shortcut.menu.track.compress=C
 shortcut.menu.range.all=A
+shortcut.menu.point.edit=E
 shortcut.menu.help.help=H
 
 # Functions
@@ -86,6 +87,7 @@ function.exportpov=Export\u00e1l\u00e1s POV-ba
 function.exportimage=Export\u00e1l\u00e1s k\u00e9pbe
 function.editwaypointname=\u00datpont nev\u00e9nek szerkeszt\u00e9se
 function.compress=Nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se
+function.deletemarked=Jel\u00f6lt pontok t\u00f6rl\u00e9se
 function.marklifts=S\u00edliftek megjel\u00f6l\u00e9se
 function.deleterange=Tartom\u00e1ny t\u00f6rl\u00e9se
 function.croptrack=Nyomvonal k\u00f6rbev\u00e1g\u00e1sa
@@ -98,10 +100,12 @@ function.convertnamestotimes=\u00datpontok neveinek konvert\u00e1l\u00e1sa id\u0
 function.deletefieldvalues=Mez\u0151 \u00e9rt\u00e9keinek t\u00f6rl\u00e9se
 function.findwaypoint=\u00datpont keres\u00e9se
 function.pastecoordinates=\u00daj koordin\u00e1t\u00e1k megad\u00e1sa
+function.pastecoordinatelist=Adja meg a koordin\u00e1t\u00e1k list\u00e1j\u00e1t
+function.enterpluscode=Pluszk\u00f3d megad\u00e1sa
 function.charts=Diagramok
 function.show3d=3D n\u00e9zet
 function.distances=T\u00e1vols\u00e1gok
-function.fullrangedetails=Teljes tartom\u00e1ny r\u00e9szletei
+function.viewfulldetails=\u00d6sszes r\u00e9szlet
 function.estimatetime=Becs\u00fclt id\u0151
 function.learnestimationparams=Id\u0151becsl\u00e9s tanul\u00e1s\u00e1nak param\u00e9terei
 function.autoplay=Nyomvonal lej\u00e1tsz\u00e1sa
@@ -110,17 +114,19 @@ function.setpaths=Program\u00fatvonalak be\u00e1ll\u00edt\u00e1sa
 function.selectsegment=Aktu\u00e1lis szakasz kiv\u00e1laszt\u00e1sa
 function.splitsegments=Nyomvonal kett\u00e9v\u00e1g\u00e1sa szakaszokk\u00e1
 function.sewsegments=Nyomvonalszakaszok \u00f6sszevon\u00e1sa
-function.getgpsies=Gpsies nyomvonalak let\u00f6lt\u00e9se
-function.uploadgpsies=Nyomvonal felt\u00f6lt\u00e9se Gpsiesra
+function.createmarkerwaypoints=\u00datpont jel\u00f6l\u0151k k\u00e9sz\u00edt\u00e9se
 function.lookupsrtm=Magass\u00e1gok let\u00f6lt\u00e9se SRTM-r\u0151l
 function.downloadsrtm=SRTM csemp\u00e9k let\u00f6lt\u00e9se
 function.getwikipedia=K\u00f6zeli Wikip\u00e9dia sz\u00f3cikkek let\u00f6lt\u00e9se
 function.searchwikipedianames=Keres\u00e9s a Wikip\u00e9di\u00e1ban n\u00e9v szerint
+function.searchosmpois=K\u00f6zeli OSM pontok
 function.searchopencachingde=Keres\u00e9s az OpenCaching.de-n
 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.projectpoint=Pont vet\u00edt\u00e9se
 function.setcolours=Sz\u00ednek be\u00e1ll\u00edt\u00e1sa
+function.setdisplaysettings=Megjelen\u00edt\u00e9s be\u00e1ll\u00edt\u00e1sa
 function.setlanguage=Nyelv be\u00e1ll\u00edt\u00e1sa
 function.connecttopoint=Kapcsol\u00e1s ponthoz
 function.disconnectfrompoint=Lev\u00e1laszt\u00e1s pontr\u00f3l
@@ -145,6 +151,7 @@ function.diskcache=T\u00e9rk\u00e9pek ment\u00e9se lemezre
 function.managetilecache=Csempegyors\u00edt\u00f3t\u00e1r kezel\u00e9se
 function.getweatherforecast=Id\u0151j\u00e1r\u00e1s el\u0151rejelz\u00e9s
 function.setaltitudetolerance=Magass\u00e1gi t\u0171r\u00e9s be\u00e1ll\u00edt\u00e1sa
+function.selecttimezone=Id\u0151z\u00f3na be\u00e1ll\u00edt\u00e1sa
 
 # Dialogs
 dialog.exit.confirm.title=Kil\u00e9p\u00e9s a GpsPrune-b\u00f3l
@@ -173,7 +180,7 @@ dialog.openoptions.deliminfo.norecords=Nincsenek rekordok
 dialog.openoptions.altitudeunits=Magass\u00e1g m\u00e9rt\u00e9kegys\u00e9ge
 dialog.openoptions.speedunits=Sebess\u00e9g m\u00e9rt\u00e9kegys\u00e9ge
 dialog.openoptions.vertspeedunits=F\u00fcgg\u0151leges sebess\u00e9g m\u00e9rt\u00e9kegys\u00e9ge
-dialog.openoptions.vspeed.positiveup=Pozit\u00edv sebess\u00e9g m\u00e9rt\u00e9kegys\u00e9ge
+dialog.openoptions.vspeed.positiveup=Pozit\u00edv sebess\u00e9g felfel\u00e9
 dialog.openoptions.vspeed.positivedown=Pozit\u00edv sebess\u00e9g lefel\u00e9
 dialog.open.contentsdoubled=Ez a f\u00e1jl minden egyes pont k\u00e9t p\u00e9ld\u00e1ny\u00e1t tartalmazza,\negyszer mint \u00fatpont, m\u00e1sodszor mint nyompont.
 dialog.selecttracks.intro=Nyomvonal vagy nyomvonalak kiv\u00e1laszt\u00e1sa bet\u00f6lt\u00e9shez
@@ -364,24 +371,14 @@ dialog.gpsies.column.length=Hossz
 dialog.gpsies.description=Le\u00edr\u00e1s
 dialog.gpsies.nodescription=Nincs le\u00edr\u00e1s
 dialog.gpsies.nonefound=Nem tal\u00e1lhat\u00f3 nyomvonal
-dialog.gpsies.username=Gpsies felhaszn\u00e1l\u00f3n\u00e9v
-dialog.gpsies.password=Gpsies jelsz\u00f3
-dialog.gpsies.keepprivate=A nyomvonal maradjon priv\u00e1t
-dialog.gpsies.confirmopenpage=Megnyitja a weboldalt a felt\u00f6lt\u00f6tt nyomvonal sz\u00e1m\u00e1ra?
-dialog.gpsies.activities=Tev\u00e9kenys\u00e9gt\u00edpusok
-dialog.gpsies.activity.trekking=T\u00far\u00e1z\u00e1s
-dialog.gpsies.activity.walking=S\u00e9ta
-dialog.gpsies.activity.jogging=Fut\u00e1s
-dialog.gpsies.activity.biking=Ker\u00e9kp\u00e1roz\u00e1s
-dialog.gpsies.activity.motorbiking=Motorker\u00e9kp\u00e1roz\u00e1s
-dialog.gpsies.activity.snowshoe=H\u00f3talpas s\u00e9ta
-dialog.gpsies.activity.sailing=Vitorl\u00e1z\u00e1s
-dialog.gpsies.activity.skating=Korcsoly\u00e1z\u00e1s
 dialog.mapillary.nonefound=Nem tal\u00e1lhat\u00f3k f\u00e9nyk\u00e9pek
 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.osmpois.column.name=N\u00e9v
+dialog.osmpois.column.type=T\u00edpus
+dialog.osmpois.nonefound=Nem tal\u00e1lhat\u00f3k pontok
 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?
@@ -441,6 +438,10 @@ 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.pastecoordinatelist.desc=Adja meg az \u00faj pontok koordin\u00e1t\u00e1it, soronk\u00e9nt egy pont\u00e9t
+dialog.pluscode.desc=Adja meg, vagy m\u00e1soja ide a pluszk\u00f3dot
+dialog.pluscode.code=Pluszk\u00f3d
+dialog.pluscode.nothingfound=Ellen\u0151rizze a k\u00f3dot, vagy pr\u00f3b\u00e1lja \u00fajra
 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
@@ -480,25 +481,9 @@ dialog.keys.intro=A k\u00f6vetkez\u0151 gyorsbillenty\u0171k haszn\u00e1lhat\u00
 dialog.keys.keylist=<table><tr><td>Ny\u00edlbillenty\u0171k</td><td>T\u00e9rk\u00e9p mozgat\u00e1sa balra, jobbra, fel, le</td></tr><tr><td>Ctrl + bal, jobb ny\u00edl</td><td>El\u0151z\u0151 vagy k\u00f6vetkez\u0151 pont kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Ctrl + fel, le ny\u00edl</td><td>Nagy\u00edt\u00e1s vagy kicsiny\u00edt\u00e9s</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>El\u0151z\u0151, k\u00f6vetkez\u0151 szakasz kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Ctrl + Home, End</td><td>Els\u0151, utols\u00f3 pont kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Del</td><td>Jelenlegi pont t\u00f6rl\u00e9se</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=A k\u00f6vetkez\u0151 be\u00e1ll\u00edt\u00e1sok menthet\u0151k egy konfigur\u00e1ci\u00f3s f\u00e1jlba
-dialog.saveconfig.prune.trackdirectory=Nyomvonalak k\u00f6nyvt\u00e1ra
-dialog.saveconfig.prune.photodirectory=F\u00e9nyk\u00e9pek k\u00f6nyvt\u00e1ra
-dialog.saveconfig.prune.languagecode=Nyelv k\u00f3dja (HU)
-dialog.saveconfig.prune.languagefile=Nyelvi f\u00e1jl
-dialog.saveconfig.prune.gpsdevice=GPS eszk\u00f6z
-dialog.saveconfig.prune.gpsformat=GPS form\u00e1tum
-dialog.saveconfig.prune.povrayfont=Povray bet\u0171t\u00edpus
-dialog.saveconfig.prune.gnuplotpath=\u00datvonal a gnuplothoz
-dialog.saveconfig.prune.gpsbabelpath=\u00datvonal a gpsbabelhez
-dialog.saveconfig.prune.exiftoolpath=\u00datvonal az exiftoolhoz
-dialog.saveconfig.prune.mapsource=Kiv\u00e1lasztott t\u00e9rk\u00e9pforr\u00e1s
-dialog.saveconfig.prune.mapsourcelist=T\u00e9rk\u00e9pforr\u00e1sok
-dialog.saveconfig.prune.diskcache=T\u00e9rk\u00e9p-gyors\u00edt\u00f3t\u00e1r
-dialog.saveconfig.prune.kmzimagewidth=KMZ k\u00e9psz\u00e9less\u00e9g
-dialog.saveconfig.prune.colourscheme=Sz\u00edns\u00e9ma
-dialog.saveconfig.prune.linewidth=Vonalsz\u00e9less\u00e9g
-dialog.saveconfig.prune.kmltrackcolour=KML nyomvonal sz\u00edne
-dialog.saveconfig.prune.autosavesettings=Automatikus ment\u00e9s be\u00e1ll\u00edt\u00e1sai
+dialog.paths.prune.gnuplotpath=\u00datvonal a gnuplothoz
+dialog.paths.prune.gpsbabelpath=\u00datvonal a gpsbabelhez
+dialog.paths.prune.exiftoolpath=\u00datvonal az exiftoolhoz
 dialog.setpaths.intro=Ha sz\u00fcks\u00e9ges, kiv\u00e1laszthatja a k\u00fcls\u0151 alkalmaz\u00e1sok \u00fatvonalait:
 dialog.setpaths.found=\u00datvonal megtal\u00e1lhat\u00f3?
 dialog.addaltitude.noaltitudes=A kiv\u00e1lasztott tartom\u00e1ny nem tartalmaz magass\u00e1gi \u00e9rt\u00e9keket
@@ -555,6 +540,20 @@ dialog.diskcache.deleted=%d f\u00e1jl t\u00f6r\u00f6lve a gyors\u00edt\u00f3t\u0
 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.displaysettings.linewidth=Adja meg a rajzoland\u00f3 vonalak vastags\u00e1g\u00e1t a nyomvonalak sz\u00e1m\u00e1ra (1-4)
+dialog.displaysettings.antialias=\u00c9lsim\u00edt\u00e1s haszn\u00e1lata
+dialog.displaysettings.waypointicons=\u00datpont ikon
+dialog.displaysettings.wpicon.default=Alap\u00e9rtelmezett
+dialog.displaysettings.wpicon.ringpt=Kerek
+dialog.displaysettings.wpicon.plectrum=Penget\u0151
+dialog.displaysettings.wpicon.ring=Gy\u0171r\u0171
+dialog.displaysettings.wpicon.pin=Gombost\u0171
+dialog.displaysettings.size.small=Kicsi
+dialog.displaysettings.size.medium=K\u00f6zepes
+dialog.displaysettings.size.large=Nagy
+dialog.displaysettings.windowstyle=Ablak st\u00edlus (\u00fajraind\u00edt\u00e1st ig\u00e9nyel)
+dialog.displaysettings.windowstyle.default=Alap\u00e9rtelmezett
+dialog.displaysettings.windowstyle.nimbus=Nimbus
+dialog.displaysettings.windowstyle.gtk=GTK
 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
@@ -586,11 +585,23 @@ dialog.deletebydate.column.keep=Megtart
 dialog.deletebydate.column.delete=T\u00f6r\u00f6l
 dialog.setaltitudetolerance.text.metres=Az a hat\u00e1r (m\u00e9terben) ami alatt a kis s\u00fcllyed\u00e9seket \u00e9s emelked\u00e9seket figyelmen k\u00edv\u00fcl hagyjuk
 dialog.setaltitudetolerance.text.feet=Az a hat\u00e1r (l\u00e1bban) ami alatt a kis s\u00fcllyed\u00e9seket \u00e9s emelked\u00e9seket figyelmen k\u00edv\u00fcl hagyjuk
+dialog.settimezone.intro=Kiv\u00e1laszthatja, hogy az \u00fatvonalpontok id\u0151b\u00e9lyegz\u0151i milyen id\u0151z\u00f3na szerint jelenjenek meg
+dialog.settimezone.system=Rendszer id\u0151z\u00f3na haszn\u00e1lata
+dialog.settimezone.custom=Az al\u00e1bbi id\u0151z\u00f3na haszn\u00e1lata
+dialog.settimezone.list.toomany=T\u00fal sok lehet\u0151s\u00e9g
+dialog.settimezone.selectedzone=Kiv\u00e1lasztott id\u0151z\u00f3na
+dialog.settimezone.offsetfromutc=Elt\u00e9r\u00e9s az UTC-t\u0151l
 dialog.autoplay.duration=Id\u0151tartam (mp)
 dialog.autoplay.usetimestamps=Nyompontok id\u0151b\u00e9lyege alapj\u00e1n
 dialog.autoplay.rewind=Vissza az elej\u00e9re
 dialog.autoplay.pause=Sz\u00fcnet
 dialog.autoplay.play=Lej\u00e1tsz\u00e1s
+dialog.markers.halves=F\u00e9l\u00fat pontok
+dialog.markers.half.distance=F\u00e9lt\u00e1v
+dialog.markers.half.climb=F\u00e9l\u00fat a m\u00e1sz\u00e1sban
+dialog.markers.half.descent=F\u00e9l\u00fat az ereszked\u00e9sben
+dialog.projectpoint.desc=A pont vet\u00edt\u00e9s\u00e9hez adjon meg egy ir\u00e1nyt \u00e9s t\u00e1vols\u00e1got
+dialog.projectpoint.bearing=Ir\u00e1nysz\u00f6g (fok \u00e9szakt\u00f3l)
 
 # 3d window
 dialog.3d.title=GpsPrune 3D n\u00e9zet
@@ -612,7 +623,7 @@ confirm.rearrangephotos=F\u00e9nyk\u00e9pek \u00fajrarendezve
 confirm.splitsegments=%d szakasz v\u00e1g\u00e1s elv\u00e9gezve
 confirm.sewsegments=%d szakasz egyes\u00edt\u00e9se elv\u00e9gezve
 confirm.cutandmove=Kijel\u00f6l\u00e9s \u00e1thelyezve
-confirm.interpolate=Pontok hozz\u00e1adva
+confirm.pointsadded=%d pontok hozz\u00e1adva
 confirm.convertnamestotimes=\u00datpont nevei konvert\u00e1lva
 confirm.saveexif.ok=Mentve %d k\u00e9pf\u00e1jl
 confirm.undo.single=m\u0171velet visszavonva
@@ -668,7 +679,6 @@ button.selectall=Mindent kijel\u00f6l
 button.selectnone=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
 button.preview=El\u0151n\u00e9zet
 button.load=Bet\u00f6lt\u00e9s
-button.upload=Felt\u00f6lt\u00e9s
 button.guessfields=Mez\u0151k kital\u00e1l\u00e1sa
 button.showwebpage=Weboldal megjelen\u00edt\u00e9se
 button.check=Ellen\u0151rz\u00e9s
@@ -742,6 +752,7 @@ map.overzoom=Nem \u00e9rhet\u0151 el t\u00e9rk\u00e9p ezen a nagy\u00edt\u00e1si
 # Field names
 fieldname.latitude=Sz\u00e9less\u00e9g
 fieldname.longitude=Hossz\u00fas\u00e1g
+fieldname.coordinates=Koordin\u00e1t\u00e1k
 fieldname.altitude=Magass\u00e1g
 fieldname.timestamp=Id\u0151
 fieldname.time=Id\u0151
@@ -756,6 +767,7 @@ fieldname.duration=Id\u0151tartam
 fieldname.speed=Sebess\u00e9g
 fieldname.verticalspeed=F\u00fcgg\u0151leges sebess\u00e9g
 fieldname.description=Le\u00edr\u00e1s
+fieldname.comment=Megjegyz\u00e9s
 fieldname.mediafilename=F\u00e1jln\u00e9v
 
 # Measurement units
@@ -783,9 +795,9 @@ units.feetpersec.short=ft/s
 units.hours=\u00f3ra
 units.minutes=perc
 units.seconds=m\u00e1sodperc
-units.degminsec=Sz\u00f6g-sz\u00f6gperc-sz\u00f6gm\u00e1sodperc
-units.degmin=Sz\u00f6g-sz\u00f6gperc
-units.deg=Sz\u00f6g
+units.degminsec=Fok-perc-m\u00e1sodperc
+units.degmin=Fok-perc
+units.deg=Fok
 units.iso8601=ISO 8601
 units.degreescelsius=Celsius
 units.degreescelsius.short=\u00b0C
@@ -855,6 +867,7 @@ error.load.nopoints=Nem tal\u00e1lhat\u00f3 koordin\u00e1tainform\u00e1ci\u00f3
 error.load.unknownxml=Ismeretlen xml form\u00e1tum:
 error.load.noxmlinzip=Nem tal\u00e1lhat\u00f3 xml f\u00e1jl a zip f\u00e1jlon bel\u00fcl
 error.load.othererror=Hiba a f\u00e1jl olvas\u00e1sa sor\u00e1n:
+error.load.nopointsintext=Nincs benne koordin\u00e1ta inform\u00e1ci\u00f3
 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
@@ -875,8 +888,6 @@ error.language.wrongfile=\u00dagy t\u0171nik, hogy a kiv\u00e1lasztott f\u00e1jl
 error.convertnamestotimes.nonames=A nevek nem konvert\u00e1lhat\u00f3k id\u0151adatokk\u00e1
 error.lookupsrtm.nonefound=Nem \u00e9rhet\u0151 el magass\u00e1gi \u00e9rt\u00e9k ezekhez a pontokhoz
 error.lookupsrtm.nonerequired=Az \u00f6sszes pont m\u00e1r rendelkezik magass\u00e1gadatokkal, \u00edgy nincs mit keresni
-error.gpsies.uploadnotok=A gpsies szerver a k\u00f6vetkez\u0151 \u00fczenetet adta vissza
-error.gpsies.uploadfailed=A felt\u00f6lt\u00e9s nem siker\u00fclt a k\u00f6vetkez\u0151 hib\u00e1val
 error.showphoto.failed=F\u00e9nyk\u00e9p bet\u00f6lt\u00e9se sikertelen
 error.playaudiofailed=A hangf\u00e1jl lej\u00e1tsz\u00e1sa nem siker\u00fclt
 error.cache.notthere=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem tal\u00e1lhat\u00f3
index 6648b0e136dfec7eb3f6c7e0bbc2342f91f0ec9e..38c46518b5e6434edd603a77d81ac40c050221eb 100644 (file)
@@ -97,10 +97,12 @@ function.convertnamestotimes=Converti nomi dei waypoint in orari
 function.deletefieldvalues=Cancella i valori del campo
 function.findwaypoint=Trova waypoint
 function.pastecoordinates=Aggiungi coordinate
+function.pastecoordinatelist=Inserisci lista di coordinate
+function.enterpluscode=Inserisci pluscode
 function.charts=Diagrammi
 function.show3d=Mostra in 3D
 function.distances=Mostra distanze
-function.fullrangedetails=Mostra dettagli
+function.viewfulldetails=Mostra dettagli
 function.estimatetime=Stima durata
 function.learnestimationparams=Apprendi parametri di stima
 function.autoplay=Riproduci traccia
@@ -110,10 +112,7 @@ 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 i punti Wikipedia nelle vicinanze
 function.searchwikipedianames=Cerca il nome in Wikipedia
 function.searchosmpois=Ottieni i punti OSM nelle vicinanze
@@ -148,6 +147,7 @@ function.managetilecache=Gestione del cache di tasselli
 function.getweatherforecast=Ottieni previsioni del tempo
 function.setaltitudetolerance=Configura tolleranza di altitudini
 function.selecttimezone=Seleziona fuso orario
+function.projectpoint=Proiettare il punto
 
 # Dialogs
 dialog.exit.confirm.title=Esci da GpsPrune
@@ -367,19 +367,6 @@ dialog.gpsies.column.length=Lunghezza
 dialog.gpsies.description=Descrizione
 dialog.gpsies.nodescription=Senza descrizione
 dialog.gpsies.nonefound=Nessuna traccia trovata
-dialog.gpsies.username=Gpsies username
-dialog.gpsies.password=Gpsies password
-dialog.gpsies.keepprivate=Rendi la traccia privata
-dialog.gpsies.confirmopenpage=Apri la pagina web per caricare la traccia?
-dialog.gpsies.activities=Attivit\u00e0
-dialog.gpsies.activity.trekking=Trekking
-dialog.gpsies.activity.walking=Camminare
-dialog.gpsies.activity.jogging=Jogging
-dialog.gpsies.activity.biking=Ciclismo
-dialog.gpsies.activity.motorbiking=Motocicletta
-dialog.gpsies.activity.snowshoe=Trekking sulla neve
-dialog.gpsies.activity.sailing=Navigazione
-dialog.gpsies.activity.skating=Pattinaggio
 dialog.mapillary.nonefound=Nessuna foto trovata
 dialog.wikipedia.column.name=Titolo articolo
 dialog.wikipedia.column.distance=Distanza
@@ -447,6 +434,10 @@ 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.pastecoordinatelist.desc=Inserisci le coordinate per i nuovi punti, utilizzando un linea per punto
+dialog.pluscode.desc=Inserisci o incolla qui il pluscode
+dialog.pluscode.code=Pluscode
+dialog.pluscode.nothingfound=Per favore, controlla il codice e riprova
 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
@@ -486,25 +477,9 @@ dialog.keys.intro=Puoi utilizzare i seguenti tast di scelta rapida al posto del
 dialog.keys.keylist=<table><tr><td>Tasti freccia</td><td>Muovi mappa destra, sinistra, su, giu'</td></tr><tr><td>Ctrl + freccia destra, sinistra</td><td>Selezione punto successivo o precedente</td></tr><tr><td>Ctrl + freccia su, giu'</td><td>Zoom in o out</td></tr><tr><td>Ctrl + pagina su, giu'</td><td>Segmento successivo o precedente</tr><tr><td>Ctrl + Home, End</td><td>Punto primo o ultimo</td></tr><tr><td>Del</td><td>Cancella punto attuale</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Comando
-dialog.saveconfig.desc=Le configurazioni seguenti possono essere salvati in un file di configurazione:
-dialog.saveconfig.prune.trackdirectory=Cartella tracce
-dialog.saveconfig.prune.photodirectory=Cartella foto
-dialog.saveconfig.prune.languagecode=Codice lingua (IT)
-dialog.saveconfig.prune.languagefile=File lingua
-dialog.saveconfig.prune.gpsdevice=Nome del Dispositivo GPS
-dialog.saveconfig.prune.gpsformat=Formato GPS
-dialog.saveconfig.prune.povrayfont=Font povray
-dialog.saveconfig.prune.gnuplotpath=Path gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Path gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Path exiftool
-dialog.saveconfig.prune.mapsource=Selezionale la fonte delle mappe
-dialog.saveconfig.prune.mapsourcelist=Fonte delle mappe
-dialog.saveconfig.prune.diskcache=Cache delle mappe
-dialog.saveconfig.prune.kmzimagewidth=larghezza immagine KMZ
-dialog.saveconfig.prune.colourscheme=Schema colori
-dialog.saveconfig.prune.linewidth=Spessore linea
-dialog.saveconfig.prune.kmltrackcolour=Colore della traccia KML
-dialog.saveconfig.prune.autosavesettings=Salvare settaggi automaticamente
+dialog.paths.prune.gnuplotpath=Path gnuplot
+dialog.paths.prune.gpsbabelpath=Path gpsbabel
+dialog.paths.prune.exiftoolpath=Path exiftool
 dialog.setpaths.intro=Se necessario, puoi indicare il percorso delle applicazioni esterne:
 dialog.setpaths.found=trovato?
 dialog.addaltitude.noaltitudes=L'intervallo selezionato non contiene altitudini
@@ -571,6 +546,9 @@ dialog.displaysettings.wpicon.pin=Spillo da lavagna
 dialog.displaysettings.size.small=Piccole
 dialog.displaysettings.size.medium=Medie
 dialog.displaysettings.size.large=Grandi
+dialog.displaysettings.windowstyle=Stile della finestra (è necessario il riavvio)
+dialog.displaysettings.windowstyle.default=Normale
+dialog.displaysettings.windowstyle.nimbus=Nimbus
 dialog.downloadosm.desc=Conferma lo scarico dei dati raw OSM per l'area specificata:
 dialog.searchwikipedianames.search=Cerca per:
 dialog.weather.location=Localit\u00e0
@@ -613,6 +591,12 @@ dialog.autoplay.usetimestamps=Usa dati temporali
 dialog.autoplay.rewind=Riavvolga
 dialog.autoplay.pause=Pausa
 dialog.autoplay.play=Riproduci
+dialog.markers.halves=Punti a metà strada
+dialog.markers.half.distance=Metà della distanza
+dialog.markers.half.climb=Metà della salita
+dialog.markers.half.descent=Metà della discesa
+dialog.projectpoint.bearing=Azimut (gradi da nord)
+dialog.projectpoint.desc=Inserire l'azimut e la distanza per la proiezione
 
 # 3d window
 dialog.3d.title=Visione GpsPrune in 3D
@@ -634,7 +618,7 @@ confirm.rearrangephotos=Foto riorganizzate
 confirm.splitsegments=%d segmenti sono stati suddivisi
 confirm.sewsegments=%d segmenti sono stati raggruppati
 confirm.cutandmove=Selezione spostata
-confirm.interpolate=Aggiungi punto
+confirm.pointsadded=Aggiungi %d punto
 confirm.convertnamestotimes=Nome del waypoint convertito
 confirm.saveexif.ok=Salvato %d foto
 confirm.undo.single=operazione annullate
@@ -653,7 +637,6 @@ confirm.running=Operazione in corso...
 confirm.lookupsrtm=Trovato %d valori di quota
 confirm.downloadsrtm=Scarica %d file nella cache
 confirm.downloadsrtm.1=Scarica %d file nella cache
-confirm.downloadsrtm.none=Nessun file scaricato, erano gi\u00e0 presenti nella cache
 confirm.deletefieldvalues=Valori del campo cancellati
 confirm.audioload=Ripresa audio aggiunta
 confirm.correlateaudios.single=la ripresa audio era correlata
@@ -663,7 +646,7 @@ confirm.correlateaudios.multi=le riprese audio erano correlate
 tip.title=Consiglio
 tip.useamapcache=Usando una cache della mappa (Preferenze -> Salva mappe su disco)\npuoi accelerare la visualizzazione e ridurre il traffico.
 tip.learntimeparams=I risultati saranno pi\u00f9 precisi usando\nTraccia -> Apprendi parametri di stima\ncon le tue tracce.
-tip.downloadsrtm=Puoi accelerare questa funzione usando\nOnline -> Scarica file da SRTM\nper salvare i dati nella cache.
+tip.downloadsrtm=Puoi accelerare questa funzione salvare i dati nella cache.
 tip.usesrtmfor3d=La traccia non include informazioni sull'altitudine.\nPuoi utilizzare la funzione SRTM per ottenere le altitudini\nper la visione 3D.
 tip.manuallycorrelateone=Con il collegamento manuale di almeno una foto, lo scarto di orario viene calcolato per te
 
@@ -690,7 +673,6 @@ button.selectall=Seleziona tutto
 button.selectnone=Deseleziona tutto
 button.preview=Anteprima
 button.load=Carica
-button.upload=Caricato
 button.guessfields=Campi soluzione
 button.showwebpage=Mostra pagina
 button.check=Controlla
@@ -764,6 +746,7 @@ map.overzoom=Mappa non disponibile a questo livello di zoom
 # Field names
 fieldname.latitude=Latitudine
 fieldname.longitude=Longitudine
+fieldname.coordinates=Coordinate
 fieldname.altitude=Altitudine
 fieldname.timestamp=Dati temporali
 fieldname.time=Tempo
@@ -778,6 +761,7 @@ fieldname.duration=Durata
 fieldname.speed=Velocit\u00e0
 fieldname.verticalspeed=Velocit\u00e0 verticale
 fieldname.description=Descrizione
+fieldname.comment=Commento
 fieldname.mediafilename=File
 
 # Measurement units
@@ -877,6 +861,7 @@ error.load.nopoints=Non ci sono coordinate nel file
 error.load.unknownxml=Formato xml non riconosciuto:
 error.load.noxmlinzip=File xml non trovato all'interno del zip file
 error.load.othererror=Errore nella lettura del file:
+error.load.nopointsintext=Non ci sono coordinate
 error.jpegload.dialogtitle=Errore nel caricamento delle foto
 error.jpegload.nofilesfound=File non trovato
 error.jpegload.nojpegsfound=File jpeg non trovato
@@ -897,8 +882,6 @@ error.language.wrongfile=Il file selezionato non sembra essere un file di lingua
 error.convertnamestotimes.nonames=Nomi non convertibili in orari
 error.lookupsrtm.nonefound=Valori di quota non trovati
 error.lookupsrtm.nonerequired=Tutti i punti hanno gi\u00e0 una quota, non c'\u00e8 niente da cercare
-error.gpsies.uploadnotok=Il server Gpsies ha riportato il messaggio
-error.gpsies.uploadfailed=Il caricamento \u00e8 fallito con l'errore
 error.showphoto.failed=Caricamento foto fallito
 error.playaudiofailed=Ripresa audio non riprodotta
 error.cache.notthere=Directory del cache di tasselli non trovato
index 738a1014733768153243fa4505f5a054c07bcde2..28a95843468de003ef6e9917852e6046291ed7d1 100644 (file)
@@ -93,11 +93,8 @@ function.pastecoordinates=\u65b0\u3057\u3044\u5ea7\u6a19\u3092\u5165\u529b
 function.charts=\u9ad8\u5ea6\u901f\u5ea6\u30c1\u30e3\u30fc\u30c8
 function.show3d=3-D\u30d3\u30e5\u30fc
 function.distances=\u8ddd\u96e2
-function.fullrangedetails=\u5168\u7bc4\u56f2\u8a73\u7d30
 function.setmapbg=\u80cc\u666f\u5730\u56f3
 function.setpaths=\u5916\u90e8\u30d7\u30ed\u30b0\u30e9\u30e0\u30d1\u30b9\u3092\u8a2d\u5b9a
-function.getgpsies=Gpsies\u30c8\u30e9\u30c3\u30af\u3092\u5f97\u308b
-function.uploadgpsies=Gpsies\u306b\u30c8\u30e9\u30c3\u30af\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9
 function.lookupsrtm=SRTM\u304b\u3089\u6a19\u9ad8\u3092\u53d6\u5f97\u3059\u308b
 function.getwikipedia=Wikipedia\u304b\u3089\u5468\u56f2\u306e\u8a18\u4e8b\u3092\u53d6\u5f97\u3059\u308b
 function.searchwikipedianames=\u540d\u524d\u3067Wikipedia\u3092\u691c\u7d22
@@ -270,17 +267,6 @@ dialog.gpsies.column.length=\u9577\u3055
 dialog.gpsies.description=\u8a18\u8ff0
 dialog.gpsies.nodescription=\u8a18\u8ff0\u304c\u3042\u308a\u307e\u305b\u3093
 dialog.gpsies.nonefound=\u30c8\u30e9\u30c3\u30af\u304c\u3042\u308a\u307e\u305b\u3093
-dialog.gpsies.username=Gpsies\u306e\u30e6\u30fc\u30b6\u30fc\u540d
-dialog.gpsies.password=Gpsies\u306e\u30d1\u30b9\u30ef\u30fc\u30c9
-dialog.gpsies.activities=\u6d3b\u52d5\u306b\u9069\u3057\u3066
-dialog.gpsies.activity.trekking=\u30cf\u30a4\u30ad\u30f3\u30b0
-dialog.gpsies.activity.walking=\u30a6\u30a9\u30fc\u30ad\u30f3\u30b0
-dialog.gpsies.activity.jogging=\u5b9f\u884c\u4e2d
-dialog.gpsies.activity.biking=\u30b5\u30a4\u30af\u30ea\u30f3\u30b0
-dialog.gpsies.activity.motorbiking=\u30e2\u30fc\u30bf\u30fc\u30d0\u30a4\u30af
-dialog.gpsies.activity.snowshoe=\u30b9\u30ce\u30fc\u30b7\u30e5\u30fc\u30a4\u30f3\u30b0
-dialog.gpsies.activity.sailing=\u30bb\u30fc\u30ea\u30f3\u30b0
-dialog.gpsies.activity.skating=\u30d5\u30a3\u30ae\u30e5\u30a2\u30b9\u30b1\u30fc\u30c8
 dialog.wikipedia.column.name=Wikipedia\u8a18\u4e8b\u540d
 dialog.wikipedia.column.distance=\u8ddd\u96e2
 dialog.correlate.notimestamps=\u30c7\u30fc\u30bf\u30dd\u30a4\u30f3\u30c8\u306b\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u304c\u306a\u3044\u306e\u3067\u3001\u5199\u771f\u3092\u95a2\u9023\u4ed8\u3051\u3089\u308c\u308b\u7269\u304c\u3042\u308a\u307e\u305b\u3093\u3002
@@ -369,24 +355,9 @@ dialog.keys.intro=\u30de\u30a6\u30b9\u306e\u4ee3\u308f\u308a\u306b\u6b21\u306e\u
 dialog.keys.keylist=<table><tr><td>\u77e2\u5370\u30ad\u30fc</td><td>\u5730\u56f3\u3092\u4e0a\u4e0b\u5de6\u53f3\u306b\u79fb\u52d5</td></tr><tr><td>Ctrl + \u5de6\u30fb\u53f3\u77e2\u5370</td><td>\u524d\u30fb\u6b21\u306e\u70b9\u3092\u9078\u629e</td></tr><tr><td>Ctrl + \u4e0a\u30fb\u4e0b\u77e2\u5370</td><td>\u62e1\u5927\u30fb\u7e2e\u5c0f</td></tr><tr><td>Del</td><td>\u73fe\u5728\u306e\u70b9\u3092\u524a\u9664</td></tr></table>
 dialog.keys.normalmodifier=Ctrl\u30ad\u30fc
 dialog.keys.macmodifier=Command\u30ad\u30fc
-dialog.saveconfig.desc=\u4e0b\u8a18\u306e\u8a2d\u5b9a\u304c\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059
-dialog.saveconfig.prune.trackdirectory=\u30c8\u30e9\u30c3\u30af\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
-dialog.saveconfig.prune.photodirectory=\u5199\u771f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
-dialog.saveconfig.prune.languagecode=\u8a00\u8a9e\u30b3\u30fc\u30c9(JA)
-dialog.saveconfig.prune.languagefile=\u8a00\u8a9e\u30d5\u30a1\u30a4\u30eb
-dialog.saveconfig.prune.gpsdevice=GPS\u30c7\u30d0\u30a4\u30b9
-dialog.saveconfig.prune.gpsformat=GPS\u30d5\u30a9\u30fc\u30de\u30c3\u30c8
-dialog.saveconfig.prune.povrayfont=Povray \u30d5\u30a9\u30f3\u30c8
-dialog.saveconfig.prune.gnuplotpath=gnuplot\u3078\u306e\u30d1\u30b9
-dialog.saveconfig.prune.gpsbabelpath=gpsbabel\u3078\u306e\u30d1\u30b9
-dialog.saveconfig.prune.exiftoolpath=exiftool\u3078\u306e\u30d1\u30b9
-dialog.saveconfig.prune.mapsource=\u30de\u30c3\u30d7\u30fb\u30bd\u30fc\u30b9\u3092\u9078\u629e
-dialog.saveconfig.prune.mapsourcelist=\u30de\u30c3\u30d7\u30fb\u30bd\u30fc\u30b9
-dialog.saveconfig.prune.diskcache=\u30de\u30c3\u30d7\u306e\u30ad\u30e3\u30c3\u30b7\u30e5
-dialog.saveconfig.prune.kmzimagewidth=KML \u753b\u50cf\u5e45
-dialog.saveconfig.prune.colourscheme=\u8272\u306e\u30b9\u30ad\u30fc\u30e0
-dialog.saveconfig.prune.linewidth=\u7dda\u306e\u5e45
-dialog.saveconfig.prune.kmltrackcolour=KML \u30c8\u30e9\u30c3\u30af\u306e\u8272
+dialog.paths.prune.gnuplotpath=gnuplot\u3078\u306e\u30d1\u30b9
+dialog.paths.prune.gpsbabelpath=gpsbabel\u3078\u306e\u30d1\u30b9
+dialog.paths.prune.exiftoolpath=exiftool\u3078\u306e\u30d1\u30b9
 dialog.setpaths.intro=\u5fc5\u8981\u306a\u3089\u3001\u5916\u90e8\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30b9\u3092\u9078\u3076\u4e8b\u304c\u3067\u304d\u307e\u3059
 dialog.addaltitude.noaltitudes=\u9078\u629e\u7bc4\u56f2\u306f\u3001\u9ad8\u5ea6\u3092\u542b\u3093\u3067\u307e\u305b\u3093\u3002
 dialog.addaltitude.desc=\u52a0\u3048\u305f\u9ad8\u5ea6\u504f\u4f4d
@@ -475,7 +446,6 @@ button.selectall=\u5168\u9078\u629e
 button.selectnone=\u9078\u629e\u89e3\u9664
 button.preview=\u30d7\u30ec\u30d3\u30e5\u30fc
 button.load=\u8aad\u307f\u8fbc\u307f
-button.upload=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9
 button.guessfields=\u30d5\u30a3\u30fc\u30eb\u30c9\u4e88\u6e2c
 button.showwebpage=\u30a6\u30a7\u30d6\u30da\u30fc\u30b8\u3092\u8868\u793a
 button.check=\u691c\u67fb
@@ -652,6 +622,4 @@ error.language.wrongfile=\u9078\u629e\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\
 error.convertnamestotimes.nonames=\u3069\u306e\u540d\u524d\u3082\u6642\u9593\u306b\u5909\u63db\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
 error.lookupsrtm.nonefound=\u3069\u306e\u6a19\u9ad8\u5024\u3082\u3053\u308c\u3089\u306e\u30dd\u30a4\u30f3\u30c8\u304b\u3089\u53d6\u5f97\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002
 error.lookupsrtm.nonerequired=\u5168\u3066\u306e\u30dd\u30a4\u30f3\u30c8\u306f\u6a19\u9ad8\u5024\u3092\u6301\u3063\u3066\u3044\u308b\u305f\u3081\u3001\u691c\u7d22\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002
-error.gpsies.uploadnotok=Gpsies\u30b5\u30fc\u30d0\u30fc\u304c\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u8fd4\u3057\u307e\u3057\u305f\u3002
-error.gpsies.uploadfailed=\u30a8\u30e9\u30fc\u306e\u305f\u3081\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f
 error.playaudiofailed=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u306e\u518d\u751f\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
index b748f07fee5812a5dbbef3395982033b70e38cf1..d553df0f4a82f25829ce76374ba6cd1419476522 100644 (file)
@@ -88,11 +88,8 @@ function.pastecoordinates=\uc0c8 \uc88c\ud45c \ub123\uae30
 function.charts=\ucc28\ud2b8
 function.show3d=3\ucc28\uc6d0 \ubcf4\uae30
 function.distances=\uac70\ub9ac
-function.fullrangedetails=\uc5f0\uacb0\uc120 \uc0c1\uc138 \uc815\ubcf4 \ubcf4\uae30
 function.setmapbg=\ubc30\uacbd \uc9c0\ub3c4 \uc9c0\uc815
 function.setpaths=\uc678\ubd80\ud504\ub85c\uadf8\ub7a8 \uc9c0\uc815
-function.getgpsies=gpsies\uc5d0\uc11c \ud2b8\ub799\ubaa9\ub85d \uc5bb\uae30
-function.uploadgpsies=gpsies\ub85c \ud2b8\ub799 \uc62c\ub9ac\uae30
 function.lookupsrtm=SRTM\uc5d0\uc11c \uace0\ub3c4 \ucc3e\uae30
 function.getwikipedia=\uc704\ud0a4\ud53c\ub514\uc544\uc5d0\uc11c \uadfc\ucc98 \uc815\ubcf4 \ucc3e\uae30
 function.searchwikipedianames=\uc774\ub984\uc73c\ub85c \uc704\ud0a4\ud53c\ub514\uc544 \uac80\uc0c9
@@ -263,19 +260,6 @@ dialog.gpsies.column.length=\uae38\uc774
 dialog.gpsies.description=\uc124\uba85
 dialog.gpsies.nodescription=\uc124\uba85 \uc5c6\uc74c
 dialog.gpsies.nonefound=\ud2b8\ub799\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc74c
-dialog.gpsies.username=Gpsies username
-dialog.gpsies.password=Gpsies \ube44\ubc00\ubc88\ud638
-dialog.gpsies.keepprivate=\ud2b8\ub799 \uac1c\uc778\uc815\ubcf4\ubcf4\ud638
-dialog.gpsies.confirmopenpage=\uc5c5\ub85c\ub4dc\ud55c \ud2b8\ub799\uc744 \uc6f9\ud398\uc774\uc9c0\uc5d0\uc11c \ubcf4\uc2dc\uaca0\uc5b4\uc694?
-dialog.gpsies.activities=\ud65c\ub3d9 \ud615\ud0dc
-dialog.gpsies.activity.trekking=\ub3c4\ubcf4\uc5ec\ud589
-dialog.gpsies.activity.walking=\uac77\uae30
-dialog.gpsies.activity.jogging=\ub2ec\ub9ac\uae30
-dialog.gpsies.activity.biking=\uc790\uc804\uac70\ud0c0\uae30
-dialog.gpsies.activity.motorbiking=\uc624\ud1a0\ubc14\uc774\ud0c0\uae30
-dialog.gpsies.activity.snowshoe=\ub208\uae38\uac77\uae30
-dialog.gpsies.activity.sailing=\ubc30\ud0c0\uae30
-dialog.gpsies.activity.skating=\uc2a4\ucf00\uc774\ud2b8\ud0c0\uae30
 dialog.wikipedia.column.name=\uac8c\uc2dc\ubb3c \uc774\ub984
 dialog.wikipedia.column.distance=\uac70\ub9ac
 dialog.correlate.notimestamps=\uc9c0\uc810 \ub370\uc774\ud130\uc5d0 \uc2dc\uac04\uc815\ubcf4\uac00 \uc5c6\uc5b4\uc11c \uc0ac\uc9c4\uc744 \uc5f0\uacb0 \ud560 \uc218 \uc5c6\uc5b4\uc694.
@@ -368,24 +352,9 @@ dialog.keys.intro=\ub9c8\uc6b0\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \ub9c8\uc2dc
 dialog.keys.keylist=<table><tr><td>\ud654\uc0b4\ud45c \ud0a4</td><td>\uc88c,\uc6b0,\uc544\ub798,\uc704\ub85c \uc9c0\ub3c4 \uc774\ub3d9</td></tr><tr><td>Ctrl + \uc67c\ucabd, \uc624\ub978\ucabd \ud654\uc0b4\ud45c</td><td>\uc9c0\uc810 \uc120\ud0dd \uc774\ub3d9</td></tr><tr><td>Ctrl + \uc704, \uc544\ub798 \ud654\uc0b4\ud45c</td><td>\uc9c0\ub3c4 \ud655\ub300 \ucd95\uc18c</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>\uc774\uc804 \uc774\ud6c4 \ubd80\ubd84 \uc120\ud0dd</td></tr><tr><td>Ctrl + Home, End</td><td>\ucc98\uc74c \uc9c0\uc810, \ub9c8\uc9c0\ub9c9 \uc9c0\uc810 \uc120\ud0dd</td></tr><tr><td>Del</td><td>\ud604\uc7ac \uc9c0\uc810 \uc0ad\uc81c</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=\uc544\ub798 \uc124\uc815\ub4e4\uc740 \uc124\uc815\ud30c\uc77c\uc5d0 \uc800\uc7a5\ud560 \uc218 \uc788\uc5b4\uc694:
-dialog.saveconfig.prune.trackdirectory=\ud2b8\ub809 \ud3f4\ub354
-dialog.saveconfig.prune.photodirectory=\uc0ac\uc9c4 \ud3f4\ub354
-dialog.saveconfig.prune.languagecode=\uc5b8\uc5b4\ucf54\ub4dc (KO)
-dialog.saveconfig.prune.languagefile=\uc5b8\uc5b4\ud30c\uc77c
-dialog.saveconfig.prune.gpsdevice=GPS\uc7a5\uce58
-dialog.saveconfig.prune.gpsformat=GPS \ud615\uc2dd
-dialog.saveconfig.prune.povrayfont=Povray \uae00\uaf34
-dialog.saveconfig.prune.gnuplotpath=gnjuplot \uacbd\ub85c
-dialog.saveconfig.prune.gpsbabelpath=gpsbabel \uacbd\ub85c
-dialog.saveconfig.prune.exiftoolpath=exiftool \uacbd\ub85c
-dialog.saveconfig.prune.mapsource=\uc120\ud0dd\ub41c \uc9c0\ub3c4 \uc704\uce58
-dialog.saveconfig.prune.mapsourcelist=\uc9c0\ub3c4 \uc18c\uc2a4
-dialog.saveconfig.prune.diskcache=\uc9c0\ub3c4 \uce90\uc2dc
-dialog.saveconfig.prune.kmzimagewidth=KMZ \uc774\ubbf8\uc9c0 \ub113\uc774
-dialog.saveconfig.prune.colourscheme=\uc0c9 \uad6c\uc131
-dialog.saveconfig.prune.linewidth=\ud2b8\ub799\uc120 \ub450\uaed8
-dialog.saveconfig.prune.kmltrackcolour=KML \ud2b8\ub799 \uc0c9
+dialog.paths.prune.gnuplotpath=gnjuplot \uacbd\ub85c
+dialog.paths.prune.gpsbabelpath=gpsbabel \uacbd\ub85c
+dialog.paths.prune.exiftoolpath=exiftool \uacbd\ub85c
 dialog.setpaths.intro=\uc678\ubd80 \ud504\ub85c\uadf8\ub7a8\uc758 \uacbd\ub85c\ub97c \uc120\ud0dd\ud560 \uc218 \uc788\uc5b4\uc694.
 dialog.setpaths.found=\uacbd\ub85c\ub97c \ucc3e\uc73c\uc168\ub098\uc694?
 dialog.addaltitude.noaltitudes=\uc120\ud0dd\ub41c \ubc94\uc704\uc5d0 \uace0\ub3c4\uac00 \ud3ec\ud568\ub418\uc5b4\uc788\uc9c0 \uc54a\ub124\uc694.
@@ -483,7 +452,6 @@ button.selectall=\uc804\ubd80\uc120\ud0dd
 button.selectnone=\uc544\ubb34\uac83\ub3c4 \uc120\ud0dd\ud558\uc9c0\uc54a\uae30
 button.preview=\ubbf8\ub9ac\ubcf4\uae30
 button.load=\ubd88\ub7ec\uc624\uae30
-button.upload=\uc62c\ub9ac\uae30
 button.guessfields=\ucd94\uce21 \ud544\ub4dc
 button.showwebpage=\uc6f9\ud398\uc774\uc9c0 \ubcf4\uae30
 button.check=\uccb4\ud06c
@@ -658,6 +626,4 @@ error.language.wrongfile=\uc120\ud0dd\ud558\uc2e0 \ud30c\uc77c\uc774 GpsPrune\uc
 error.convertnamestotimes.nonames=\uc774\ub984\uc744 \uc2dc\uac04\uc73c\ub85c \ubcc0\ud658 \ud560 \uc218 \uc5c6\uc5b4\uc694./n(\uacbd\uc720\uc9c0\uc774\ub984\uc744 \ucc3e\uc9c0 \ubabb\ud588\uac70\ub098 \ubcc0\ud658\ud560 \uc218 \uc5c6\ub294 \uacbd\uc6b0\uc5d0\uc694.)
 error.lookupsrtm.nonefound=\uc774 \uc9c0\uc810\ub4e4\uc5d0\uc11c \uace0\ub3c4\uac12\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc5b4\uc694./n\uc774 \uc601\uc5ed\uc5d0\uc11c \ud0c0\uc77c\uc774 \uc5c6\uac70\ub098, \uc790\ub8cc\uac00 \uc54c\uc218 \uc5c6\ub294 \uac83\ub4e4\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294 \uacbd\uc720.
 error.lookupsrtm.nonerequired=\ubaa8\ub4e0 \uc9c0\uc810\uc774 \uace0\ub3c4 \uc815\ubcf4\uac00 \uc788\uc5b4\uc11c, \ucc3e\uc744\uac8c \uc544\ubb34\uac83\ub3c4 \uc5c6\ub124\uc694.
-error.gpsies.uploadnotok=gpsies \uc11c\ubc84\uac00 \uba54\uc138\uc9c0\ub97c \ub2e4\uc74c\uacfc \uac19\uc740 \ub3cc\ub824\uc90d\ub2c8\ub2e4
-error.gpsies.uploadfailed=\ub2e4\uc74c\uacfc \uac19\uc740 \uc774\uc720\ub85c \uc5c5\ub85c\ub4dc\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4
 error.playaudiofailed=\uc18c\ub9ac\ud30c\uc77c \uc7ac\uc0dd \uc2e4\ud328
index 2e0f72772922487200bb5f4528489b02248e6f63..009d2488cc268dfa456dd8b57b1de767310525ae 100644 (file)
@@ -1,7 +1,7 @@
 # Text entries for the GpsPrune application
 # Dutch entries thanks to Jeroen
 
-# Menu entries
+## Menu entries
 menu.file=Bestand
 menu.file.addphotos=Foto's toevoegen
 menu.file.recentfiles=Onlangs geopend
@@ -12,7 +12,6 @@ menu.track=Route
 menu.track.undo=Ongedaan maken
 menu.track.clearundo=Ongedaan-maken lijst wissen
 menu.track.markrectangle=Makeer alle punten in een vierkant
-function.deletemarked=Verwijderen gemarkeerde punten
 menu.range=Reeks
 menu.range.all=Selecteer alles
 menu.range.none=Selecteer geen
@@ -36,12 +35,14 @@ menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Yahoo maps
 menu.view.browser.bing=Bing maps
+menu.view.browser.inlinemap=Inline kaart
+menu.view.browser.graphhopper=GraphHopper
 menu.settings=Instellingen
 menu.settings.onlinemode=Kaarten van internet ophalen
 menu.settings.autosave=Automatisch opslaan bij afsluiten
 menu.help=Help
 
-# Popup menu for map
+## Popup menu for map
 menu.map.zoomin=Zoom +
 menu.map.zoomout=Zoom -
 menu.map.zoomfull=Zoom alles
@@ -53,7 +54,7 @@ menu.map.showmap=Toon kaart
 menu.map.showscalebar=Toon schaal
 menu.map.editmode=Wijzigen
 
-# Alt keys for menus
+## Alt keys for menus
 altkey.menu.file=F
 altkey.menu.online=O
 altkey.menu.track=R
@@ -65,7 +66,7 @@ altkey.menu.audio=A
 altkey.menu.settings=I
 altkey.menu.help=H
 
-# Ctrl shortcuts for menu items
+## Ctrl shortcuts for menu items
 shortcut.menu.file.open=O
 shortcut.menu.file.load=L
 shortcut.menu.file.save=S
@@ -75,7 +76,7 @@ shortcut.menu.range.all=A
 shortcut.menu.point.edit=W
 shortcut.menu.help.help=H
 
-# Functions
+## Functions
 function.open=Open bestand
 function.importwithgpsbabel=Bestand importeren met GPSBabel
 function.loadfromgps=Gegevens lezen van GPS
@@ -86,6 +87,7 @@ function.exportpov=Export POV
 function.exportimage=Bestand exporteren
 function.editwaypointname=Hernoem waypoint
 function.compress=Route comprimeren
+function.deletemarked=Verwijderen gemarkeerde punten
 function.marklifts=Markeer opgaande liften
 function.deleterange=Verwijder reeks
 function.croptrack=Route bijknippen
@@ -98,10 +100,12 @@ function.convertnamestotimes=Converteer waypointnamen naar tijden
 function.deletefieldvalues=Verwijder veldwaarden
 function.findwaypoint=Zoek waypoint
 function.pastecoordinates=Nieuwe co\u00f6rdinaten ingeven
+function.pastecoordinatelist=Lijst met co\u00f6rdinaten invoeren
+function.enterpluscode=Pluscode invoeren
 function.charts=Diagram
 function.show3d=3D beeld
 function.distances=Afstanden
-function.fullrangedetails=Reeks details
+function.viewfulldetails=Alle details
 function.estimatetime=Geschatte tijd
 function.learnestimationparams=Parameters voor geschatte tijd
 function.autoplay=Route automatisch afspelen
@@ -111,10 +115,7 @@ 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
@@ -122,6 +123,7 @@ 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.projectpoint=Bereken punt
 function.setcolours=Instellen kleuren
 function.setdisplaysettings=Instellen afbeeldingopties
 function.setlanguage=Instellen taal
@@ -150,7 +152,7 @@ function.getweatherforecast=Ophalen weersvoorspelling
 function.setaltitudetolerance=Instellen hoogtetolerantie
 function.selecttimezone=Instellen tijdzone
 
-# Dialogs
+## Dialogs
 dialog.exit.confirm.title=GpsPrune afsluiten
 dialog.exit.confirm.text=Uw data is niet opgeslagen. Weet u zeker dat u wilt afsluiten?
 dialog.openappend.title=Toevoegen aan bestaande data
@@ -368,19 +370,6 @@ dialog.gpsies.column.length=Lengte
 dialog.gpsies.description=Omschrijving
 dialog.gpsies.nodescription=Geen omschrivjing
 dialog.gpsies.nonefound=Geen routes gevonden
-dialog.gpsies.username=Gpsies gebruikersnaam
-dialog.gpsies.password=Gpsies wachtwoord
-dialog.gpsies.keepprivate=Houd route priv\u00e9
-dialog.gpsies.confirmopenpage=Webpagina van de ge\u00fcploade route openen?
-dialog.gpsies.activities=Aktiviteit
-dialog.gpsies.activity.trekking=Trekking
-dialog.gpsies.activity.walking=Lopen
-dialog.gpsies.activity.jogging=Hardlopen
-dialog.gpsies.activity.biking=Fietsen
-dialog.gpsies.activity.motorbiking=Motorrijden
-dialog.gpsies.activity.snowshoe=Sneeuwschoen-lopen
-dialog.gpsies.activity.sailing=Zeilen
-dialog.gpsies.activity.skating=Skating
 dialog.mapillary.nonefound=Geen foto's gevonden
 dialog.wikipedia.column.name=Artikelnaam
 dialog.wikipedia.column.distance=Afstand
@@ -448,6 +437,10 @@ 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.pastecoordinatelist.desc=Geef de co\u00f6rdinaten voor nieuwe punten met \u00e9\u00e9n punt per regel
+dialog.pluscode.desc=Type of plak hier de pluscode
+dialog.pluscode.code=Pluscode
+dialog.pluscode.nothingfound=Controleer de code en probeer het nogmaals
 dialog.help.help=Ga naar\n https://gpsprune.activityworkshop.net/\nvoor meer informatie en handleidingen.
 dialog.about.version=Versie
 dialog.about.build=Build
@@ -461,9 +454,9 @@ dialog.about.systeminfo.os=Besturingssysteem
 dialog.about.systeminfo.java=Java Runtime
 dialog.about.systeminfo.java3d=Java3d ge\u00efnstalleerd
 dialog.about.systeminfo.povray=Povray ge\u00efnstalleerd
-dialog.about.systeminfo.exiftool=Exiftool ge\u00efnstalleer
-dialog.about.systeminfo.gpsbabel=Gpsbabel ge\u00efnstalleer
-dialog.about.systeminfo.gnuplot=Gnuplot ge\u00efnstalleer
+dialog.about.systeminfo.exiftool=Exiftool ge\u00efnstalleerd
+dialog.about.systeminfo.gpsbabel=Gpsbabel ge\u00efnstalleerd
+dialog.about.systeminfo.gnuplot=Gnuplot ge\u00efnstalleerd
 dialog.about.yes=Ja
 dialog.about.no=Nee
 dialog.about.credits=Credits
@@ -487,25 +480,9 @@ dialog.keys.intro=De volgende sneltoetsen vervangen muisacties
 dialog.keys.keylist=<table><tr><td>Pijltjestoetsen</td><td>verschuif de kaart links, rechts, omhoog, omlaag</td></tr><tr><td>Ctrl + pijltje naar links, rechts</td><td>Selecteer volgende, vorige punt</td></tr><tr><td>Ctrl + pijltje omhoog, omlaag</td><td>Zoom in of uit</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Selecteer vorig, volgend segment</td></tr><tr><td>Ctrl + Home, End</td><td>Select eerste, laatste punt</td></tr><tr><td>Del</td><td>Verwijder huidige punt</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=De volgende instellingen kunnen worden opgeslagen in een configuratiebestand:
-dialog.saveconfig.prune.trackdirectory=Route map
-dialog.saveconfig.prune.photodirectory=Foto map
-dialog.saveconfig.prune.languagecode=Taalcode (NL)
-dialog.saveconfig.prune.languagefile=Taal bestand
-dialog.saveconfig.prune.gpsdevice=GPS apparaat
-dialog.saveconfig.prune.gpsformat=GPS formaat
-dialog.saveconfig.prune.povrayfont=Povray lettertype
-dialog.saveconfig.prune.gnuplotpath=Pad naar gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Pad naar gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Pad naar exiftool
-dialog.saveconfig.prune.mapsource=Geselecteerde kaartbron
-dialog.saveconfig.prune.mapsourcelist=Kaartbronnen
-dialog.saveconfig.prune.diskcache=Kaartcache
-dialog.saveconfig.prune.kmzimagewidth=KMZ afbeelding breedte
-dialog.saveconfig.prune.colourscheme=Kleurenschema
-dialog.saveconfig.prune.linewidth=Lijndikte
-dialog.saveconfig.prune.kmltrackcolour=KML routekleur
-dialog.saveconfig.prune.autosavesettings=Instellingen automatisch opslaan
+dialog.paths.prune.gnuplotpath=Pad naar gnuplot
+dialog.paths.prune.gpsbabelpath=Pad naar gpsbabel
+dialog.paths.prune.exiftoolpath=Pad naar exiftool
 dialog.setpaths.intro=Indien nodig kan je de paden naar externe applicaties kiezen:
 dialog.setpaths.found=Pad gevonden?
 dialog.addaltitude.noaltitudes=De geselecteerde reeks bevat geen hoogtes
@@ -572,6 +549,9 @@ dialog.displaysettings.wpicon.pin=Punaise
 dialog.displaysettings.size.small=Klein
 dialog.displaysettings.size.medium=Middel
 dialog.displaysettings.size.large=Groot
+dialog.displaysettings.windowstyle=Windows stijl (herstart nodig)
+dialog.displaysettings.windowstyle.default=Standaard
+dialog.displaysettings.windowstyle.nimbus=Nimbus
 dialog.downloadosm.desc=Bevestig het downloaden van ruwe OSM data voor dit gebied:
 dialog.searchwikipedianames.search=Zoeken naar:
 dialog.weather.location=Locatie
@@ -614,12 +594,18 @@ dialog.autoplay.usetimestamps=Gebruik tijdinfo van punt
 dialog.autoplay.rewind=Terug naar begin
 dialog.autoplay.pause=Pauze
 dialog.autoplay.play=Afspelen
+dialog.markers.halves=Punt op halve afstand
+dialog.markers.half.distance=Halve afstand
+dialog.markers.half.climb=Halve klim
+dialog.markers.half.descent=Halve afdaling
+dialog.projectpoint.desc=Geef de richting en afstand om dit punt te berekenen
+dialog.projectpoint.bearing=Richting (graden Noord)
 
-# 3d window
+## 3d window
 dialog.3d.title=GpsPrune in 3D
 dialog.3d.altitudefactor=Hoogte overdrijvingsfactor
 
-# Confirm messages
+## Confirm messages
 confirm.loadfile=Data van schijf geladen
 confirm.save.ok1=Succesvol opgeslagen
 confirm.save.ok2=Punten naar bestand
@@ -635,7 +621,7 @@ confirm.rearrangephotos=Foto herschikt
 confirm.splitsegments=Er zijn %d opdelingen gemaakt
 confirm.sewsegments=Er zijn %d samenvoegingen gemaakt
 confirm.cutandmove=Selectie verplaatst
-confirm.interpolate=Punten toegevoegd
+confirm.pointsadded=%d punten toegevoegd
 confirm.convertnamestotimes=Namen waypoint geconverteerd
 confirm.saveexif.ok=Opgeslagen %d foto bestanden
 confirm.undo.single=Actie geannuleerd
@@ -654,21 +640,20 @@ confirm.running=Bezig...
 confirm.lookupsrtm=Gevonden %d hoote waarden
 confirm.downloadsrtm=Er zijn %d bestanden gedownload nar de cache
 confirm.downloadsrtm.1=Er zijn %d bestanden gedownload nar de cache
-confirm.downloadsrtm.none=Geen bestanden gedownload, waren al aanwezig in de cache.
 confirm.deletefieldvalues=Veldwaarden gewist
 confirm.audioload=Audiobestanden toegevoegd
 confirm.correlateaudios.single=audiobestand gecorreleerd
 confirm.correlateaudios.multi=audiobestanden gecorreleerd
 
-# Tips, shown just once when appropriate
+## Tips, shown just once when appropriate
 tip.title=Tip
 tip.useamapcache=Door het instellen van een schijfcache (Instellingen -> Kaart opslaan op schijf)\nkan je de afbeeldsnelheid verbeteren en het netwerkverkeer verminderen.
 tip.learntimeparams=De resultaten zullen nauwkeuriger zijn als je \nRoute -> Parameters voor geschatte tijd\ngebruikt op je opgenomen routes.
-tip.downloadsrtm=Je kan dit versnellen door hier\nOnline -> Download SRTM tegels\nde data in je kaartcache op te slaan.
+tip.downloadsrtm=Je kan dit versnellen door de data in je kaartcache op te slaan.
 tip.usesrtmfor3d=Deze route heeft geen hoogten.\nJe kan de SRTM functies gebruiken om een geschatte hoogte\nop te halen voor het 3d beeld.
 tip.manuallycorrelateone=Door handmatig een foto te koppelen kan het tijdsverschil voor u berekend worden.
 
-# Buttons
+## Buttons
 button.ok=OK
 button.back=Terug
 button.next=Volgende
@@ -691,7 +676,6 @@ button.selectall=Selecteer alles
 button.selectnone=Selecteer niets
 button.preview=Voorbeeld
 button.load=Laden
-button.upload=Upload
 button.guessfields=Raad velden
 button.showwebpage=Toon webpagina
 button.check=Controleren
@@ -702,7 +686,7 @@ button.delete=Verwijderen
 button.manage=Beheer
 button.combine=Samenvoegen
 
-# File types
+## File types
 filetype.txt=TXT bestand
 filetype.jpeg=JPG bestand
 filetype.kmlkmz=KML, KMZ bestand
@@ -714,7 +698,7 @@ filetype.svg=SVG bestand
 filetype.png=PNG bestand
 filetype.audio=MP3, OGG, WAV bestanden
 
-# Display components
+## Display components
 display.nodata=Geen gegevens geladen
 display.noaltitudes=Route gegevens bevatten geen hoogte
 display.notimestamps=Route gegevens bevatten geen tijdinformatie
@@ -762,9 +746,10 @@ details.audio.file=Audiobestand
 details.audio.playing=Afspelen...
 map.overzoom=Geen kaarten beschikbaar op dit zoom-niveau
 
-# Field names
+## Field names
 fieldname.latitude=Breedtegraad
 fieldname.longitude=Lengtegraad
+fieldname.coordinates=Co\u00f6rdinaten
 fieldname.altitude=Hoogte
 fieldname.timestamp=Tijd
 fieldname.time=Tijd
@@ -779,9 +764,10 @@ fieldname.duration=Duur
 fieldname.speed=Snelheid
 fieldname.verticalspeed=Verticale snelheid
 fieldname.description=Omschrijving
+fieldname.comment=Commentaar
 fieldname.mediafilename=Bestandsnaam
 
-# Measurement units
+## Measurement units
 units.original=Oorspronkelijke
 units.default=Default
 units.metres=Meters
@@ -815,24 +801,24 @@ units.degreescelsius.short=\u00baC
 units.degreesfahrenheit=Fahrenheit
 units.degreesfahrenheit.short=\u00baF
 
-# How to combine conditions, such as filters
+## How to combine conditions, such as filters
 logic.and=en
 logic.or=of
 
-# External urls and services
+## 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
 
-# Cardinals for 3d plots
+## Cardinals for 3d plots
 cardinal.n=N
 cardinal.s=Z
 cardinal.e=O
 cardinal.w=W
 
-# Undo operations
+## Undo operations
 undo.load=gegevens laden
 undo.loadphotos=foto's laden
 undo.loadaudios=audiobestanden laden
@@ -863,7 +849,7 @@ undo.lookupsrtm=opzoeken hoogtes in SRTM
 undo.deletefieldvalues=verwijder veldwaarden
 undo.correlateaudios=correleer audiobestanden
 
-# Error messages
+## Error messages
 error.save.dialogtitle=Fout bij opslaan gegevens
 error.save.nodata=Geen gegevens om op te slaan
 error.save.failed=Kon de gegevens niet naar bestand wegschrijven
@@ -878,6 +864,7 @@ error.load.nopoints=Geen co\u00f6rdinaat informatie gevonden in bestand
 error.load.unknownxml=Onbekend xml-formaat:
 error.load.noxmlinzip=Geen xml-bestand gevonden in zipbestand
 error.load.othererror=Fout bij lezen bestand:
+error.load.nopointsintext=Geen co\u00f6rdinaten informatie gevonden
 error.jpegload.dialogtitle=Fout bij inlezen foto's
 error.jpegload.nofilesfound=Bestanden niet gevonden
 error.jpegload.nojpegsfound=Geen jpeg-bestanden gevonden
@@ -898,8 +885,6 @@ error.language.wrongfile=Het geselecteerde bestand is vermoedelijk geen taalbest
 error.convertnamestotimes.nonames=Namen konden niet naar tijden worden geconverteerd
 error.lookupsrtm.nonefound=Geen hoogtewaarden beschikbaar voor deze punten
 error.lookupsrtm.nonerequired=Alle punten hebben reeds hoogte, er hoeft niets te worden opgezocht.
-error.gpsies.uploadnotok=Gpsies server antwoordde met
-error.gpsies.uploadfailed=De upload is mislukt. Fout
 error.showphoto.failed=Foto laden mislukt
 error.playaudiofailed=Kon audiobestand niet afspelen
 error.cache.notthere=De tegelcache map niet gevonden
index c90acb7a34d5f5cd07a922638bd886c5905ce482..b83cc0c7fc985050869ee77e2bdbc746b80b5395 100644 (file)
@@ -7,8 +7,6 @@ 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
@@ -54,7 +52,6 @@ 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
@@ -66,7 +63,6 @@ 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.track.compress=K
@@ -82,16 +78,13 @@ 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
@@ -99,21 +92,12 @@ 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.viewfulldetails=Vis alle detaljer
 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
index eb0a727c3f4141d3f5024dff42205efab37ee0d1..2733749ebe7c2411c31c212ebd14f4383ad3177a 100644 (file)
@@ -97,10 +97,11 @@ function.convertnamestotimes=Zamie\u0144 nazwy punkt\u00f3w na czas
 function.deletefieldvalues=Usu\u0144 warto\u015bci
 function.findwaypoint=Znajd\u017a punkt po\u015bredni
 function.pastecoordinates=Wprowad\u017a nowe wsp\u00f3\u0142rz\u0119dne
+function.enterpluscode=Wpisz kod plus
 function.charts=Wykres
 function.show3d=Poka\u017c model 3D
 function.distances=Odleg\u0142o\u015bci
-function.fullrangedetails=Wszystkie detale
+function.viewfulldetails=Wszystkie detale
 function.estimatetime=Przewidywany czas
 function.learnestimationparams=Skoryguj wsp\u00f3\u0142czynniki szacowania czasu
 function.autoplay=Gra\u0107 \u015bcie\u017ck\u0119
@@ -110,16 +111,14 @@ 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
-function.downloadsrtm=Zapisz dane z SRTM
 function.getwikipedia=Szukaj w Wikipedii o okolicy
 function.searchwikipedianames=Szukaj nazwy w Wikipedii
 function.searchopencachingde=Szukaj w OpenCaching.de
 function.mapillary=Szukaj zdj\u0119cia w Mapillary
 function.downloadosm=Za\u0142aduj dane obszaru z OSM
 function.duplicatepoint=Duplikuj plik
+function.projectpoint=Rzucaj plik
 function.setcolours=Ustaw kolory
 function.setdisplaysettings=Ustawienia wy\u015bwietlacza
 function.setlanguage=Zmie\u0144 j\u0119zyk
@@ -366,25 +365,14 @@ dialog.gpsies.column.length=D\u0142ugo\u015b\u0107
 dialog.gpsies.description=Opis
 dialog.gpsies.nodescription=Brak opisu
 dialog.gpsies.nonefound=Nie znalaz\u0142em \u015bcie\u017cek
-dialog.gpsies.username=nazwa u\u017cytkownika w Gpsies
-dialog.gpsies.password=has\u0142o u\u017cytkownika w Gpsies
-dialog.gpsies.keepprivate=Zaznacz \u015bcie\u017cki jako prywatne
-dialog.gpsies.confirmopenpage=Otw\u00f3rz stron\u0119 z za\u0142adowan\u0105 \u015bcie\u017ck\u0105
-dialog.gpsies.activities=Aktywno\u015b\u0107
-dialog.gpsies.activity.trekking=W\u0119drowa\u0107
-dialog.gpsies.activity.walking=Spacer
-dialog.gpsies.activity.jogging=Bieganie
-dialog.gpsies.activity.biking=Wycieczka rowerowa
-dialog.gpsies.activity.motorbiking=Wycieczka motocyklowa
-dialog.gpsies.activity.snowshoe=Snowshoeing
-dialog.gpsies.activity.sailing=\u017beglarstwo
-dialog.gpsies.activity.skating=Wrotki/rolki
 dialog.mapillary.nonefound=Nic nie zosta\u0142o znalezione
 dialog.wikipedia.column.name=Tytu\u0142 artyku\u0142u
 dialog.wikipedia.column.distance=Odleg\u0142o\u015b\u0107
 dialog.wikipedia.nonefound=Brak wpis\u00f3w w wikipedii
 dialog.wikipedia.gallery=Galeria
 dialog.osmpois.column.name=Nazwa
+dialog.osmpois.column.type=Typ
+dialog.osmpois.nonefound=Nie znaleziono punkt\u00f3w
 dialog.geocaching.nonefound=Nic nie zosta\u0142o znalezione
 dialog.correlate.notimestamps=Punkty nie maj\u0105 znacznik\u00f3w czasu, nie mo\u017cna ich powi\u0105za\u0107 ze zdj\u0119ciami.
 dialog.correlate.nouncorrelatedphotos=Nie ma nie powi\u0105zanych zdj\u0119\u0107.\nCzy na pewno chcesz kontynuowa\u0107?
@@ -483,25 +471,9 @@ dialog.keys.intro=U\u017cuwaj nast\u0119puj\u0105cych klawiszy skr\u00f3t\u00f3w
 dialog.keys.keylist=<table><tr><td>klawisze strza\u0142ek</td><td>Przesuwa map\u0119 w lewo, w prawo, w g\u00f3r\u0119, w d\u00f3\u0142</td></tr><tr><td>Ctrl + lewa, prawa strza\u0142ka</td><td>Wybierz punkt poprzedni lub nast\u0119pny</td></tr><tr><td>Ctrl + strza\u0142ka w g\u00f3r\u0119, w d\u00f3\u0142</td><td>Powi\u0119ksz, pomniejsz</td></tr><tr><td>Del</td><td>Usun bie\u017c\u0105cy punkt</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=Nast\u0119puj\u0105ce ustawienia mog\u0105 zosta\u0107 zapisane w pliku konfiguracyjnym:
-dialog.saveconfig.prune.trackdirectory=Katalog ze \u015bcie\u017ckami
-dialog.saveconfig.prune.photodirectory=Katalog ze zdj\u0119ciami
-dialog.saveconfig.prune.languagecode=Kod j\u0119zyka (PL)
-dialog.saveconfig.prune.languagefile=Plik t\u0142umaczenia
-dialog.saveconfig.prune.gpsdevice=Urz\u0105dzenie GPS
-dialog.saveconfig.prune.gpsformat=Format pliku GPS
-dialog.saveconfig.prune.povrayfont=czcionka dla Povray-a
-dialog.saveconfig.prune.gnuplotpath=\u015bcie\u017cka do gnuplot
-dialog.saveconfig.prune.gpsbabelpath=\u015bcie\u017cka do gpsbabel
-dialog.saveconfig.prune.exiftoolpath=\u015bcie\u017cka do exiftool
-dialog.saveconfig.prune.mapsource=Wybrany dostawca map
-dialog.saveconfig.prune.mapsourcelist=Dostawcy map
-dialog.saveconfig.prune.diskcache=Pami\u0119\u0107 podr\u0119czna map
-dialog.saveconfig.prune.kmzimagewidth=szeroko\u015b\u0107 obrazka w KMZ
-dialog.saveconfig.prune.colourscheme=Schemat kolor\u00f3w
-dialog.saveconfig.prune.linewidth=Szeroko\u015b\u0107 linii
-dialog.saveconfig.prune.kmltrackcolour=Kolor \u015bcie\u017cki w pliku KML
-dialog.saveconfig.prune.autosavesettings=ustawienia autozapisu
+dialog.paths.prune.gnuplotpath=\u015bcie\u017cka do gnuplot
+dialog.paths.prune.gpsbabelpath=\u015bcie\u017cka do gpsbabel
+dialog.paths.prune.exiftoolpath=\u015bcie\u017cka do exiftool
 dialog.setpaths.intro=Je\u015bli zachodzi tak potrzeba, mo\u017cesz wybra\u0107 \u015bcie\u017cki do aplikacji zewn\u0119trznych
 dialog.setpaths.found=Znalezione \u015bcie\u017cki?
 dialog.addaltitude.noaltitudes=Wybrany zakres nie zawiera danych o wysoko\u015bciach
@@ -557,11 +529,16 @@ 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.displaysettings.waypointicons=Ikony dla punkt\u00f3w
+dialog.displaysettings.wpicon.plectrum=Plektron
+dialog.displaysettings.wpicon.ring=Pier\u015Bcie\u0144
 dialog.displaysettings.linewidth=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek
 dialog.displaysettings.antialias=U\u017Cyj antyaliasingu
 dialog.displaysettings.size.small=Ma\u0142e
 dialog.displaysettings.size.medium=\u015arednie
 dialog.displaysettings.size.large=Du\u017ce
+dialog.displaysettings.windowstyle=Styl okna (wymaga to restartu)
+dialog.displaysettings.windowstyle.default=Domy\u015Blny
 dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM:
 dialog.searchwikipedianames.search=Szukaj
 dialog.weather.location=Pozycja
@@ -593,12 +570,14 @@ dialog.deletebydate.column.keep=Zostaw
 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.settimezone.system=Stref\u0119 czasow\u0105 systemu
 dialog.settimezone.selectedzone=Wybrana strefa czasowa
 dialog.autoplay.duration=Czas trwania (sek)
 dialog.autoplay.usetimestamps=U\u017Cyj znacznik\u00f3w czasowych
 dialog.autoplay.rewind=Przewin\u0105\u0107
 dialog.autoplay.pause=Pauza
 dialog.autoplay.play=Graj
+dialog.projectpoint.bearing=Kierunek (stopnie od p\u00f3\u0142nocy)
 
 # 3d window
 dialog.3d.title=GpsPrune widok tr\u00f3jwymiarowy
@@ -620,7 +599,7 @@ confirm.rearrangephotos=Zmieniono kolejno\u015b\u0107 zdj\u0119\u0107
 confirm.splitsegments=Podzielono na %d fragmenty/\u00f3w
 confirm.sewsegments=Po\u0142\u0105czono %d fragmenty/\u00f3w
 confirm.cutandmove=Przesuni\u0119to zaznaczenie
-confirm.interpolate=Dodano punkty
+confirm.pointsadded=Dodano %d punkty
 confirm.convertnamestotimes=Zmieniono nazwy punkt\u00f3w po\u015brednich
 confirm.saveexif.ok=Zapisano %d plik(\u00f3w) zdj\u0119\u0107
 confirm.undo.single=cofni\u0119to operacj\u0119
@@ -639,7 +618,6 @@ confirm.running=Przetwarzam dane ...
 confirm.lookupsrtm=Znaleziono %d warto\u015bci wysoko\u015bci
 confirm.downloadsrtm=Pobrano %d plik\u00f3w do kesza
 confirm.downloadsrtm.1=Pobrano %d plik do kesza
-confirm.downloadsrtm.none=Nie pobrano \u017cadnych plik\u00f3w, wszystkie by\u0142y ju\u017c w keszu
 confirm.deletefieldvalues=Warto\u015bci p\u00f3l usuni\u0119to
 confirm.audioload=dodano pliki audio
 confirm.correlateaudios.single=audio zosta\u0142o po\u0142\u0105czone
@@ -649,7 +627,7 @@ confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone
 tip.title=Porada
 tip.useamapcache=Konfiguruj\u0105c kesz dyskowy (Ustawienia -> Zapisz mapy na dysk)\nprzyspieszasz wy\u015bwietlanie i ograniczasz ruch sieciowy
 tip.learntimeparams=Resultat b\u0119dzie dok\u0142adniejszy je\u015bli u\u017cyjesz
-tip.downloadsrtm=Mo\u017cesz przyspieszy\u0107 operacj\u0119 wywo\u0142uj\u0105c polecenie
+tip.downloadsrtm=Mo\u017cesz przyspieszy\u0107 operacj\u0119 konfiguruj\u0105c kesz dyskowy
 tip.usesrtmfor3d=\u015acie\u017cka nie zawiera danych o wysoko\u015bciach\nMo\u017cesz u\u017cy\u0107 funkcji SRTM by pobrac przybli\u017cone dane\no wysko\u015bciach w trybie widoku 3D.
 tip.manuallycorrelateone=Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie.
 
@@ -676,7 +654,6 @@ button.selectall=Zaznacz wszystko
 button.selectnone=Odznacz
 button.preview=Podgl\u0105d
 button.load=\u0141aduj
-button.upload=Wy\u015blij
 button.guessfields=Zgadnij pola
 button.showwebpage=Poka\u017c stron\u0119 web
 button.check=Sprawd\u017a
@@ -750,6 +727,7 @@ map.overzoom=Brak map dla danego powi\u0119kszenia
 # Field names
 fieldname.latitude=Szeroko\u015b\u0107
 fieldname.longitude=D\u0142ugo\u015b\u0107
+fieldname.coordinates=Wsp\u00f3\u0142rz\u0119dne
 fieldname.altitude=Wysoko\u015b\u0107
 fieldname.timestamp=Czas
 fieldname.time=Czas
@@ -764,6 +742,7 @@ fieldname.duration=Czas trwania
 fieldname.speed=Pr\u0119dko\u015b\u0107
 fieldname.verticalspeed=Pr\u0119dko\u015b\u0107 pionowa
 fieldname.description=Opis
+fieldname.comment=Komentarz
 fieldname.mediafilename=Nazwa pliku
 
 # Measurement units
@@ -863,6 +842,7 @@ error.load.nopoints=Nie znaleziono informacji o wsp\u00f3\u0142rz\u0119dnych w p
 error.load.unknownxml=Nieznany format xml:
 error.load.noxmlinzip=Nie znaleziono pliku xml w pliku zip
 error.load.othererror=B\u0142\u0105d czytania pliku:
+error.load.nopointsintext=Nie znaleziono wsp\u00f3\u0142rz\u0119dnych
 error.jpegload.dialogtitle=B\u0142\u0105d \u0142adowania zdj\u0119cia
 error.jpegload.nofilesfound=Nie znaleziono plik\u00f3w
 error.jpegload.nojpegsfound=Nie znaleziono plik\u00f3w jpeg
@@ -883,8 +863,6 @@ error.language.wrongfile=Wybrany plik nie jest plikiem z t\u0142umaczeniem dla G
 error.convertnamestotimes.nonames=\u017badne nazwy nie mog\u0142y zosta\u0107 zmienione na czas
 error.lookupsrtm.nonefound=Nie znaleziono danych o wysoko\u015bci.
 error.lookupsrtm.nonerequired=Wszystkie pola maj\u0105 informacj\u0119 o wysoko\u015bci, nie ma czego szuka\u0107
-error.gpsies.uploadnotok=Serwer Gpsies zwr\u00f3ci\u0142 informacj\u0119
-error.gpsies.uploadfailed=B\u0142\u0105d wysy\u0142ania
 error.showphoto.failed=Nie powiod\u0142o si\u0119 za\u0142adowanie zdj\u0119cia
 error.playaudiofailed=Nie powiod\u0142o si\u0119 odtwarzanie pliku audio
 error.cache.notthere=Nie znaleziono katalogu kesza
index bab5623c85b05696f95c3e9f725b90e5d7fd19ac..8190965e8de708c991594d14756194286df112e8 100644 (file)
@@ -96,10 +96,11 @@ function.convertnamestotimes=Converter nomes dos pontos para tempos
 function.deletefieldvalues=Remover valores do campo
 function.findwaypoint=Encontrar ponto
 function.pastecoordinates=Inserir novas coordenadas
+function.enterpluscode=Inserir um plus code
 function.charts=Gr\u00e1ficos
 function.show3d=Visualizar 3D
 function.distances=Dist\u00e2ncias
-function.fullrangedetails=Todos os detalhes
+function.viewfulldetails=Todos os detalhes
 function.estimatetime=Tempo estimado
 function.learnestimationparams=Aprender os par\u00e2metros para estimativa de tempo
 function.setmapbg=Definir como fundo do mapa
@@ -107,10 +108,7 @@ function.setpaths=Definir caminhos do programa
 function.selectsegment=Selecionar segmento atual
 function.splitsegments=Dividir rota em segmentos
 function.sewsegments=Reunir segmentos em rota
-function.getgpsies=Obter rotas Gpsies
-function.uploadgpsies=Enviar rotas para o Gpsies
 function.lookupsrtm=Obter altitudes a partir do SRTM
-function.downloadsrtm=Baixar arquivos SRTM
 function.getwikipedia=Obter artigos da Wikip\u00e9dia das redondezas
 function.searchwikipedianames=Procurar na Wikip\u00e9dia por nome
 function.searchopencachingde=Procurar na OpenCaching.de
@@ -360,20 +358,9 @@ dialog.gpsies.column.length=Extens\u00e3o
 dialog.gpsies.description=Descri\u00e7\u00e3o
 dialog.gpsies.nodescription=Sem descri\u00e7\u00e3o
 dialog.gpsies.nonefound=Nenhuma rota encontrada
-dialog.gpsies.username=Nome do usu\u00e1rio Gpsies
-dialog.gpsies.password=Senha do Gpsies
-dialog.gpsies.keepprivate=Manter rota privada
-dialog.gpsies.confirmopenpage=Abrir a p\u00e1gina para rotas enviadas?
-dialog.gpsies.activities=Tipos de atividade
-dialog.gpsies.activity.trekking=Trilha
-dialog.gpsies.activity.walking=Caminhada
-dialog.gpsies.activity.jogging=Corrida
-dialog.gpsies.activity.biking=Ciclismo
-dialog.gpsies.activity.motorbiking=Motocross
-dialog.gpsies.activity.snowshoe=Snowshoeing
-dialog.gpsies.activity.sailing=Sailing
-dialog.gpsies.activity.skating=Patina\u00e7\u00e3o
 dialog.mapillary.nonefound=Nenhuma foto encontrada
+dialog.osmpois.column.name=Nome
+dialog.osmpois.column.type=Tipo
 dialog.wikipedia.column.name=Nome do artigo
 dialog.wikipedia.column.distance=Dist\u00e2ncia
 dialog.wikipedia.nonefound=Nenhum artigo encontrado
@@ -434,6 +421,7 @@ dialog.deletemarked.nonefound=Nenhum dado dos pontos pode ser removido
 dialog.pastecoordinates.desc=Insira ou cole as coordenadas aqui
 dialog.pastecoordinates.coords=Coordenadas
 dialog.pastecoordinates.nothingfound=Por favor, verifique as coordenadas novamente
+dialog.pluscode.code=C\u00f3digo
 dialog.help.help=Por favor, veja\n https://gpsprune.activityworkshop.net/\npara mais informa\u00e7\u00f5es e guia do usu\u00e1rio.
 dialog.about.version=Vers\u00e3o
 dialog.about.build=Compila\u00e7\u00e3o
@@ -473,25 +461,9 @@ dialog.keys.intro=Voc\u00ea pode usar os seguintes atalhos de teclado ao inv\u00
 dialog.keys.keylist=<table><tr><td>Cursores</td><td>Move o mapa para esquerda, direita, acima e abaixo</td></tr><tr><td>Ctrl + cursores esquerdo e direito</td><td>Seleciona o pr\u00f3ximo ponto ou o anterior</td></tr><tr><td>Ctrl + cursores acima e abaixo</td><td>Amplia ou reduz</td></tr><tr><td>Del</td><td>Remove o ponto atual</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=As seguintes configura\u00e7\u00f5es podem ser salvas para um arquivo de configura\u00e7\u00e3o.
-dialog.saveconfig.prune.trackdirectory=Pasta de rotas
-dialog.saveconfig.prune.photodirectory=Pasta de fotos
-dialog.saveconfig.prune.languagecode=C\u00f3digo do idioma (PT_BR)
-dialog.saveconfig.prune.languagefile=Arquivo de idioma
-dialog.saveconfig.prune.gpsdevice=Dispositivo de GPS
-dialog.saveconfig.prune.gpsformat=Formato do GPS
-dialog.saveconfig.prune.povrayfont=Fonte Povray
-dialog.saveconfig.prune.gnuplotpath=Caminho para o gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Caminho para o gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Caminho para o exiftool
-dialog.saveconfig.prune.mapsource=Selecionar fonte de mapas
-dialog.saveconfig.prune.mapsourcelist=Fontes de mapas
-dialog.saveconfig.prune.diskcache=Cache de mapas
-dialog.saveconfig.prune.kmzimagewidth=Largura da imagem KMZ
-dialog.saveconfig.prune.colourscheme=Esquema de cores
-dialog.saveconfig.prune.linewidth=Espessura da linha
-dialog.saveconfig.prune.kmltrackcolour=Cor da rota KML
-dialog.saveconfig.prune.autosavesettings=Configura\u00e7\u00f5es para salvamento autom\u00e1tico
+dialog.paths.prune.gnuplotpath=Caminho para o gnuplot
+dialog.paths.prune.gpsbabelpath=Caminho para o gpsbabel
+dialog.paths.prune.exiftoolpath=Caminho para o exiftool
 dialog.setpaths.intro=Se voc\u00ea precisar, voc\u00ea pode escolher os caminhos para as aplica\u00e7\u00f5es externas:
 dialog.setpaths.found=Caminho encontrado?
 dialog.addaltitude.noaltitudes=O intervalo selecionado n\u00e3o cont\u00e9m altitudes
@@ -566,6 +538,7 @@ dialog.autoplay.usetimestamps=Usar data-hora
 dialog.autoplay.rewind=Rebobinar
 dialog.autoplay.pause=Suspender
 dialog.autoplay.play=Tocar
+dialog.projectpoint.bearing=Azimute (graus de N)
 
 # 3d window
 dialog.3d.title=Vista 3D do GpsPrune
@@ -587,7 +560,7 @@ confirm.rearrangephotos=Fotos rearrumadas
 confirm.splitsegments=%d divis\u00f5es de segmentos feitas
 confirm.sewsegments=%d reuni\u00f5es de segmentos feitas
 confirm.cutandmove=Sele\u00e7\u00e3o movida
-confirm.interpolate=Pontos adicionados
+confirm.pointsadded=%d pontos adicionados
 confirm.convertnamestotimes=Nomes dos pontos convertidos
 confirm.saveexif.ok=Salvo %d arquivos de foto
 confirm.undo.single=opera\u00e7\u00e3o desfeita
@@ -606,7 +579,6 @@ confirm.running=Rodando...
 confirm.lookupsrtm=Encontrado %d valores de altitude
 confirm.downloadsrtm=%d arquivos baixados para a cache
 confirm.downloadsrtm.1=%d arquivo baixados para a cache
-confirm.downloadsrtm.none=Nenhum arquivo baixado, pois j\u00e1 est\u00e3o na cache.
 confirm.deletefieldvalues=Valores do campo removidos
 confirm.audioload=Arquivos de \u00e1udio adicionados
 confirm.correlateaudios.single=\u00e1udio foi correlacionado
@@ -616,7 +588,6 @@ confirm.correlateaudios.multi=\u00e1udios foram correlacionados
 tip.title=Dica
 tip.useamapcache=Configurando a cache de disco (Configura\u00e7\u00f5es -> Salvar mapas para disco)\nvoc\u00ea pode acelerar a exibi\u00e7\u00e3o e reduzir o tr\u00e1fego de rede.
 tip.learntimeparams=Os resultados ser\u00e3o mais precisos se voc\u00ea usar\nRota -> Aprender os par\u00e2metros para estimativa de tempo\nde suas rotas gravadas.
-tip.downloadsrtm=Voc\u00ea pode acelerar chamando\nOnline -> Baixar ladrilhos SRTM\npara obter as altitudes\naproximadas para a vis\u00e3o 3D.
 tip.usesrtmfor3d=Esta rota n\u00e3o possui altitudes.\nVoc\u00ea pode usar as fun\u00e7\u00f5es SRTM para obter as altitudes\naproximadas para a vis\u00e3o 3D.
 tip.manuallycorrelateone=Correlacionando pelo menos uma foto manualmente, a diferen\u00e7a de tempo pode ser calculada para voc\u00ea.
 
@@ -643,7 +614,6 @@ button.selectall=Selecionar todos
 button.selectnone=Selecionar nenhum
 button.preview=Previs\u00e3o
 button.load=Carregar
-button.upload=Enviar
 button.guessfields=Campos adivinhados
 button.showwebpage=Mostrar p\u00e1gina Web
 button.check=Verificar
@@ -717,6 +687,7 @@ map.overzoom=Nenhum mapa dispon\u00edvel neste n\u00edvel de amplia\u00e7\u00e3o
 # Field names
 fieldname.latitude=Latitude
 fieldname.longitude=Longitude
+fieldname.coordinates=Coordenadas
 fieldname.altitude=Altura
 fieldname.timestamp=Tempo
 fieldname.time=Tempo
@@ -731,6 +702,7 @@ fieldname.duration=Dura\u00e7\u00e3o
 fieldname.speed=Velocidade
 fieldname.verticalspeed=Velocidade vertical
 fieldname.description=Descri\u00e7\u00e3o
+fieldname.comment=Coment\u00e1rio
 fieldname.mediafilename=Arquivo
 
 # Measurement units
@@ -850,8 +822,6 @@ error.language.wrongfile=O arquivo selecionado n\u00e3o parece ser um arquivo de
 error.convertnamestotimes.nonames=Nenhum nome pode ser convertido para tempo
 error.lookupsrtm.nonefound=Nenhum valor de altitude encontrado
 error.lookupsrtm.nonerequired=Todos os pontos j\u00e1 possuem altitude, assim n\u00e3o h\u00e1 nada a procurar
-error.gpsies.uploadnotok=O servidor Gpsies retornou a mensagem
-error.gpsies.uploadfailed=O envio falhou com o erro
 error.showphoto.failed=Falha ao carregar foto
 error.playaudiofailed=Falha ao reproduzir arquivo de \u00e1udio
 error.cache.notthere=A paste de cache de fundos n\u00e3o foi encontrada
index 9c62dbe42d08ebfafc28257fd56034cfc0f1d177..7affd24348c668cd6d446e7e83ab0fa0ed93814f 100644 (file)
@@ -39,6 +39,12 @@ menu.view.browser.bing=Harta Bing
 menu.settings=Set\u0103ri
 menu.settings.onlinemode=\u00cencarc\u0103 h\u0103r\u021bi
 dialog.displaysettings.antialias=Folose\u0219te antialiasing
+dialog.displaysettings.wpicon.plectrum=Plectru
+dialog.displaysettings.wpicon.ring=Inel
+dialog.displaysettings.size.small=Mic
+dialog.displaysettings.size.medium=Mediu
+dialog.displaysettings.size.large=Mare
+dialog.displaysettings.windowstyle=Stilul ferestrei (este necesar\u0103 o repornire)
 menu.settings.autosave=Salveaz\u0103 set\u0103rile automat la ie\u0219ire
 menu.help=Ajutor
 
@@ -99,10 +105,11 @@ function.convertnamestotimes=Converte\u0219te numele waypoint-urilor \u00een tim
 function.deletefieldvalues=\u0218terge valorile c\u00e2mpurilor
 function.findwaypoint=G\u0103se\u0219te waypoint
 function.pastecoordinates=Introdu coordonate noi
+function.pastecoordinatelist=Introdu o list\u0103 de coordonate
 function.charts=Grafice
 function.show3d=Vizualizare 3D
 function.distances=Distan\u0163e
-function.fullrangedetails=Informa\u0163ie complet\u0103
+function.viewfulldetails=Informa\u0163ie complet\u0103
 function.estimatetime=Estimare durat\u0103
 function.learnestimationparams=\u00cenva\u021b\u0103 parametri de estimare timpi
 function.autoplay=Parcurge traseu
@@ -111,10 +118,7 @@ function.setpaths=Seteaz\u0103 calea c\u0103tre aplica\u021bii
 function.selectsegment=Selecteaz\u0103 segment curent
 function.splitsegments=Divizeaz\u0103 traseul \u00een segmente
 function.sewsegments=Combin\u0103 segmentele traseului
-function.getgpsies=\u00cencarc\u0103 trasee Gpsies
-function.uploadgpsies=Trimite date spre Gpsies
 function.lookupsrtm=Descarc\u0103 date SRTM \u00een cache
-function.downloadsrtm=Descarc\u0103 date SRTM
 function.getwikipedia=Caut\u0103 articole Wikipedia din proximitate
 function.searchwikipedianames=Caut\u0103 Wikipedia dup\u0103 nume
 function.searchopencachingde=Caut\u0103 OpenCaching.de
@@ -146,6 +150,7 @@ function.diskcache=Salvare harti
 function.managetilecache=Administreaz\u0103 imaginile salvate
 function.getweatherforecast=Prognoz\u0103 meteo
 function.setaltitudetolerance=Alege toleran\u021ba varia\u021biei altitudinii
+function.selecttimezone=Selecta\u021Bi fusul orar
 
 # Dialogs
 dialog.exit.confirm.title=Ie\u015fire din programul GpsPrune
@@ -365,19 +370,6 @@ dialog.gpsies.column.length=Lungime
 dialog.gpsies.description=Descriere
 dialog.gpsies.nodescription=F\u0103r\u0103 descriere
 dialog.gpsies.nonefound=Nu a fost g\u0103sit niciun traseu
-dialog.gpsies.username=Gpsies username
-dialog.gpsies.password=Gpsies parol\u0103
-dialog.gpsies.keepprivate=Traseu privat
-dialog.gpsies.confirmopenpage=Deschid pagin\u0103 web pentru traseul \u00eenc\u0103rcat?
-dialog.gpsies.activities=Activit\u0103\u0163i
-dialog.gpsies.activity.trekking=Mers pe munte
-dialog.gpsies.activity.walking=Mers pe jos
-dialog.gpsies.activity.jogging=Alergare
-dialog.gpsies.activity.biking=Biciclet\u0103
-dialog.gpsies.activity.motorbiking=Motociclet\u0103
-dialog.gpsies.activity.snowshoe=Mers cu rachete de z\u0103pad\u0103
-dialog.gpsies.activity.sailing=Navigare
-dialog.gpsies.activity.skating=Role
 dialog.mapillary.nonefound=Nicio fotografie nu a fost g\u0103sit\u0103
 dialog.wikipedia.column.name=Nume
 dialog.wikipedia.column.distance=Distan\u0163\u0103
@@ -481,25 +473,9 @@ dialog.keys.intro=Pute\u021bi folosi urm\u0103toarele scurt\u0103turi \u00een lo
 dialog.keys.keylist=<table><tr><td>Taste s\u0103ge\u021bi</td><td>Mut\u0103 harta st\u00e2nga, dreapta, sus, jos</td></tr><tr><td>Ctrl + s\u0103geat\u0103 st\u00e2nga, dreapta</td><td>Selecteaz\u0103 punctul anterior sau urm\u0103tor</td></tr><tr><td>Ctrl + s\u0103geat\u0103 sus, jos</td><td>Aproprie sau \u00eendep\u0103rteaz\u0103</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Selecteaz\u0103 segmentul anterior sau urm\u0103tor</td></tr><tr><td>Ctrl + Home, End</td><td>Selecteaz\u0103 primul, ultimul punct</td></tr><tr><td>Del</td><td>\u0218terge punctul curent</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=Urm\u0103toarele set\u0103ri pot fi salvate \u00eentr-un fi\u0219ier de configur\u0103ri :
-dialog.saveconfig.prune.trackdirectory=Director trasee
-dialog.saveconfig.prune.photodirectory=Director foto
-dialog.saveconfig.prune.languagecode=Limb\u0103 (RO)
-dialog.saveconfig.prune.languagefile=Fi\u015fier de limba
-dialog.saveconfig.prune.gpsdevice=Dispozitiv GPS
-dialog.saveconfig.prune.gpsformat=Format GPS
-dialog.saveconfig.prune.povrayfont=Font Povray
-dialog.saveconfig.prune.gnuplotpath=Calea c\u0103tre gnuplot
-dialog.saveconfig.prune.gpsbabelpath=Calea c\u0103tre gpsbabel
-dialog.saveconfig.prune.exiftoolpath=Calea c\u0103tre exiftool
-dialog.saveconfig.prune.mapsource=Surs\u0103 hart\u0103 selectat\u0103
-dialog.saveconfig.prune.mapsourcelist=Surse hart\u0103
-dialog.saveconfig.prune.diskcache=Cache hart\u0103
-dialog.saveconfig.prune.kmzimagewidth=Dimensiuni imagini \u00een KMZ
-dialog.saveconfig.prune.colourscheme=Schem\u0103 de culoare
-dialog.saveconfig.prune.linewidth=Grosime linie
-dialog.saveconfig.prune.kmltrackcolour=Culoar track KML
-dialog.saveconfig.prune.autosavesettings=Set\u0103ri salv\u0103ri automate
+dialog.paths.prune.gnuplotpath=Calea c\u0103tre gnuplot
+dialog.paths.prune.gpsbabelpath=Calea c\u0103tre gpsbabel
+dialog.paths.prune.exiftoolpath=Calea c\u0103tre exiftool
 dialog.setpaths.intro=Dac\u0103 dori\u021bi pute\u021bi alege calea c\u0103tre aplica\u021bii externe
 dialog.setpaths.found=Cale g\u0103sit\u0103?
 dialog.addaltitude.noaltitudes=Intervalul nu con\u021bine altitudini
@@ -613,7 +589,7 @@ confirm.rearrangephotos=Fotografiile au fost rearanjate
 confirm.splitsegments=Au fost f\u0103cute %d diviz\u0103ri de segmente
 confirm.sewsegments=Au fost combinate %d segmente
 confirm.cutandmove=Selec\u021bia a fost mutat\u0103
-confirm.interpolate=Punctele au fost ad\u0103ugate
+confirm.pointsadded=%d punctele au fost ad\u0103ugate
 confirm.convertnamestotimes=Numele waypoint-urile au fost convertite
 confirm.saveexif.ok=Au fost salvate %d fi\u0219iere foto
 confirm.undo.single=opera\u021bia a fost anulat\u0103
@@ -632,7 +608,6 @@ confirm.running=Executare ...
 confirm.lookupsrtm=Au fost g\u0103site %d valori de altitudine
 confirm.downloadsrtm=S-au desc\u0103rcat %d fi\u015fiere
 confirm.downloadsrtm.1=S-au desc\u0103rcat %d fi\u015fier
-confirm.downloadsrtm.none=Niciun fi\u0219ier nu a fost desc\u0103rcat, ele erau deja prezente \u00een cache
 confirm.deletefieldvalues=Valorile c\u00e2mpurilor au fost \u0219terse
 confirm.audioload=Fi\u0219iere audio au fost ad\u0103ugate
 confirm.correlateaudios.single=clipul audio a fost corelat
@@ -642,7 +617,6 @@ confirm.correlateaudios.multi=clipurile audio au fost corelate
 tip.title=Indiciu
 tip.useamapcache=Prin configurarea unui cache pe disc (Set\u0103ri -> Salvare h\u0103r\u021bi)\npute\u021bi \u00eembun\u0103t\u0103\u021bi viteza de afi\u0219are \u0219i reduce traficul de re\u021bea
 tip.learntimeparams=Rezultatele vor fi mai corecte dac\u0103 folosi\u021bi\nTraseu -> \u00cenva\u021b\u0103 parametri pentru estim\u0103ri de timp\npe traseele deja \u00eenregistrate.
-tip.downloadsrtm=Pute\u021bi \u00eembun\u0103t\u0103\u021bi viteza prin folosirea\nInternet -> Descarc\u0103 date SRTM
 tip.usesrtmfor3d=Acest traseu nu are valori de altitudine.\nPute\u021bi folosi func\u021biile SRTM pentru a aproxima altitudinile \u00een vederea 3D.
 tip.manuallycorrelateone=Decalajul de timp poate fi calculat dac\u0103 cel pu\u021bin un element e conectat.
 
@@ -669,7 +643,6 @@ button.selectall=Selecteaz\u0103 tot
 button.selectnone=Deselecteaz\u0103 tot
 button.preview=Previzualizare
 button.load=Descarc\u0103
-button.upload=Trimite
 button.guessfields=Ghice\u0219te c\u00e2mpuri
 button.showwebpage=Deschide pagina web
 button.check=Verific\u0103
@@ -743,6 +716,7 @@ map.overzoom=Nu exist\u0103 h\u0103r\u021bi la acest nivel de apropiere
 # Field names
 fieldname.latitude=Latitudine
 fieldname.longitude=Longitudine
+fieldname.coordinates=Coordonate
 fieldname.altitude=Altitudine
 fieldname.timestamp=Timp
 fieldname.time=Timp
@@ -757,6 +731,7 @@ fieldname.duration=Durat\u0103
 fieldname.speed=Vitez\u0103
 fieldname.verticalspeed=Vitez\u0103 vertical\u0103
 fieldname.description=Descriere
+fieldname.comment=Comentariu
 fieldname.mediafilename=Fi\u0219ier
 
 # Measurement units
@@ -876,8 +851,6 @@ error.language.wrongfile=Fi\u0219ierul selectat nu pare a fi un fi\u0219ier de l
 error.convertnamestotimes.nonames=Niciun nume de waypoint nu a fost g\u0103sit sau nu au putut fi convertite
 error.lookupsrtm.nonefound=Pentru aceste puncte nu exist\u0103 valori de altitudine
 error.lookupsrtm.nonerequired=Toate punctele au deja altitudine, nu e nimic de calculat
-error.gpsies.uploadnotok=Server-ul gpsies a \u00eentors mesajul
-error.gpsies.uploadfailed=Upload-ul a e\u0219uat cu eroarea
 error.showphoto.failed=\u00cenc\u0103rcarea foto a e\u0219uat
 error.playaudiofailed=\u00cencercarea de a reda clipul audio a e\u0219uat
 error.cache.notthere=Directorul tile cache nu a fost g\u0103sit
index ea4286bf80553430799f65e9f88c10b4a08dc9b6..82a8fa9a33797948ca2af3e746a522d060bf429d 100644 (file)
@@ -12,7 +12,6 @@ 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 \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
 menu.range.none=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u044b\u0431\u043e\u0440\u043a\u0443
@@ -86,6 +85,7 @@ function.exportpov=\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 POV
 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.deletemarked=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438
 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
@@ -98,10 +98,12 @@ function.convertnamestotimes=\u041a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0
 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
+function.pastecoordinatelist=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442
+function.enterpluscode=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 pluscode
 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.viewfulldetails=\u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0435 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\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=\u0410\u0432\u0442\u043e\u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0440\u0435\u043a\u0430
@@ -111,10 +113,7 @@ function.selectsegment=\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0442\u0435\u
 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=\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.searchosmpois=\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0435 \u0442\u043e\u0447\u043a\u0438 OSM
@@ -122,6 +121,7 @@ function.searchopencachingde=\u041f\u043e\u0438\u0441\u043a \u0442\u0430\u0439\u
 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 \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.projectpoint=\u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u0447\u043a\u0443
 function.setcolours=\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0446\u0432\u0435\u0442\u0430
 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
@@ -369,19 +369,6 @@ dialog.gpsies.column.length=\u0414\u043b\u0438\u043d\u0430
 dialog.gpsies.description=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435
 dialog.gpsies.nodescription=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e
 dialog.gpsies.nonefound=\u0422\u0440\u0435\u043a\u043e\u0432 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e
-dialog.gpsies.username=\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 Gpsies
-dialog.gpsies.password=\u043f\u0430\u0440\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f Gpsies
-dialog.gpsies.keepprivate=\u0427\u0430\u0441\u0442\u043d\u044b\u0439 \u0442\u0440\u0435\u043a
-dialog.gpsies.confirmopenpage=\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0442\u0440\u0435\u043a\u0430 \u043d\u0430 \u0441\u0430\u0439\u0442 gpsies.com?
-dialog.gpsies.activities=\u0422\u0438\u043f\u044b \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438
-dialog.gpsies.activity.trekking=\u041f\u0435\u0448\u0438\u0439 \u0442\u0443\u0440\u0438\u0437\u043c
-dialog.gpsies.activity.walking=\u0425\u043e\u0434\u044c\u0431\u0430
-dialog.gpsies.activity.jogging=\u0411\u0435\u0433
-dialog.gpsies.activity.biking=\u041d\u0430 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u0435
-dialog.gpsies.activity.motorbiking=\u041d\u0430 \u043c\u043e\u0442\u043e\u0446\u0438\u043a\u043b\u0435
-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
@@ -449,6 +436,10 @@ dialog.deletemarked.nonefound=\u041d\u0435\u0442 \u043f\u043e\u043c\u0435\u0447\
 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\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.pastecoordinatelist.desc=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0434\u043b\u044f \u043d\u043e\u0432\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0435 \u043d\u0430 \u043f\u0440\u044f\u043c\u043e\u0439
+dialog.pluscode.desc=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043b\u0438 \u0432\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043b\u044e\u0441-\u043a\u043e\u0434 \u0437\u0434\u0435\u0441\u044c
+dialog.pluscode.code=\u041f\u043b\u044e\u0441-\u043a\u043e\u0434
+dialog.pluscode.nothingfound=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043a\u043e\u0434 \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \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\nhttps://gpsprune.activityworkshop.net/
 dialog.about.version=\u0412\u0435\u0440\u0441\u0438\u044f
 dialog.about.build=\u0420\u0435\u0432\u0438\u0437\u0438\u044f
@@ -488,25 +479,9 @@ dialog.keys.intro=\u0412\u043c\u0435\u0441\u0442\u043e \u043c\u044b\u0448\u0438
 dialog.keys.keylist=<table><tr><td></td><td>\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</td><td><tr><td>Ctrl + \u043b\u0435\u0432\u0430\u044f, \u043f\u0440\u0430\u0432\u0430\u044f \u0441\u0442\u0440\u0435\u043b\u043a\u0430</td><td>\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</td></tr><tr><td>Ctrl + \u0441\u0442\u0440\u0435\u043b\u043a\u0438 \u0432\u0432\u0435\u0440\u0445, \u0432\u043d\u0438\u0437 </td><td> \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</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>\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</td></tr><tr><td>Ctrl + Home, End</td><td>\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</td></tr><tr><td>Del</td><td>\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0442\u043e\u0447\u043a\u0443</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b \u0432 \u0444\u0430\u0439\u043b:
-dialog.saveconfig.prune.trackdirectory=\u041f\u0430\u043f\u043a\u0430 \u0441 \u0442\u0440\u0435\u043a\u0430\u043c\u0438
-dialog.saveconfig.prune.photodirectory=\u041f\u0430\u043f\u043a\u0430 \u0441 \u0444\u043e\u0442\u043e
-dialog.saveconfig.prune.languagecode=\u041a\u043e\u0434 \u044f\u0437\u044b\u043a\u0430 (RU)
-dialog.saveconfig.prune.languagefile=\u042f\u0437\u044b\u043a\u043e\u0432\u043e\u0439 \u0444\u0430\u0439\u043b
-dialog.saveconfig.prune.gpsdevice=GPS \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e
-dialog.saveconfig.prune.gpsformat=GPS \u0444\u043e\u0440\u043c\u0430\u0442
-dialog.saveconfig.prune.povrayfont=Povray \u0448\u0440\u0438\u0444\u0442
-dialog.saveconfig.prune.gnuplotpath=\u041f\u0443\u0442\u044c \u043a GNUPLOT
-dialog.saveconfig.prune.gpsbabelpath=\u041f\u0443\u0442\u044c \u043a GPSBabel
-dialog.saveconfig.prune.exiftoolpath=\u041f\u0443\u0442\u044c \u043a ExifTool
-dialog.saveconfig.prune.mapsource=\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043a\u0430\u0440\u0442\u044b
-dialog.saveconfig.prune.mapsourcelist=\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438 \u043a\u0430\u0440\u0442
-dialog.saveconfig.prune.diskcache=\u041a\u0435\u0448 \u043a\u0430\u0440\u0442\u044b
-dialog.saveconfig.prune.kmzimagewidth=\u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f KMZ
-dialog.saveconfig.prune.colourscheme=\u0426\u0432\u0435\u0442\u043e\u0432\u0430\u044f \u0441\u0445\u0435\u043c\u0430
-dialog.saveconfig.prune.linewidth=\u0422\u043e\u043b\u0449\u0438\u043d\u0430 \u043b\u0438\u043d\u0438\u0438
-dialog.saveconfig.prune.kmltrackcolour=\u0446\u0432\u0435\u0442 \u0442\u0440\u0435\u043a\u0430 KML
-dialog.saveconfig.prune.autosavesettings=\u0410\u0432\u0442\u043e\u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043e\u043a
+dialog.paths.prune.gnuplotpath=\u041f\u0443\u0442\u044c \u043a GNUPLOT
+dialog.paths.prune.gpsbabelpath=\u041f\u0443\u0442\u044c \u043a GPSBabel
+dialog.paths.prune.exiftoolpath=\u041f\u0443\u0442\u044c \u043a ExifTool
 dialog.setpaths.intro=\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u043f\u0443\u0442\u044c \u043a \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c:
 dialog.setpaths.found=\u041f\u0443\u0442\u044c \u043d\u0430\u0439\u0434\u0435\u043d?
 dialog.addaltitude.noaltitudes=\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0432\u044b\u0441\u043e\u0442
@@ -573,6 +548,8 @@ 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.displaysettings.windowstyle=\u0441\u0442\u0438\u043b\u044c \u043e\u043a\u043d\u0430
+dialog.displaysettings.windowstyle.default=\u0441\u0442\u0438\u043b\u044c \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e
 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
@@ -615,6 +592,12 @@ dialog.autoplay.usetimestamps=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u
 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
+dialog.markers.halves=\u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438
+dialog.markers.half.distance=\u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0430 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u044f
+dialog.markers.half.climb=\u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0430 \u043f\u043e\u0434\u044a\u0451\u043c\u0430
+dialog.markers.half.descent=\u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0430 \u0441\u043f\u0443\u0441\u043a\u0430
+dialog.projectpoint.desc=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0446\u0438\u0438 \u044d\u0442\u043e\u0439 \u0442\u043e\u0447\u043a\u0438
+dialog.projectpoint.bearing=\u0430\u0437\u0438\u043c\u0443\u0442 (\u0433\u0440\u0430\u0434\u0443\u0441\u044b \u0441 \u0441\u0435\u0432\u0435\u0440\u0430)
 
 # 3d window
 dialog.3d.title=GpsPrune 3D-\u0432\u0438\u0434
@@ -636,7 +619,7 @@ confirm.rearrangephotos=\u0424\u043e\u0442\u043e \u043f\u0435\u0440\u0435\u0434\
 confirm.splitsegments=\u0421\u0434\u0435\u043b\u0430\u043d\u043e %d \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0439 \u043d\u0430 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u044b
 confirm.sewsegments=\u0421\u0434\u0435\u043b\u0430\u043d\u043e %d \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u043e\u0432
 confirm.cutandmove=\u041e\u0442\u043e\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u043e
-confirm.interpolate=\u0422\u043e\u0447\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b
+confirm.pointsadded=%d \u0422\u043e\u0447\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b
 confirm.convertnamestotimes=\u0418\u043c\u044f \u043f\u0443\u0442\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u043e
 confirm.saveexif.ok=\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u044b \u0441 \u0444\u043e\u0442\u043e
 confirm.undo.single=\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043e\u0442\u043c\u0435\u043d\u044b
@@ -655,7 +638,6 @@ confirm.running=\u0420\u0430\u0431\u043e\u0442\u0430\u044e...
 confirm.lookupsrtm=\u041d\u0430\u0439\u0434\u0435\u043d\u043e %d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b
 confirm.downloadsrtm=\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u043e\u0432 \u0432 \u043a\u044d\u0448
 confirm.downloadsrtm.1=\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u043e\u0432 \u0432 \u043a\u044d\u0448
-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\u0430 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430
@@ -665,7 +647,7 @@ confirm.correlateaudios.multi=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u
 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.downloadsrtm=\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u044d\u0442\u043e \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.
 
@@ -692,7 +674,6 @@ button.selectall=\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435
 button.selectnone=\u041e\u0442\u043c\u0435\u043d\u0442\u0438\u0442\u044c \u0432\u044b\u0431\u043e\u0440\u043a\u0443
 button.preview=\u041f\u0440\u0435\u0434\u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440
 button.load=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c
-button.upload=\u0412\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c
 button.guessfields=\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0435 \u043f\u043e\u043b\u044f
 button.showwebpage=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443
 button.check=\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430
@@ -766,6 +747,7 @@ map.overzoom=\u041d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b
 # Field names
 fieldname.latitude=\u0428\u0438\u0440\u043e\u0442\u0430
 fieldname.longitude=\u0414\u043e\u043b\u0433\u043e\u0442\u0430
+fieldname.coordinates=\u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0301\u0442\u044b
 fieldname.altitude=\u0412\u044b\u0441\u043e\u0442\u0430
 fieldname.timestamp=\u0412\u0440\u0435\u043c\u044f
 fieldname.time=\u0412\u0440\u0435\u043c\u044f
@@ -780,6 +762,7 @@ fieldname.duration=\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\
 fieldname.speed=\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c
 fieldname.verticalspeed=\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c
 fieldname.description=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435
+fieldname.comment=\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0301\u0440\u0438\u0439
 fieldname.mediafilename=\u0424\u0430\u0439\u043b
 
 # Measurement units
@@ -879,6 +862,7 @@ error.load.nopoints=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u04
 error.load.unknownxml=\u041d\u0435\u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d\u043d\u044b\u0439 XML- \u0444\u043e\u0440\u043c\u0430\u0442:
 error.load.noxmlinzip=\u0412 zip-\u0430\u0440\u0445\u0438\u0432\u0435 \u043d\u0435\u0442 XML-\u0444\u0430\u0439\u043b\u0430
 error.load.othererror=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438 \u0444\u0430\u0439\u043b\u0430:
+error.load.nopointsintext=\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430
 error.jpegload.dialogtitle=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0444\u043e\u0442\u043e
 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
@@ -899,8 +883,6 @@ error.language.wrongfile=\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439
 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.
 error.lookupsrtm.nonerequired=\u0412\u0441\u0435 \u0442\u043e\u0447\u043a\u0438 \u0443\u0436\u0435 \u0438\u043c\u0435\u044e\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432\u044b\u0441\u043e\u0442, \u043d\u0435\u0447\u0435\u0433\u043e \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c
-error.gpsies.uploadnotok=Gpsies \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u0435\u0440\u043d\u0443\u043b \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435
-error.gpsies.uploadfailed=\u0412\u044b\u0433\u0440\u0443\u0437\u043a\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0430 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439
 error.showphoto.failed=\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u043e\u0442\u043e
 error.playaudiofailed=\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438
 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
index d149f98baa82de4fd77e0a37ca0bc27cb36fb912..4e46980887292f702f79aeacb91a7a23fafbb165 100644 (file)
@@ -1,5 +1,5 @@
 # Text entries for the GpsPrune application
-# Swedish entries
+# Swedish entries thanks to erikiiofph7
 
 # Menu entries
 menu.file=Arkiv
@@ -17,10 +17,10 @@ menu.range.all=V\u00e4lj alla
 menu.range.none=V\u00e4lj ingen
 menu.range.start=S\u00e4tt till b\u00f6rjan av intervall
 menu.range.end=S\u00e4tt till slutet av intervall
-menu.range.average=Medelv\u00e4rdesval
+menu.range.average=Skapa medelv\u00e4rdespunkt
 menu.range.reverse=V\u00e4nd intervall
 menu.range.mergetracksegments=Sl\u00e5 ihop sp\u00e5rsegment
-menu.range.cutandmove=Klipp och flytta urval
+menu.range.cutandmove=Klipp och flytta intervall
 menu.point=Punkt
 menu.point.editpoint=Redigera punkt
 menu.point.deletepoint=Radera punkt
@@ -35,6 +35,8 @@ menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Yahoo maps
 menu.view.browser.bing=Bing maps
+menu.view.browser.inlinemap=Inline map
+menu.view.browser.graphhopper=GraphHopper
 menu.settings=Inst\u00e4llningar
 menu.settings.onlinemode=H\u00e4mta kartor fr\u00e5n Internet
 menu.settings.autosave=Autospara inst\u00e4llningar vid avslut
@@ -71,6 +73,7 @@ shortcut.menu.file.save=S
 shortcut.menu.track.undo=Z
 shortcut.menu.track.compress=K
 shortcut.menu.range.all=A
+shortcut.menu.point.edit=E
 shortcut.menu.help.help=H
 
 # Functions
@@ -96,23 +99,23 @@ function.rearrangewaypoints=Ordna waypoints
 function.convertnamestotimes=Omvandla waypointnamn till tidpunkter
 function.deletefieldvalues=Ta bort f\u00e4lt-v\u00e4rden
 function.findwaypoint=S\u00f6k waypoint
-function.pastecoordinates=Infoga koordinater
+function.pastecoordinates=Skapa punkt via koordinater
+function.pastecoordinatelist=Skapa punkter via en koordinat-lista
+function.enterpluscode=Skapa punkt(er) via pluss-kod
 function.charts=Diagram
 function.show3d=3D-vy
 function.distances=Avst\u00e5nd
-function.fullrangedetails=Alla intevall-detaljer
+function.viewfulldetails=Alla detaljer
 function.estimatetime=Uppskatta tid
 function.learnestimationparams=L\u00e4r upp tidsuppskattningsparametrar
+function.autoplay=Spela upp sp\u00e5ret
 function.setmapbg=V\u00e4lj bakgrundskarta
 function.setpaths=V\u00e4lj s\u00f6kv\u00e4gar f\u00f6r program
 function.selectsegment=Markera aktuellt segment
 function.splitsegments=Dela upp sp\u00e5ret i segment
-function.sewsegments=Sy ihop sp\u00e5r-segment
+function.sewsegments=Sy ihop sp\u00e5rsegment
 function.createmarkerwaypoints=Skapa markerings-waypoints
-function.getgpsies=H\u00e4mta Gpsies-sp\u00e5r
-function.uploadgpsies=Ladda upp sp\u00e5r till Gpsies
 function.lookupsrtm=H\u00e4mta h\u00f6jddata fr\u00e5n SRTM
-function.downloadsrtm=Ladda ner SRTM-h\u00f6jddata
 function.getwikipedia=H\u00e4mta n\u00e4rliggande Wikipedia-artiklar
 function.searchwikipedianames=S\u00f6k i Wikipedia p\u00e5 namn
 function.searchosmpois=H\u00e4mta n\u00e4rliggande OSM-punkter
@@ -120,6 +123,7 @@ function.searchopencachingde=S\u00f6k p\u00e5 OpenCaching.de
 function.mapillary=S\u00f6k efter foton i Mapillary
 function.downloadosm=Ladda ner OSM-data f\u00f6r omr\u00e5det
 function.duplicatepoint=Duplicera punkt
+function.projectpoint=Projicera punkt
 function.setcolours=V\u00e4lj f\u00e4rger
 function.setdisplaysettings=V\u00e4lj visningsalternativ
 function.setlanguage=V\u00e4lj spr\u00e5k
@@ -163,9 +167,15 @@ dialog.openoptions.filesnippet=Filextrakt
 dialog.load.table.field=F\u00e4lt
 dialog.load.table.datatype=Datatyp
 dialog.load.table.description=Beskrivning
+dialog.delimiter.label=Avgr\u00e4nsare
 dialog.delimiter.comma=Komma ,
+dialog.delimiter.tab=Tabb
 dialog.delimiter.space=Mellanslag
+dialog.delimiter.semicolon=Semikolon ;
 dialog.delimiter.other=Annat
+dialog.openoptions.deliminfo.records=rader, med
+dialog.openoptions.deliminfo.norecords=Inga rader
+dialog.openoptions.deliminfo.fields=f\u00e4lt
 dialog.openoptions.altitudeunits=H\u00f6jdenhet
 dialog.openoptions.speedunits=Hastighetsenheter
 dialog.openoptions.vertspeedunits=Enheter f\u00f6r vertikal hastighet
@@ -180,6 +190,7 @@ dialog.jpegload.loadjpegsoutsidearea=Inkludera foton utanf\u00f6r aktuellt omr\u
 dialog.jpegload.progress.title=Laddar in foton
 dialog.jpegload.progress=V\u00e4nta medan foton s\u00f6ks
 dialog.gpsload.nogpsbabel=Inget GPSBabel-program kunde hittas. Forts\u00e4tt \u00e4nd\u00e5?
+dialog.gpsload.device=Enhetsnamn
 dialog.gpsload.format=Format
 dialog.gpsload.getwaypoints=Ladda in waypoints
 dialog.gpsload.gettracks=Ladda in sp\u00e5r
@@ -331,14 +342,309 @@ dialog.estimatetime.results.actualtime=Faktisk tid
 dialog.estimatetime.error.nodistance=Tidsuppskattningen kr\u00e4ver kopplade sp\u00e5rpunkter f\u00f6r att f\u00e5 avst\u00e5nd
 dialog.estimatetime.error.noaltitudes=Intervallet har ingen h\u00f6jd-data
 dialog.learnestimationparams.intro=Detta \u00e4r parametrarna ber\u00e4knade fr\u00e5n detta sp\u00e5ret
-dialog.rearrange.tostart=Alla till b\u00f6rjan av fil
-dialog.rearrange.toend=Alla till slut av fil
+dialog.learnestimationparams.averageerror=Medel-avvikelse
+dialog.learnestimationparams.combine=Dessa parametrar kan kombineras med nuvarande v\u00e4rden
+dialog.learnestimationparams.combinedresults=Kombinerat resultat
+dialog.learnestimationparams.weight.100pccurrent=Beh\u00e5ll nuvarande v\u00e4rden
+dialog.learnestimationparams.weight.current=nuvarande
+dialog.learnestimationparams.weight.calculated=ber\u00e4knat
+dialog.learnestimationparams.weight.50pc=Medel av nuvarande v\u00e4rden och ber\u00e4knade v\u00e4rden
+dialog.learnestimationparams.weight.100pccalculated=Anv\u00e4nd dom ber\u00e4knade v\u00e4rdena
+dialog.setmapbg.intro=V\u00e4lj en av kartorna, eller l\u00e4gg till en egen
+dialog.addmapsource.title=L\u00e4gg till en ny karta
+dialog.addmapsource.sourcename=Kartans namn
+dialog.addmapsource.layer1url=URL f\u00f6r f\u00f6rsta lagret
+dialog.addmapsource.layer2url=URL f\u00f6r andra lagret
+dialog.addmapsource.maxzoom=Max zoom-niv\u00e5
+dialog.addmapsource.noname=Namnl\u00f6s
+dialog.gpsies.column.name=Sp\u00e5rnamn
+dialog.gpsies.column.length=L\u00e4ngd
+dialog.gpsies.description=Beskrivning
+dialog.gpsies.nodescription=Ingen beskrivning
+dialog.gpsies.nonefound=Inga sp\u00e5r funna
+dialog.mapillary.nonefound=Inga foton funna
+dialog.wikipedia.column.name=Artikelnamn
+dialog.wikipedia.column.distance=Avst\u00e5nd
+dialog.wikipedia.nonefound=Inga punkter hittade p\u00e5 Wikipedia
+dialog.wikipedia.gallery=Galleri
+dialog.osmpois.column.name=Namn
+dialog.osmpois.column.type=Typ
+dialog.osmpois.nonefound=Inga punkter hittade
+dialog.correlate.photoselect.intro=V\u00e4lj en av dessa korrelerade foton att anv\u00e4nda som tids-offset
+dialog.correlate.select.photoname=Fotonamn
+dialog.correlate.select.timediff=Tidsskillnad
+dialog.correlate.select.photolater=Foto senare
+dialog.correlate.options.intro=V\u00e4lj inst\u00e4llningar f\u00f6r automatisk korrelation
+dialog.correlate.options.offsetpanel=tids-offset
+dialog.correlate.options.offset=Offset
+dialog.correlate.options.offset.hours=timmar,
+dialog.correlate.options.offset.minutes=minuter och
+dialog.correlate.options.offset.seconds=sekunder
+dialog.correlate.options.photolater=Foto senare \u00e4n punkt
+dialog.correlate.options.pointlaterphoto=Punkt senare \u00e4n foto
+dialog.correlate.options.audiolater=Ljud senare \u00e4n punkt
+dialog.correlate.options.pointlateraudio=Punkt senare \u00e4n ljud
+dialog.correlate.options.limitspanel=Korrelationsbegr\u00e4nsningar
+dialog.correlate.options.notimelimit=Ingen tidsbegr\u00e4nsning
+dialog.correlate.options.timelimit=Tidsbegr\u00e4nsning
+dialog.correlate.options.nodistancelimit=Ingen avst\u00e5ndsbegr\u00e4nsning
+dialog.correlate.options.distancelimit=Avst\u00e5ndsbegr\u00e4nsning
+dialog.correlate.options.correlate=Korrelera
+dialog.correlate.alloutsiderange=Alla objekt \u00e4r utanf\u00f6r tidsomf\u00e5nget f\u00f6r sp\u00e5ret, s\u00e5 inga kan korreleras.\nF\u00f6rs\u00f6k att \u00e4ndra offset eller manuellt korrelera \u00e5tminstone ett av objekten.
+dialog.correlate.filetimes=Filens tidsst\u00e4mpel markerar:
+dialog.correlate.filetimes2=av ljudklippet
+dialog.correlate.correltimes=F\u00f6r korrelering, anv\u00e4nd
+dialog.correlate.timestamp.beginning=B\u00f6rjan
+dialog.correlate.timestamp.middle=Mitten
+dialog.correlate.timestamp.end=Slutet
+dialog.correlate.audioselect.intro=V\u00e4lj en av dessa korrelerade ljud att anv\u00e4nda som tids-offset
+dialog.correlate.select.audioname=Ljudnamn
+dialog.correlate.select.audiolater=Ljud senare
+dialog.rearrangewaypoints.desc=V\u00e4lj vart och i vilken ordning waypoints ska hamna
+dialog.rearrangephotos.desc=V\u00e4lj vart och i vilken ordning foto-punkterna ska hamna
+dialog.rearrange.tostart=Alla till b\u00f6rjan
+dialog.rearrange.toend=Alla till slutet
 dialog.rearrange.tonearest=Varje till n\u00e4rmaste sp\u00e5rpunkt
+dialog.rearrange.nosort=Sortera inte
+dialog.rearrange.sortbyfilename=Sortera p\u00e5 filnamn
+dialog.rearrange.sortbyname=Sortera p\u00e5 namn
+dialog.rearrange.sortbytime=Sortera p\u00e5 tid
+dialog.compress.closepoints.title=Ta bort punkter f\u00f6r n\u00e4ra varandra
+dialog.compress.closepoints.paramdesc=Spannfaktor
+dialog.compress.wackypoints.title=Ta bort avvikande punkter
+dialog.compress.wackypoints.paramdesc=Avst\u00e5ndsfaktor
+dialog.compress.singletons.title=Ta bort Singletons
+dialog.compress.singletons.paramdesc=Avst\u00e5ndsfaktor
+dialog.compress.duplicates.title=Ta bort dubbletter
+dialog.compress.douglaspeucker.title=Douglas-Peucker-komprimering
+dialog.compress.douglaspeucker.paramdesc=Spannfaktor
+dialog.compress.summarylabel=Punkter att ta bort
+dialog.compress.confirm=%d punkter har markerats.\nVill du ta bort de markerade punkterna?
+dialog.compress.confirmnone=inga punkter har markerats
+dialog.deletemarked.nonefound=Inga datapunkter kunde tas bort
+dialog.pastecoordinates.desc=Fyll i koordinaterna h\u00e4r
+dialog.pastecoordinates.coords=Koordinater
+dialog.pastecoordinates.nothingfound=V\u00e4nligen kontrollera koordinaterna och f\u00f6rs\u00f6k igen
+dialog.pastecoordinatelist.desc=Fyll i koordinaterna f\u00f6r dom nya punkterna, en punkt per rad
+dialog.pluscode.desc=Fyll i pluss-koden (Open Location Code) h\u00e4r
+dialog.pluscode.code=Pluss-kod
+dialog.pluscode.nothingfound=Kontrollera koden och f\u00f6rs\u00f6k igen.
+dialog.help.help=L\u00e4s\n https://gpsprune.activityworkshop.net/\nf\u00f6r mer information och tips,\ninklusive en PDF-anv\u00e4ndarhandbok som man kan k\u00f6pa.
+dialog.about.version=Version
+dialog.about.build=Build
+dialog.about.summarytext1=GpsPrune \u00e4r ett program f\u00f6r att ladda in, visa och redigera data fr\u00e5n GPS-mottagare.
+dialog.about.summarytext2=Det \u00e4r publicerat under Gnu GPL f\u00f6r fri, \u00f6ppen, v\u00e4rldsomsp\u00e4nnande anv\u00e4ndning och f\u00f6rb\u00e4ttring.<br>Kopiering, \u00e5terutgivning och modifieringar \u00e4r till\u00e5tna och uppmuntras<br>enligt villkoren i den inkluderade filen <code>license.txt</code>.
+dialog.about.summarytext3=Bes\u00f6k <code style="font-weight:bold">https://activityworkshop.net/</code> f\u00f6r mer information och tips, inklusive<br>en PDF-anv\u00e4ndarhandbok som du kan k\u00f6pa.
+dialog.about.languages=Tillg\u00e4ngliga spr\u00e5k
+dialog.about.translatedby=\u00d6versatt till svenska av erikiiofph7.
+dialog.about.systeminfo=System-info
+dialog.about.systeminfo.os=Operativsystem
+dialog.about.systeminfo.java=Java Runtime
+dialog.about.systeminfo.java3d=Java3d installerat
+dialog.about.systeminfo.povray=Povray installerat
+dialog.about.systeminfo.exiftool=Exiftool installerat
+dialog.about.systeminfo.gpsbabel=Gpsbabel installerat
+dialog.about.systeminfo.gnuplot=Gnuplot installerat
+dialog.about.yes=Ja
+dialog.about.no=Nej
+dialog.about.credits=Tillk\u00e4nnagivanden
+dialog.about.credits.code=GpsPune:s kod skriven av
+dialog.about.credits.exifcode=Exif:s kad skriven av
+dialog.about.credits.icons=N\u00e5gra ikoner tagna fr\u00e5n
+dialog.about.credits.translators=\u00d6vers\u00e4ttare
+dialog.about.credits.translations=\u00d6vers\u00e4ttningar hj\u00e4lpta av
+dialog.about.credits.devtools=Utvecklingsverktyg
+dialog.about.credits.othertools=\u00d6vriga verktyg
+dialog.about.credits.thanks=Tack till
+dialog.about.readme=L\u00e4sMig
+dialog.checkversion.error=Versionsnummret kunde inte kollas upp.\nVar god kontrollera din internetuppkoppling.
+dialog.checkversion.uptodate=Du anv\u00e4nder den senaste versionen av GpsPrune.
+dialog.checkversion.newversion1=En ny version av GpsPrune finns nu tillg\u00e4nglig! Den senaste versionen \u00e4r
+dialog.checkversion.newversion2=.
+dialog.checkversion.releasedate1=Denna nya version sl\u00e4pptes
+dialog.checkversion.releasedate2=.
+dialog.checkversion.download=F\u00f6r att ladda ner den nya versionen g\u00e5 till https://gpsprune.activityworkshop.net/download.html.
+dialog.keys.intro=Du kan anv\u00e4nda dessa snabbkommandon ist\u00e4llet f\u00f6r att anv\u00e4nda musen
+dialog.keys.keylist=<table><tr><td>Piltangenter</td><td>Panorera kartan \u00e5t v\u00e4nster, h\u00f6ger, upp, ner</td></tr><tr><td>Ctrl + v\u00e4nster, h\u00f6ger piltangent</td><td>Markera f\u00f6reg\u00e5ende eller n\u00e4sta punkt</td></tr><tr><td>Ctrl + pil upp, pil ner</td><td>Zooma in eller ut</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Markera f\u00f6reg\u00e5ende, n\u00e4sta segment</td></tr><tr><td>Ctrl + Home, End</td><td>Markera f\u00f6rsta, sista punkt</td></tr><tr><td>Del</td><td>Radera markerad punkt</td></tr></table>
+dialog.keys.normalmodifier=Ctrl
+dialog.keys.macmodifier=Command
+dialog.paths.prune.gnuplotpath=S\u00f6kv\u00e4g till gnuplot
+dialog.paths.prune.gpsbabelpath=S\u00f6kv\u00e4g till gpsbabel
+dialog.paths.prune.exiftoolpath=S\u00f6kv\u00e4g till exiftool
+dialog.setpaths.intro=Om du beh\u00f6ver kan du ange s\u00f6kv\u00e4gar till dom externa programmen:
+dialog.setpaths.found=S\u00f6kv\u00e4g hittad?
+dialog.addaltitude.noaltitudes=Det valda intervallet inneh\u00e5ller ingen h\u00f6jddata
+dialog.addaltitude.desc=H\u00f6jd-offset att addera
+dialog.lookupsrtm.overwritezeros=Skriva \u00f6ver h\u00f6jd-v\u00e4rden som \u00e4r noll?
+dialog.setcolours.intro=Klicka p\u00e5 en f\u00e4rg-ruta f\u00f6r att \u00e4ndra f\u00e4rg
+dialog.setcolours.background=Bakgrund
+dialog.setcolours.borders=Kanter
+dialog.setcolours.lines=Linjer
+dialog.setcolours.primary=Prim\u00e4r
+dialog.setcolours.secondary=Sekund\u00e4r
+dialog.setcolours.point=Punkter
+dialog.setcolours.selection=Markering
+dialog.setcolours.text=Text
+dialog.colourchooser.title=V\u00e4lj f\u00e4rg
+dialog.colourchooser.red=R\u00f6d
+dialog.colourchooser.green=Gr\u00f6n
+dialog.colourchooser.blue=Bl\u00e5
+dialog.colourer.intro=En punkt-m\u00e5lare kan ge sp\u00e5rpunkter olika f\u00e4rg
+dialog.colourer.type=M\u00e5lar-typ
+dialog.colourer.type.none=Ingen
+dialog.colourer.type.byfile=Efter fil
+dialog.colourer.type.bysegment=Efter segment
+dialog.colourer.type.byaltitude=Efter h\u00f6jd
+dialog.colourer.type.byspeed=Efter hastighet
+dialog.colourer.type.byvertspeed=Efter vertikal hastighet
+dialog.colourer.type.bygradient=Efter gradient
+dialog.colourer.type.bydate=Efter datum
+dialog.colourer.start=Start-f\u00e4rg
+dialog.colourer.end=Slut-f\u00e4rg
+dialog.colourer.maxcolours=Max antal f\u00e4rger
+dialog.setlanguage.firstintro=Du kan antingen v\u00e4lja ett av dom inkluderade spr\u00e5ken<p>eller v\u00e4lja att anv\u00e4nda en text-fil ist\u00e4llet.
+dialog.setlanguage.secondintro=Du m\u00e5ste spara dina inst\u00e4llningar och sedan<p>starta om GpsPrune f\u00f6r att \u00e4ndringarna ska verkst\u00e4llas.
+dialog.setlanguage.language=Spr\u00e5k
+dialog.setlanguage.languagefile=Spr\u00e5k-fil
+dialog.setlanguage.endmessage=Spara dina inst\u00e4llningar nu och starta om GpsPrune\nf\u00f6r att \u00e4ndringarna ska verkst\u00e4llas.
+dialog.setlanguage.endmessagewithautosave=Var god starta om GpsPrune f\u00f6r att bytet av spr\u00e5k ska ske.
+dialog.diskcache.save=Spara kartbilder till h\u00e5rddisk
+dialog.diskcache.dir=Cache-mapp
+dialog.diskcache.createdir=Skapa mapp
+dialog.diskcache.nocreate=Ingen cache-mapp skapad
+dialog.diskcache.cannotwrite=Kart-rutor ("tiles") kan inte sparas i den valda mappen
+dialog.diskcache.table.path=Mapp
+dialog.diskcache.table.usedby=Anv\u00e4nds av
+dialog.diskcache.table.zoom=Zoom
+dialog.diskcache.table.tiles=Kart-rutor ("tiles")
+dialog.diskcache.table.megabytes=Megabytes
+dialog.diskcache.tileset=Upps\u00e4ttning kart-rutor ("tiles")
+dialog.diskcache.tileset.multiple=multipla
+dialog.diskcache.deleteold=Ta bort gamla kart-rutor ("tiles")
+dialog.diskcache.maximumage=Max antal dagar gammal
+dialog.diskcache.deleteall=Ta bort alla kart-rutor ("tiles")
+dialog.diskcache.deleted=Ta bort %d filer fr\u00e5n cachen
+dialog.deletefieldvalues.intro=V\u00e4lj f\u00e4lt att ta bort f\u00f6r det valda intervallet
+dialog.deletefieldvalues.nofields=Det finns inga f\u00e4lt att ta bort f\u00f6r detta intervallet
+dialog.displaysettings.linewidth=Linjetjocklek f\u00f6r sp\u00e5ren (1-4)
+dialog.displaysettings.antialias=Anv\u00e4nd kantutj\u00e4mning ("antialiasing")
+dialog.displaysettings.waypointicons=Waypoint-ikon
+dialog.displaysettings.wpicon.default=Standardmark\u00f6r
+dialog.displaysettings.wpicon.ringpt=Rund mark\u00f6r
+dialog.displaysettings.wpicon.plectrum=Plektrum
+dialog.displaysettings.wpicon.ring=Ring
+dialog.displaysettings.wpicon.pin=H\u00e4ftstift
+dialog.displaysettings.size.small=Liten
+dialog.displaysettings.size.medium=Mellan
+dialog.displaysettings.size.large=Stor
+dialog.displaysettings.windowstyle=F\u00f6nster-stil (kr\u00e4ver omstart)
+dialog.displaysettings.windowstyle.default=Standard
+dialog.displaysettings.windowstyle.nimbus=Nimbus
+dialog.displaysettings.windowstyle.gtk=GTK
+dialog.downloadosm.desc=Bekr\u00e4fta att OSM-r\u00e5data laddas ner f\u00f6r det specifika omr\u00e5det:
+dialog.searchwikipedianames.search=S\u00f6k efter:
+dialog.weather.location=Plats
+dialog.weather.update=Prognos uppdaterad
+dialog.weather.sunrise=Soluppg\u00e5ng
+dialog.weather.sunset=Solnedg\u00e5ng
+dialog.weather.temperatureunits=Temperaturer
+dialog.weather.currentforecast=Nuvarande v\u00e4der
+dialog.weather.dailyforecast=Flerdygnsprognos
+dialog.weather.3hourlyforecast=3-timmars-prognos
+dialog.weather.day.now=Nuvarande v\u00e4der
+dialog.weather.day.today=Idag
+dialog.weather.day.tomorrow=Imorgon
+dialog.weather.day.monday=M\u00e5ndag
+dialog.weather.day.tuesday=Tisdag
+dialog.weather.day.wednesday=Onsdag
+dialog.weather.day.thursday=Torsdag
+dialog.weather.day.friday=Fredag
+dialog.weather.day.saturday=L\u00f6rdag
+dialog.weather.day.sunday=S\u00f6ndag
+dialog.weather.wind=Vind
+dialog.weather.temp=Temp
+dialog.weather.humidity=Fuktighet
+dialog.weather.creditnotice=Denna data har tillhandah\u00e5llits av openweathermap.org. Mer detaljer finns p\u00e5 deras hemsida.
+dialog.deletebydate.onlyonedate=Alla punkter \u00e4r fr\u00e5n samma datum.
+dialog.deletebydate.intro=F\u00f6r varje datum i sp\u00e5ret kan du v\u00e4lja att ta bort eller beh\u00e5lla punkterna
+dialog.deletebydate.nodate=Ingen tidsst\u00e4mpel
+dialog.deletebydate.column.keep=Beh\u00e5ll
+dialog.deletebydate.column.delete=Ta bort
+dialog.settimezone.intro=H\u00e4r kan du v\u00e4lja i vilken tidszon du vill visa punkternas tidsst\u00e4mplar
+dialog.settimezone.system=Anv\u00e4nd systemets tidszon
+dialog.settimezone.custom=Anv\u00e4nd f\u00f6ljande tidszon:
+dialog.settimezone.list.toomany=F\u00f6r m\u00e5nga f\u00f6r att v\u00e4lja
+dialog.settimezone.selectedzone=Vald tidszon
+dialog.settimezone.offsetfromutc=Offset fr\u00e5n UTC
+dialog.autoplay.duration=L\u00e4ngd (sekunder)
+dialog.autoplay.usetimestamps=Anv\u00e4nd tidsst\u00e4mplar fr\u00e5n punkter
+dialog.autoplay.rewind=Tillbaka till b\u00f6rjan
+dialog.autoplay.pause=Paus
+dialog.autoplay.play=Spela upp
+dialog.markers.halves=Halvv\u00e4gs-punkter
+dialog.markers.half.distance=Halva avst\u00e5ndet
+dialog.markers.half.climb=Halva kl\u00e4ttringen
+dialog.markers.half.descent=Halva nedstigningen
+dialog.projectpoint.desc=Fyll i riktning och avst\u00e5nd f\u00f6r att projicera denna punkt
+dialog.projectpoint.bearing=B\u00e4ring (grader fr\u00e5n N)
+
+# 3d window
+dialog.3d.title=GpsPrune 3D-vy
+dialog.3d.altitudefactor=F\u00f6rst\u00e4rkningsfaktor f\u00f6r h\u00f6jddata
+
+# Confirm messages
+confirm.loadfile=Data inladdat fr\u00e5n filen
+confirm.save.ok1=Det gick bra att spara
+confirm.save.ok2=punkter till filen
+confirm.deletepoint.single=datapunkt togs bort
+confirm.deletepoint.multi=datapunkter togs bort
+confirm.point.edit=punkt redigerad
+confirm.mergetracksegments=Sp\u00e5r-segment ihopslagna
+confirm.reverserange=Intervall v\u00e4ndes
+confirm.addtimeoffset=Tids-offset tillagt
+confirm.addaltitudeoffset=H\u00f6jdoffset tillagt
+confirm.rearrangewaypoints=Waypoint:ar omarrangerade
+confirm.rearrangephotos=Foton omarrangerade
+confirm.splitsegments=segment delades upp i %d delar
+confirm.sewsegments=segment slogs ihop p\u00e5 %d st\u00e4llen
+confirm.cutandmove=Intervall flyttat
+confirm.pointsadded=%d punkter adderade
+confirm.convertnamestotimes=Waypoint-namn konverterade
+confirm.saveexif.ok=%d foton sparade
+confirm.undo.single=operation \u00e5terst\u00e4lld
+confirm.undo.multi=operationer \u00e5terst\u00e4llda
+confirm.jpegload.single=foto lades till
+confirm.jpegload.multi=foton lades till
+confirm.media.connect=media kopplats
+confirm.photo.disconnect=foto kopplats bort
+confirm.audio.disconnect=ljud kopplats bort
+confirm.media.removed=togs bort
+confirm.correlatephotos.single=foto korrelerat
+confirm.correlatephotos.multi=foton korrelerade
+confirm.createpoint=punkt skapad
+confirm.rotatephoto=foto roterat
+confirm.running=P\u00e5g\u00e5r...
+confirm.lookupsrtm=Hittade %d h\u00f6jd-datav\u00e4rden
+confirm.downloadsrtm=Laddade ner %d filer till cache:n
+confirm.downloadsrtm.1=Laddade ner %d fil till cache:n
+confirm.deletefieldvalues=F\u00e4lt-v\u00e4rden borttagna
+confirm.audioload=Ljud-filer adderade
+confirm.correlateaudios.single=ljud korrelerat
+confirm.correlateaudios.multi=ljud korrelerade
+
+# Tips, shown just once when appropriate
+tip.title=Tips
+tip.useamapcache=Genom att definiera en cache-mapp (Inst\u00e4llningar -> Spara kartor p\u00e5 h\u00e5rddisken)\nkan du snabba upp visningen och reducera datatrafiken.
+tip.learntimeparams=Resultaten f\u00e5r b\u00e4ttre nogrannhet om man f\u00f6rst anv\u00e4nder\nSp\u00e5r -> L\u00e4r upp tidsuppskattningsparametrar\np\u00e5 dina sp\u00e5r.
+tip.downloadsrtm=Du kan snabba upp detta genom att definiera en cachemapp\nf\u00f6r att spara SRTM-data lokalt.
+tip.usesrtmfor3d=Detta sp\u00e5r saknar h\u00f6jddata.\nDu kan anv\u00e4nda SRTM-funktioner f\u00f6r att f\u00e5 uppskattad\nh\u00f6jddata f\u00f6r 3d-vyn.
+tip.manuallycorrelateone=Genom att manuellt koppla \u00e5tminstone ett objekt kan tids-offset ber\u00e4knas f\u00f6r dig.
 
 # Buttons
 button.ok=OK
 button.back=Bak\u00e5t
 button.next=N\u00e4sta
+button.finish=Avsluta
 button.cancel=Avbryt
 button.overwrite=Skriv \u00f6ver
 button.moveup=Flytta upp
@@ -346,19 +652,20 @@ button.movedown=Flytta ner
 button.edit=Redigera
 button.exit=Avsluta
 button.close=St\u00e4ng
+button.continue=Forts\u00e4tta
 button.yes=Ja
 button.no=Nej
 button.yestoall=Ja till alla
 button.notoall=Nej till alla
 button.always=Alltid
-button.select=Markera
-button.selectall=Markera alla
-button.selectnone=Markera inget
+button.select=V\u00e4lj
+button.selectall=V\u00e4lj alla
+button.selectnone=V\u00e4lj ingen
 button.preview=F\u00f6rhandsvisa
 button.load=Ladda in
-button.upload=Ladda upp
 button.guessfields=Gissa f\u00e4lt
 button.showwebpage=Visa hemsida
+button.check=Kontrollera
 button.resettodefaults=\u00c5terst\u00e4ll till default
 button.browse=Bl\u00e4ddra...
 button.addnew=L\u00e4gg till ny
@@ -366,6 +673,18 @@ button.delete=Ta bort
 button.manage=Hantera
 button.combine=Kombinera
 
+# File types
+filetype.txt=TXT-filer
+filetype.jpeg=JPG-filer
+filetype.kmlkmz=KML- & KMZ-filer
+filetype.kml=KML-filer
+filetype.kmz=KMZ-filer
+filetype.gpx=GPX-filer
+filetype.pov=POV-filer
+filetype.svg=SVG-filer
+filetype.png=PNG-filer
+filetype.audio=MP3-, OGG- & WAV-filer
+
 # Display components
 display.nodata=Ingen data laddad
 display.noaltitudes=Sp\u00e5rdatan saknar h\u00f6jddata
@@ -388,8 +707,8 @@ details.range.to=till
 details.altitude.to=till
 details.range.climb=Uppf\u00f6r
 details.range.descent=Nedf\u00f6r
-details.coordformat=Koortinat-format
-details.distanceunits=Distans-enhet
+details.coordformat=Koordinatformat
+details.distanceunits=Distansenhet
 display.range.time.secs=s
 display.range.time.mins=m
 display.range.time.hours=h
@@ -397,6 +716,7 @@ display.range.time.days=d
 details.range.avespeed=Medelhastighet
 details.range.maxspeed=Maxhastighet
 details.range.numsegments=Antal segment
+details.range.pace=Tempo
 details.range.gradient=Gradient
 details.lists.waypoints=Waypoints
 details.lists.photos=Foton
@@ -413,5 +733,131 @@ details.audio.file=Ljudfil
 details.audio.playing=spelar...
 map.overzoom=Inga kartor tillg\u00e4ngliga p\u00e5 denna zoom-niv\u00e5
 
+# Field names
+fieldname.latitude=Latitud
+fieldname.longitude=Longitud
+fieldname.coordinates=Koordinaterna
+fieldname.altitude=Altitud
+fieldname.timestamp=Tidpunkt
+fieldname.time=Tid
+fieldname.date=Datum
+fieldname.waypointname=Namn
+fieldname.waypointtype=Typ
+fieldname.newsegment=Segment
+fieldname.custom=Anpassad
+fieldname.prefix=F\u00e4lt
+fieldname.distance=Distans
+fieldname.duration=Varaktighet
+fieldname.speed=Hastighet
+fieldname.verticalspeed=Vertikal hastighet
+fieldname.description=Beskrivning
+fieldname.comment=Kommentar
+fieldname.mediafilename=Filnamn
+
+# Measurement units
+units.original=Original
+units.default=Default
+units.metres=Meter
+units.metres.short=m
+units.feet=Fot
+units.feet.short=fot
+units.kilometres=Kilometer
+units.kilometres.short=km
+units.kilometresperhour=km per timme
+units.kilometresperhour.short=km/h
+units.miles=Miles
+units.miles.short=mi
+units.milesperhour=miles per timme
+units.milesperhour.short=mph
+units.nauticalmiles=Nautiska mil
+units.nauticalmiles.short=N.m.
+units.nauticalmilesperhour.short=knop
+units.metrespersec=meter per sekund
+units.metrespersec.short=m/s
+units.feetpersec=fot per sekund
+units.feetpersec.short=fot/s
+units.hours=timmar
+units.minutes=minuter
+units.seconds=sekunder
+units.degminsec=Grad-min-sek
+units.degmin=Grad-min
+units.deg=Grader
+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=och
+logic.or=eller
+
 # External urls and services
+url.googlemaps=maps.google.se
+wikipedia.lang=sv
 openweathermap.lang=se
+webservice.peakfinder=\u00d6ppna Peakfinder.org
+webservice.geohack=\u00d6ppna Geohack-sidan
+
+# Cardinals for 3d plots
+cardinal.n=N
+cardinal.s=S
+cardinal.e=\u00d6
+cardinal.w=V
+
+# Undo operations
+undo.load=Ladda data
+undo.loadphotos=Ladda foton
+undo.loadaudios=Ladda ljudklipp
+undo.editpoint=redigera punkt
+undo.deletepoint=ta bort punkt
+undo.removephoto=ta bort foto
+undo.removeaudio=ta bort ljudklipp
+undo.deleterange=ta bort intervall
+undo.croptrack=besk\u00e4r sp\u00e5r
+undo.deletemarked=ta bort punkter
+undo.insert=infoga punkter
+undo.reverse=v\u00e4nd intervall
+undo.mergetracksegments=sl\u00e5 ihop sp\u00e5rsegment
+undo.splitsegments=dela upp sp\u00e5ret i segment
+undo.sewsegments=sy ihop sp\u00e5rsegment
+undo.addtimeoffset=l\u00e4gg till tids-offset
+undo.addaltitudeoffset=l\u00e4gg till h\u00f6jd-offset
+undo.rearrangewaypoints=omarrangera waypoints
+undo.cutandmove=flytta intervall
+undo.connect=koppla
+undo.disconnect=koppla bort
+undo.correlatephotos=korrelera foton
+undo.rearrangephotos=omarrangera foton
+undo.createpoint=skapa punkt
+undo.rotatephoto=rotera foto
+undo.convertnamestotimes=konvertera namn till tidpunkter
+undo.lookupsrtm=kolla upp h\u00f6jddata via SRTM
+undo.deletefieldvalues=ta bort f\u00e4lt-v\u00e4rden
+undo.correlateaudios=korrelera ljudklipp
+
+# Error messages
+error.save.dialogtitle=Fel vid sparande av data
+error.save.nodata=Ingen data att spara
+error.save.failed=Misslyckades att spara datan till fil
+error.saveexif.filenotfound=Misslyckades att hitta foto-fil
+error.saveexif.cannotoverwrite1=Foto-fil
+error.saveexif.cannotoverwrite2=\u00e4r skrivskyddad och kan inte skrivas \u00f6ver. Spara som kopia?
+error.saveexif.failed=Misslyckades att spara %d foton.
+error.saveexif.forced=%d foton kr\u00e4vde forcering
+error.load.dialogtitle=Fel vid l\u00e4sning av data
+error.load.noread=Kan inte l\u00e4sa fil
+error.load.nopoints=Ingen koordinat-information hittades i filen
+error.load.unknownxml=K\u00e4nde inte igen xml-formatet:
+error.load.noxmlinzip=Ingen xml-fil hittades inuti zip-filen
+error.load.othererror=Fel vid l\u00e4sning av fil:
+error.load.nopointsintext=Ingen koordinat-information kunde hittas
+error.jpegload.dialogtitle=Fel vid l\u00e4sning av foton
+error.jpegload.nofilesfound=Ingen fil hittades
+error.jpegload.nojpegsfound=Ingen jpeg-fil hittades
+error.jpegload.nogpsfound=Ingen GPS-information hittades
+error.audioload.nofilesfound=Inga ljud-klipp hittades
+error.gpsload.unknown=Ok\u00e4nt fel
+error.undofailed.title=\u00c5ngra misslyckades
+error.function.notavailable.title=Funktionen \u00e4r inte tillg\u00e4nglig
+error.readme.notfound=Ingen L\u00e4sMig-fil hittades
index 998c1126ad08b6afb2ddbffdff56305ad867eec4..72a718fd8e903b60c2b3c8559a10c4b84f55d1b7 100644 (file)
@@ -85,10 +85,8 @@ function.pastecoordinates=Yeni korrdinatlar gir
 function.charts=Krokiler
 function.show3d=3B g\u00fcr\u00fcnt\u00fcs\u00fc
 function.distances=Uzakl\u0131klar
-function.fullrangedetails=S\u0131ran\u0131n b\u00fct\u00fcn ayr\u0131nt\u0131lar
 function.setmapbg=Arkafonun haritas\u0131 se\u00e7
 function.setpaths=Uygulamalar\u0131n yollar\u0131 ayarla
-function.getgpsies=Gpsies.com'dan yolu al
 function.duplicatepoint=Noktay\u0131 kopyala
 function.setcolours=Renkleri ayarla
 function.setlanguage=Dil se\u00e7
@@ -213,13 +211,6 @@ dialog.gpsies.column.length=Uzunlu\u011fu
 dialog.gpsies.description=A\u00e7\u0131klama
 dialog.gpsies.nodescription=A\u00e7\u0131klama yok
 dialog.gpsies.nonefound=Herhangi bir yol bulunmad\u0131
-dialog.gpsies.activities=Etkinlik
-dialog.gpsies.activity.trekking=Y\u00fcr\u00fcy\u00fc\u015f
-dialog.gpsies.activity.walking=Y\u00fcr\u00fcme
-dialog.gpsies.activity.jogging=Ko\u015fma
-dialog.gpsies.activity.biking=Bisiklet
-dialog.gpsies.activity.sailing=Yelken
-dialog.gpsies.activity.skating=Paten
 dialog.correlate.select.photoname=Foto ad\u0131
 dialog.correlate.select.photolater=Foto sonra
 dialog.correlate.options.offset.hours=saat,
@@ -256,19 +247,9 @@ dialog.checkversion.releasedate2=.
 dialog.checkversion.download=Yeni s\u00fcr\u00fcm indirmek i\u00e7in https://gpsprune.activityworkshop.net/download.html adresine git.
 dialog.keys.intro=Fare yerinde a\u015fa\u011f\u0131daki k\u0131sayol tu\u015flar\u0131 kullanabilirsin:
 dialog.keys.keylist=<table><tr><td>Ok tu\u015flar\u0131</td><td>Haritay\u0131 sola/sa\u011fa/a\u015fa\u011f\u0131/yukar\u0131 kayd\u0131r</td></tr><tr><td>Ctrl + sol, sa\u011f</td><td>\u00d6nceki/sonraki noktay\u0131 se\u00e7</td></tr><tr><td>Ctrl + yukar/a\u015fa\u011f\u0131</td><td>Yak\u0131nla\u015ft\u0131r/Uzakla\u015ft\u0131r</td></tr><tr><td>Del</td><td>Se\u00e7ili noltay\u0131 sil</td></tr></table>
-dialog.saveconfig.desc=A\u011fa\u015f\u0131daki ayarlar\u0131 bir dasyada kaydedilir:
-dialog.saveconfig.prune.trackdirectory=\u0130z klas\u00f6r\u00fc
-dialog.saveconfig.prune.photodirectory=Foto klas\u00f6r\u00fc
-dialog.saveconfig.prune.languagecode=Dil kodu (TR)
-dialog.saveconfig.prune.gpsdevice=GPS ayg\u0131t
-dialog.saveconfig.prune.gpsformat=GPS bi\u00e7imi
-dialog.saveconfig.prune.povrayfont=Povray yaz\u0131tipi
-dialog.saveconfig.prune.gnuplotpath=gnuplot'un yeriyolu
-dialog.saveconfig.prune.gpsbabelpath=gpsbabel'in yeriyolu
-dialog.saveconfig.prune.exiftoolpath=exiftool'un yeriyolu
-dialog.saveconfig.prune.mapserverindex=Harita sunucunun index
-dialog.saveconfig.prune.mapserverurl=Harita sunucunun adresi
-dialog.saveconfig.prune.kmzimagewidth=KMZ resim geni\u015fli\u011fi
+dialog.paths.prune.gnuplotpath=gnuplot'un yeriyolu
+dialog.paths.prune.gpsbabelpath=gpsbabel'in yeriyolu
+dialog.paths.prune.exiftoolpath=exiftool'un yeriyolu
 dialog.setpaths.intro=\u0130ste\u011fe ba\u011fl\u0131 a\u015fa\u011f\u0131daki uygulamalar\u0131n veriyolu kaydedebilirsin:
 dialog.addaltitude.noaltitudes=Se\u00e7ili s\u0131rada y\u00fckseklik bilgisi bulunmad\u0131
 dialog.addaltitude.desc=Eklenecek y\u00fckseklik ofseti
index 15b6b531597aebcc9d145366cfa5ab936e77f282..8e82e11ea284092c640651e56a335661cc5df77d 100644 (file)
@@ -98,7 +98,6 @@ function.pastecoordinates=\u0412\u0432\u0435\u0434\u0435\u043d\u043d\u044f \u043
 function.charts=\u0413\u0440\u0430\u0444\u0456\u043a\u0438
 function.show3d=3D-\u0432\u0438\u0433\u043b\u044f\u0434
 function.distances=\u0412\u0456\u0434\u0441\u0442\u0430\u043d\u0456
-function.fullrangedetails=\u0414\u0435\u0442\u0430\u043b\u0456\u0437\u0430\u0446\u0456\u044f \u043f\u043e \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0443
 function.estimatetime=\u041f\u0440\u0438\u0431\u043b\u0438\u0437\u043d\u0438\u0439 \u0447\u0430\u0441
 function.learnestimationparams=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 \u0432\u0433\u0430\u0434\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0440\u0438\u0431\u043b\u0438\u0437\u043d\u043e\u0433\u043e \u0447\u0430\u0441\u0443
 function.setmapbg=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u043c\u0430\u043f\u0443-\u043f\u0456\u0434\u043a\u043b\u0430\u0434\u043a\u0443
@@ -106,10 +105,7 @@ function.setpaths=\u0412\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0438 \
 function.selectsegment=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u043f\u043e\u0442\u043e\u0447\u043d\u0438\u0439 \u0441\u0435\u0433\u043c\u0435\u043d\u0442
 function.splitsegments=\u0420\u043e\u0437\u0431\u0438\u0442\u0438 \u0442\u0440\u0435\u043a \u043d\u0430 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0438
 function.sewsegments=\u0417\u0448\u0438\u0442\u0438 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0438 \u0442\u0440\u0435\u043a\u0456\u0432 \u0440\u0430\u0437\u043e\u043c
-function.getgpsies=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0442\u0440\u0435\u043a\u0438 \u0437 Gpsies
-function.uploadgpsies=\u0412\u0438\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0442\u0440\u0435\u043a\u0438 \u043d\u0430 Gpsies
 function.lookupsrtm=\u041e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 \u0432\u0438\u0441\u043e\u0442\u0438 \u0437 SRTM
-function.downloadsrtm=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 SRTM-\u0442\u0430\u0439\u043b\u0438
 function.getwikipedia=\u041e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 \u043d\u0430\u0439\u0431\u043b\u0438\u0436\u0447\u0443 \u0441\u0442\u0430\u0442\u0442\u044e \u0437 \u0412\u0456\u043a\u0456\u043f\u0435\u0434\u0456\u0457
 function.searchwikipedianames=\u041f\u043e\u0448\u0443\u043a \u0441\u0442\u0430\u0442\u0435\u0439 \u0437 \u0412\u0456\u043a\u0456\u043f\u0435\u0434\u0456\u0457 \u0437\u0430 \u043d\u0430\u0437\u0432\u043e\u044e
 function.downloadosm=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 OSM-\u0434\u0430\u043d\u0456 \u043d\u0430 \u0442\u0435\u0440\u0438\u0442\u043e\u0440\u0456\u044e
index c42fe08d5d6807fc3b5caee3f7447e06403554e4..fb68fb23358491e03e3f9dc4722792d8cc796606 100644 (file)
@@ -98,7 +98,6 @@ function.pastecoordinates=\u8f93\u5165\u65b0\u5750\u6807
 function.charts=\u56fe\u8868
 function.show3d=3D\u89c6\u56fe
 function.distances=\u8ddd\u79bb
-function.fullrangedetails=\u5168\u822a\u6bb5\u8be6\u7ec6\u4fe1\u606f
 function.estimatetime=\u4f30\u8ba1\u65f6\u95f4
 function.learnestimationparams=\u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4
 function.setmapbg=\u80cc\u666f\u5730\u56fe
@@ -106,10 +105,7 @@ function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84
 function.selectsegment=\u9009\u4e2d\u5f53\u524d\u8f68\u8ff9\u6bb5
 function.splitsegments=\u5206\u5272\u8f68\u8ff9
 function.sewsegments=\u63a5\u5408\u8f68\u8ff9\u7247\u6bb5
-function.getgpsies=\u83b7\u53d6Gpsies\u8f68\u8ff9
-function.uploadgpsies=\u4e0a\u4f20\u8f68\u8ff9\u5230Gpsies
 function.lookupsrtm=\u4eceSRTM\u83b7\u5f97\u9ad8\u5ea6\u4fe1\u606f
-function.downloadsrtm=\u4e0b\u8f7dSRTM\u6570\u636e
 function.getwikipedia=\u7ef4\u57fa\u767e\u79d1\u6709\u5173\u672c\u5730\u6587\u7ae0
 function.searchwikipedianames=\u6309\u540d\u5b57\u4ece\u7ef4\u57fa\u767e\u79d1\u67e5\u627e
 function.downloadosm=\u4e0b\u8f7d\u6b64\u5730OSM\u6570\u636e
@@ -358,19 +354,6 @@ dialog.gpsies.column.length=\u957f\u5ea6
 dialog.gpsies.description=\u63cf\u8ff0
 dialog.gpsies.nodescription=\u65e0\u63cf\u8ff0
 dialog.gpsies.nonefound=\u672a\u627e\u5230\u8f68\u8ff9
-dialog.gpsies.username=Gpsies\u7f51\u7ad9\u7528\u6237\u540d
-dialog.gpsies.password=Gpsies\u7f51\u7ad9\u5bc6\u7801
-dialog.gpsies.keepprivate=\u4e0d\u516c\u5f00\u8f68\u8ff9
-dialog.gpsies.confirmopenpage=\u6253\u5f00\u4e0a\u4f20\u8f68\u8ff9\u7684\u7f51\u7ad9\uff1f
-dialog.gpsies.activities=\u6d3b\u52a8\u7c7b\u578b
-dialog.gpsies.activity.trekking=\u5f92\u6b65
-dialog.gpsies.activity.walking=\u6b65\u884c
-dialog.gpsies.activity.jogging=\u8dd1\u6b65
-dialog.gpsies.activity.biking=\u81ea\u884c\u8f66
-dialog.gpsies.activity.motorbiking=\u7535\u52a8\u81ea\u884c\u8f66
-dialog.gpsies.activity.snowshoe=\u96ea\u978b\u5065\u884c
-dialog.gpsies.activity.sailing=\u5e06\u8239
-dialog.gpsies.activity.skating=\u6ed1\u51b0
 dialog.wikipedia.column.name=\u6587\u7ae0\u9898\u76ee
 dialog.wikipedia.column.distance=\u8ddd\u79bb
 dialog.wikipedia.nonefound=\u672a\u627e\u5230\u7ef4\u57fa\u767e\u79d1\u6761\u76ee
@@ -471,25 +454,9 @@ dialog.keys.intro=\u53ef\u7528\u4e0b\u5217\u5feb\u6377\u952e\u66ff\u4ee3\u9f20\u
 dialog.keys.keylist=<table><tr><td>\u7bad\u5934</td><td>\u4e0a\u4e0b\u5de6\u53f3\u79fb\u52a8\u5730\u56fe</td></tr><tr><td>Ctrl + \u5de6\u53f3\u7bad\u5934</td><td>\u9009\u53d6\u524d\uff0c\u540e\u70b9</td></tr><tr><td>Ctrl + \u4e0a\u4e0b\u7bad\u5934</td><td>\u653e\u5927\u7f29\u5c0f</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>\u9009\u62e9\u524d\u540e\u6bb5</td></tr><tr><td>Ctrl + Home, End</td><td>\u9009\u62e9\u9996\u672b\u70b9</td></tr><tr><td>Del</td><td>\u5220\u9664\u5f53\u524d\u70b9</td></tr></table>
 dialog.keys.normalmodifier=Ctrl
 dialog.keys.macmodifier=Command
-dialog.saveconfig.desc=\u4e0b\u5217\u8bbe\u7f6e\u5c06\u4fdd\u5b58\u5230\u8bbe\u7f6e\u6587\u4ef6
-dialog.saveconfig.prune.trackdirectory=\u8f68\u8ff9\u6587\u4ef6\u5939
-dialog.saveconfig.prune.photodirectory=\u7167\u7247\u6587\u4ef6\u5939
-dialog.saveconfig.prune.languagecode=\u8bed\u8a00\u9009\u62e9(ZH)
-dialog.saveconfig.prune.languagefile=\u8bed\u8a00\u6587\u4ef6\u5305
-dialog.saveconfig.prune.gpsdevice=GPS\u7aef\u53e3\u540d\u79f0
-dialog.saveconfig.prune.gpsformat=GPS\u6587\u4ef6\u683c\u5f0f
-dialog.saveconfig.prune.povrayfont=Povray \u5b57\u4f53
-dialog.saveconfig.prune.gnuplotpath=gnuplot\u8def\u5f84
-dialog.saveconfig.prune.gpsbabelpath=gpsbabel\u8def\u5f84
-dialog.saveconfig.prune.exiftoolpath=exiftool\u8def\u5f84
-dialog.saveconfig.prune.mapsource=\u5df2\u9009\u62e9\u7684\u5730\u56fe\u6570\u636e\u6e90
-dialog.saveconfig.prune.mapsourcelist=\u5730\u56fe\u6570\u636e\u6e90
-dialog.saveconfig.prune.diskcache=\u5b58\u50a8\u8def\u5f84
-dialog.saveconfig.prune.kmzimagewidth=KMZ\u56fe\u50cf\u5bbd\u5ea6
-dialog.saveconfig.prune.colourscheme=\u989c\u8272
-dialog.saveconfig.prune.linewidth=\u7ebf\u4f53\u5bbd\u5ea6
-dialog.saveconfig.prune.kmltrackcolour=KML\u8f68\u8ff9\u989c\u8272
-dialog.saveconfig.prune.autosavesettings=\u81ea\u52a8\u4fdd\u5b58\u8bbe\u7f6e
+dialog.paths.prune.gnuplotpath=gnuplot\u8def\u5f84
+dialog.paths.prune.gpsbabelpath=gpsbabel\u8def\u5f84
+dialog.paths.prune.exiftoolpath=exiftool\u8def\u5f84
 dialog.setpaths.intro=\u5982\u679c\u9700\u8981\uff0c\u53ef\u8bbe\u5b9a\u5916\u6302\u7a0b\u5e8f\u8def\u5f84
 dialog.setpaths.found=\u627e\u5230\u8def\u5f84\uff1f
 dialog.addaltitude.noaltitudes=\u8f68\u8ff9\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f
@@ -598,7 +565,6 @@ confirm.rearrangephotos=\u7167\u7247\u5df2\u91cd\u6392
 confirm.splitsegments=\u8f68\u8ff9\u5df2\u5206\u5272\u4e3a %d \u6bb5
 confirm.sewsegments=%d \u8f68\u8ff9\u6bb5\u5df2\u5408\u5e76
 confirm.cutandmove=\u5df2\u79fb\u52a8\u7684\u8f68\u8ff9\u6bb5
-confirm.interpolate=\u8f68\u8ff9\u70b9\u5df2\u6dfb\u52a0
 confirm.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u5df2\u8f6c\u6362
 confirm.saveexif.ok=\u5df2\u4fdd\u5b58 %d \u7167\u7247\u6587\u4ef6
 confirm.undo.single=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c
@@ -617,7 +583,6 @@ confirm.running=\u8bf7\u7a0d\u7b49...
 confirm.lookupsrtm=\u627e\u5230 %d \u9ad8\u5ea6\u503c
 confirm.downloadsrtm=\u4e0b\u8f7d %d \u9ad8\u5ea6\u6587\u4ef6\u5230\u7f13\u5b58\u4e2d
 confirm.downloadsrtm.1=\u4e0b\u8f7d %d \u9ad8\u5ea6\u6587\u4ef6\u5230\u7f13\u5b58\u4e2d
-confirm.downloadsrtm.none=\u65e0\u9700\u4e0b\u8f7d\uff0c\u6587\u4ef6\u5df2\u5b58\u50a8\u5728\u7f13\u5b58\u4e2d
 confirm.deletefieldvalues=\u533a\u57df\u6570\u636e\u5df2\u5220\u9664
 confirm.audioload=\u5df2\u6dfb\u52a0\u58f0\u97f3\u6587\u4ef6
 confirm.correlateaudios.single=\u58f0\u97f3\u5df2\u5173\u8054
@@ -627,7 +592,6 @@ confirm.correlateaudios.multi=\u58f0\u97f3\u5df2\u5173\u8054
 tip.title=\u63d0\u793a
 tip.useamapcache=\u542f\u7528\u78c1\u76d8\u7f13\u5b58 (\u8bbe\u7f6e -> \u4fdd\u5b58\u5730\u56fe)\n\u53ef\u4ee5\u63d0\u9ad8\u663e\u793a\u901f\u5ea6\uff0c\u51cf\u5c11\u7f51\u7edc\u6d41\u91cf
 tip.learntimeparams=\u5bf9\u8bb0\u5f55\u7684\u8f68\u8ff9\u542f\u7528 \u8f68\u8ff9 -> \u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4\n\u5c06\u4f7f\u7ed3\u679c\u66f4\u52a0\u7cbe\u786e
-tip.downloadsrtm=\u53ef\u4ee5\u70b9\u51fb \u8054\u7f51 -> \u4e0b\u8f7dSRTM\u6570\u636e\n\u5c06\u6570\u636e\u4fdd\u5b58\u5230\u78c1\u76d8\u7f13\u5b58\u4ee5\u63d0\u9ad8\u901f\u5ea6
 tip.usesrtmfor3d=\u6b64\u8f68\u8ff9\u6ca1\u6709\u9ad8\u5ea6\u4fe1\u606f\n\u53ef\u4ee5\u901a\u8fc7SRTM\u83b7\u53d6\u5927\u81f4\u9ad8\u5ea6\u4ee5\u663e\u793a3D\u89c6\u56fe
 tip.manuallycorrelateone=\u63d0\u793a\uff1a\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u7167\u7247\uff0c\u53ef\u81ea\u52a8\u8ba1\u7b97\u65f6\u95f4\u504f\u79fb
 
@@ -654,7 +618,6 @@ button.selectall=\u5168\u9009
 button.selectnone=\u5168\u4e0d\u9009
 button.preview=\u9884\u89c8
 button.load=\u5bfc\u5165
-button.upload=\u4e0a\u8f7d
 button.guessfields=\u4f30\u6d4b\u6570\u636e\u6bb5
 button.showwebpage=\u663e\u793a\u7f51\u9875
 button.check=\u68c0\u67e5
@@ -858,8 +821,6 @@ error.language.wrongfile=\u6240\u9009\u8bed\u8a00\u5305\u6587\u4ef6\u683c\u5f0f\
 error.convertnamestotimes.nonames=\u540d\u79f0\u65e0\u6cd5\u8f6c\u6362\u6210\u65f6\u95f4
 error.lookupsrtm.nonefound=\u65e0\u9ad8\u5ea6\u4fe1\u606f
 error.lookupsrtm.nonerequired=\u6240\u6709\u70b9\u5747\u542b\u9ad8\u5ea6\u4fe1\u606f
-error.gpsies.uploadnotok=gpsies\u670d\u52a1\u5668\u8fd4\u56de\u4fe1\u606f\uff1a
-error.gpsies.uploadfailed=\u4e0a\u4f20\u5931\u8d25
 error.showphoto.failed=\u52a0\u8f7d\u7167\u7247\u5931\u8d25
 error.playaudiofailed=\u65e0\u6cd5\u64ad\u653e\u58f0\u97f3\u6587\u4ef6
 error.cache.notthere=\u672a\u627e\u5230\u533a\u57df\u6570\u636e\u7f13\u5b58\u6587\u4ef6\u5939
index 5c54dfb5f574cf2d33a676fec30d1c93c33c1d06..73c40ac8492ff782a0b8d92dc97c81996b3c111c 100644 (file)
@@ -100,6 +100,7 @@ public abstract class BabelFileFormats
                        "GeocachingDB for Palm/OS", "gcdb", null,
                        "Geogrid-Viewer ascii overlay file", "ggv_ovl", ".ovl",
                        "Geogrid-Viewer tracklogs", "ggv_log", ".log",
+                       "GeoJSON", "geojson", ".json",
                        "GEOnet Names Server (GNS)", "geonet", null,
                        "GlobalSat DG-100/BT-335", "dg-100", null,
                        "GlobalSat DG-200", "dg-200", null,
diff --git a/src/tim/prune/load/ContentCacher.java b/src/tim/prune/load/ContentCacher.java
new file mode 100644 (file)
index 0000000..623abda
--- /dev/null
@@ -0,0 +1,90 @@
+package tim.prune.load;
+
+import java.util.ArrayList;
+
+/**
+ * General point data cacher
+ */
+public abstract class ContentCacher
+{
+       /** Array to hold lines of file */
+       private String[] _contentArray = null;
+
+
+       /**
+        * @return Contents of the file as array of non-blank Strings
+        */
+       public String[] getContents()
+       {
+               return _contentArray;
+       }
+
+
+       /**
+        * Get the top section of the file for preview
+        * @param inNumRows number of lines to extract
+        * @param inMaxWidth max length of Strings (longer ones will be chopped)
+        * @return String array containing non-blank lines from the file
+        */
+       public String[] getSnippet(int inNumRows, int inMaxWidth)
+       {
+               final int MIN_SNIPPET_SIZE = 3;
+               // Check size is within sensible limits
+               int numToCopy = inNumRows;
+               if (numToCopy > getNumLines()) numToCopy = getNumLines();
+               int size = numToCopy;
+               if (size < MIN_SNIPPET_SIZE) size = MIN_SNIPPET_SIZE;
+               String[] result = new String[size];
+               // Copy Strings across
+               System.arraycopy(_contentArray, 0, result, 0, numToCopy);
+               // Chop Strings to max width if necessary
+               if (inMaxWidth > 10)
+               {
+                       for (int i=0; i<size; i++)
+                       {
+                               if (result[i] == null)
+                                       result[i] = "";
+                               else
+                               {
+                                       if (result[i].length() > inMaxWidth)
+                                               result[i] = result[i].trim();
+                                       if (result[i].length() > inMaxWidth)
+                                               result[i] = result[i].substring(0, inMaxWidth);
+                               }
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * @return the number of non-blank lines in the file
+        */
+       public int getNumLines()
+       {
+               return _contentArray.length;
+       }
+
+
+       /**
+        * Clear the memory
+        */
+       public void clear()
+       {
+               _contentArray = null;
+       }
+
+       /**
+        * Populate the string array
+        * @param inList list of lines
+        */
+       protected void setContents(ArrayList<String> inList)
+       {
+               // Convert into String array for keeps
+               int numLines = inList.size();
+               _contentArray = new String[numLines];
+               for (int i=0; i<numLines; i++)
+               {
+                       _contentArray[i] = inList.get(i);
+               }
+       }
+}
index a151c2e285022f48c5ffe8bbdf2c3d8433c09df0..ca7a4abba8f5abac4f855e4b4127a65b9b0c5ba1 100644 (file)
@@ -10,37 +10,30 @@ import java.util.ArrayList;
  * Class to load the contents of a file
  * into an array for later retrieval
  */
-public class FileCacher
+public class FileCacher extends ContentCacher
 {
-       /** File to cache */
-       private File _file = null;
-       /** Array to hold lines of file */
-       private String[] _contentArray = null;
-
-
        /**
         * Constructor
         * @param inFile File object to cache
         */
        public FileCacher(File inFile)
        {
-               _file = inFile;
-               loadFile();
+               loadFile(inFile);
        }
 
 
        /**
         * Load the specified file into memory
         */
-       private void loadFile()
+       private void loadFile(File inFile)
        {
                ArrayList<String> contentList = new ArrayList<String>();
-               if (_file != null && _file.exists() && _file.canRead())
+               if (inFile != null && inFile.exists() && inFile.canRead())
                {
                        BufferedReader reader = null;
                        try
                        {
-                               reader = new BufferedReader(new FileReader(_file));
+                               reader = new BufferedReader(new FileReader(inFile));
                                String currLine = reader.readLine();
                                if (currLine != null && currLine.startsWith("<?xml")) {
                                        return; // it's an xml file, it shouldn't use this cacher
@@ -68,74 +61,6 @@ public class FileCacher
                                catch (Exception e) {}
                        }
                }
-               // Convert into String array for keeps
-               int numLines = contentList.size();
-               _contentArray = new String[numLines];
-               for (int i=0; i<numLines; i++)
-                       _contentArray[i] = contentList.get(i);
-       }
-
-
-       /**
-        * @return Contents of the file as array of non-blank Strings
-        */
-       public String[] getContents()
-       {
-               return _contentArray;
-       }
-
-
-       /**
-        * Get the top section of the file for preview
-        * @param inNumRows number of lines to extract
-        * @param inMaxWidth max length of Strings (longer ones will be chopped)
-        * @return String array containing non-blank lines from the file
-        */
-       public String[] getSnippet(int inNumRows, int inMaxWidth)
-       {
-               final int MIN_SNIPPET_SIZE = 3;
-               // Check size is within sensible limits
-               int numToCopy = inNumRows;
-               if (numToCopy > getNumLines()) numToCopy = getNumLines();
-               int size = numToCopy;
-               if (size < MIN_SNIPPET_SIZE) size = MIN_SNIPPET_SIZE;
-               String[] result = new String[size];
-               // Copy Strings across
-               System.arraycopy(_contentArray, 0, result, 0, numToCopy);
-               // Chop Strings to max width if necessary
-               if (inMaxWidth > 10)
-               {
-                       for (int i=0; i<size; i++)
-                       {
-                               if (result[i] == null)
-                                       result[i] = "";
-                               else
-                               {
-                                       if (result[i].length() > inMaxWidth)
-                                               result[i] = result[i].trim();
-                                       if (result[i].length() > inMaxWidth)
-                                               result[i] = result[i].substring(0, inMaxWidth);
-                               }
-                       }
-               }
-               return result;
-       }
-
-       /**
-        * @return the number of non-blank lines in the file
-        */
-       public int getNumLines()
-       {
-               return _contentArray.length;
-       }
-
-
-       /**
-        * Clear the memory
-        */
-       public void clear()
-       {
-               _file = null;
-               _contentArray = null;
+               setContents(contentList);
        }
 }
index 52bdef232942f0c5290d2c8160dccba45ce0ef92..ba1100581e90d2c371e28017eef7ad6386a100fb 100644 (file)
@@ -10,6 +10,7 @@ import javax.swing.JFrame;
 import tim.prune.App;
 import tim.prune.config.Config;
 import tim.prune.data.Photo;
+import tim.prune.load.json.JsonFileLoader;
 import tim.prune.load.xml.GzipFileLoader;
 import tim.prune.load.xml.XmlFileLoader;
 import tim.prune.load.xml.ZipFileLoader;
@@ -142,6 +143,10 @@ public class FileLoader
                        _app.informPhotosLoaded(photoSet);
                        _app.informNoDataLoaded(); // To trigger load of next file if any
                }
+               else if (fileExtension.equals("json"))
+               {
+                       new JsonFileLoader(_app).openFile(inFile);
+               }
                else
                {
                        // Use text loader for everything else
index 60c2c5dbcf5e429e21d4d3d76a18460d273b0525..990279211edf23aa2385dd66d86a41504ed4ed7f 100644 (file)
@@ -6,7 +6,7 @@ package tim.prune.load;
  */
 public class FileSplitter
 {
-       private FileCacher _cacher = null;
+       private ContentCacher _cacher = null;
        private int _numRows = 0;
        private int _numColumns = 0;
        private boolean[] _columnStates = null;
@@ -15,9 +15,9 @@ public class FileSplitter
 
        /**
         * Constructor
-        * @param inCacher FileCacher object holding file contents
+        * @param inCacher cacher object holding file contents
         */
-       public FileSplitter(FileCacher inCacher)
+       public FileSplitter(ContentCacher inCacher)
        {
                _cacher = inCacher;
        }
index 3e22eba029ccb76ce341a2fffaea6327669a28f1..471226f2919dc15c634454a2ce352e69039b20df 100644 (file)
@@ -256,11 +256,9 @@ public class JpegLoader implements Runnable, Cancellable
                if (timestamp == null) {
                        timestamp = new TimestampUtc(inFile.lastModified());
                }
-               // Apply timestamp to photo and its point (if any)
+               // Apply timestamp to photo (but not its point)
                photo.setTimestamp(timestamp);
-               if (photo.getDataPoint() != null) {
-                       // photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.Format.ISO8601), false);
-               }
+
                return photo;
        }
 
diff --git a/src/tim/prune/load/TextCacher.java b/src/tim/prune/load/TextCacher.java
new file mode 100644 (file)
index 0000000..0945bfe
--- /dev/null
@@ -0,0 +1,42 @@
+package tim.prune.load;
+
+import java.util.ArrayList;
+
+/**
+ * Class to split a pasted text
+ * into an array for later retrieval
+ */
+public class TextCacher extends ContentCacher
+{
+       /**
+        * Constructor
+        * @param inText text to cache
+        */
+       public TextCacher(String inText)
+       {
+               splitText(inText);
+       }
+
+
+       /**
+        * Load and split the specified text
+        */
+       private void splitText(String inText)
+       {
+               ArrayList<String> contentList = new ArrayList<String>();
+               if (inText != null)
+               {
+                       for (String currLine : inText.split("\n"))
+                       {
+                               if (currLine != null)
+                               {
+                                       currLine = currLine.trim();
+                                       if (currLine.length() > 0) {
+                                               contentList.add(currLine);
+                                       }
+                               }
+                       }
+               }
+               setContents(contentList);
+       }
+}
index b4a0ca829efc6c75fe64b7c8478ff9b6f5f50c8f..f61af3993d0f49cc3ef2ba2864ffb8e544848a3b 100644 (file)
@@ -48,7 +48,7 @@ public class TextFileLoader
        private JTextField _otherDelimiterText = null;
        private JLabel _statusLabel = null;
        private DelimiterInfo[] _delimiterInfos = null;
-       private FileCacher _fileCacher = null;
+       private ContentCacher _contentCacher = null;
        private JList<String> _snippetBox = null;
        private FileExtractTableModel _fileExtractTableModel = null;
        private JTable _fieldTable;
@@ -117,29 +117,7 @@ public class TextFileLoader
                _file = inFile;
                if (preCheckFile(_file))
                {
-                       _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.openoptions.title"), true);
-                       _dialog.setLocationRelativeTo(_parentFrame);
-                       _dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
-                       // add closing listener
-                       _dialog.addWindowListener(new WindowAdapter() {
-                               public void windowClosing(WindowEvent e) {
-                                       _dialog.dispose();
-                                       _app.informNoDataLoaded();
-                               }
-                       });
-                       _dialog.getContentPane().add(makeDialogComponents());
-
-                       // select best separator according to row counts (more is better)
-                       int bestDelim = getBestOption(_delimiterInfos[0].getNumWinningRecords(),
-                               _delimiterInfos[1].getNumWinningRecords(), _delimiterInfos[2].getNumWinningRecords(),
-                               _delimiterInfos[3].getNumWinningRecords());
-                       if (bestDelim >= 0)
-                               _delimiterRadios[bestDelim].setSelected(true);
-                       else
-                               _delimiterRadios[_delimiterRadios.length-1].setSelected(true);
-                       informDelimiterSelected();
-                       _dialog.pack();
-                       _dialog.setVisible(true);
+                       showDialog();
                }
                else
                {
@@ -152,8 +130,38 @@ public class TextFileLoader
 
 
        /**
-        * Check the given file for readability and funny characters,
-        * and count the fields for the various separators
+        * Checks passed, so now build and show the dialog
+        */
+       private void showDialog()
+       {
+               _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.openoptions.title"), true);
+               _dialog.setLocationRelativeTo(_parentFrame);
+               _dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
+               // add closing listener
+               _dialog.addWindowListener(new WindowAdapter() {
+                       public void windowClosing(WindowEvent e) {
+                               _dialog.dispose();
+                               _app.informNoDataLoaded();
+                       }
+               });
+               _dialog.getContentPane().add(makeDialogComponents());
+
+               // select best separator according to row counts (more is better)
+               int bestDelim = getBestOption(_delimiterInfos[0].getNumWinningRecords(),
+                       _delimiterInfos[1].getNumWinningRecords(), _delimiterInfos[2].getNumWinningRecords(),
+                       _delimiterInfos[3].getNumWinningRecords());
+               if (bestDelim >= 0)
+                       _delimiterRadios[bestDelim].setSelected(true);
+               else
+                       _delimiterRadios[_delimiterRadios.length-1].setSelected(true);
+               informDelimiterSelected();
+               _dialog.pack();
+               _dialog.setVisible(true);
+       }
+
+
+       /**
+        * Check the given file for validity
         * @param inFile file to check
         */
        private boolean preCheckFile(File inFile)
@@ -164,11 +172,20 @@ public class TextFileLoader
                        return false;
                }
                // Use a FileCacher to read the file into an array
-               _fileCacher = new FileCacher(inFile);
+               _contentCacher = new FileCacher(inFile);
+
+               return preCheckContents();
+       }
 
+       /**
+        * Check the contents for readability and funny characters,
+        * and count the fields for the various separators
+        */
+       private boolean preCheckContents()
+       {
                // Check each line of the file
-               String[] fileContents = _fileCacher.getContents();
-               if (fileContents == null) {
+               String[] contents = _contentCacher.getContents();
+               if (contents == null) {
                        return false; // nothing cached, might be binary
                }
                boolean fileOK = true;
@@ -178,9 +195,9 @@ public class TextFileLoader
                String currLine = null;
                String[] splitFields = null;
                int commaFields = 0, semicolonFields = 0, tabFields = 0, spaceFields = 0;
-               for (int lineNum=0; lineNum<fileContents.length && fileOK; lineNum++)
+               for (int lineNum=0; lineNum<contents.length && fileOK; lineNum++)
                {
-                       currLine = fileContents[lineNum];
+                       currLine = contents[lineNum];
                        // check for invalid characters
                        if (currLine.indexOf('\0') >= 0) {fileOK = false;}
                        // check for commas
@@ -211,6 +228,39 @@ public class TextFileLoader
                return fileOK;
        }
 
+       /**
+        * @param inText text to load (as if it came from a file)
+        */
+       public void loadText(String inText)
+       {
+               _file = null;
+               if (preCheckText(inText))
+               {
+                       showDialog();
+               }
+               else
+               {
+                       // Didn't pass pre-check
+                       _app.showErrorMessage("error.load.dialogtitle", "error.load.nopointsintext");
+               }
+       }
+
+       /**
+        * Check the given text for validity
+        * @param inText (pasted) text to check
+        */
+       private boolean preCheckText(String inText)
+       {
+               if (inText == null || inText.length() < 6)
+               {
+                       return false;
+               }
+               // Use a cacher to split the text into an array
+               _contentCacher = new TextCacher(inText);
+
+               return preCheckContents();
+       }
+
 
        /**
         * Get the index of the best one in the list
@@ -326,7 +376,7 @@ public class TextFileLoader
                delimsPanel.add(_statusLabel);
                firstCard.add(delimsPanel, BorderLayout.SOUTH);
                // load snippet to show first few lines
-               _snippetBox = new JList<String>(_fileCacher.getSnippet(SNIPPET_SIZE, MAX_SNIPPET_WIDTH));
+               _snippetBox = new JList<String>(_contentCacher.getSnippet(SNIPPET_SIZE, MAX_SNIPPET_WIDTH));
                _snippetBox.setEnabled(false);
                firstCard.add(makeLabelledPanel("dialog.openoptions.filesnippet", _snippetBox), BorderLayout.CENTER);
 
@@ -531,11 +581,13 @@ public class TextFileLoader
         */
        public DelimiterInfo getSelectedDelimiterInfo()
        {
-               for (int i=0; i<4; i++)
+               for (int i=0; i<4; i++) {
                        if (_delimiterRadios[i].isSelected()) return _delimiterInfos[i];
+               }
                // must be "other" - build info if necessary
-               if (_delimiterInfos[4] == null)
+               if (_delimiterInfos[4] == null) {
                        _delimiterInfos[4] = new DelimiterInfo(_otherDelimiterText.getText().charAt(0));
+               }
                return _delimiterInfos[4];
        }
 
@@ -567,7 +619,7 @@ public class TextFileLoader
        private void prepareSecondPanel()
        {
                DelimiterInfo info = getSelectedDelimiterInfo();
-               FileSplitter splitter = new FileSplitter(_fileCacher);
+               FileSplitter splitter = new FileSplitter(_contentCacher);
                // Check info makes sense - num fields > 0, num records > 0
                // set "Finished" button to disabled if not ok
                // Add data to GUI elements
@@ -637,7 +689,7 @@ public class TextFileLoader
                _lastSelectedFields = _fieldTableModel.getFieldArray();
                // TODO: Remember all the units selections for next load?
                // Get the selected units for altitudes and speeds
-               SourceInfo sourceInfo = new SourceInfo(_file, SourceInfo.FILE_TYPE.TEXT);
+               SourceInfo sourceInfo = (_file == null ? null : new SourceInfo(_file, SourceInfo.FILE_TYPE.TEXT));
                PointCreateOptions options = new PointCreateOptions();
                options.setAltitudeUnits(_altitudeUnitsDropdown.getSelectedIndex() == 0 ? UnitSetLibrary.UNITS_METRES : UnitSetLibrary.UNITS_FEET);
                Unit hSpeedUnit = UnitSetLibrary.ALL_SPEED_UNITS[_hSpeedUnitsDropdown.getSelectedIndex()];
@@ -649,7 +701,7 @@ public class TextFileLoader
                _app.informDataLoaded(_fieldTableModel.getFieldArray(),
                        _fileExtractTableModel.getData(), options, sourceInfo, null);
                // clear up file cacher
-               _fileCacher.clear();
+               _contentCacher.clear();
                // dispose of dialog
                _dialog.dispose();
        }
diff --git a/src/tim/prune/load/json/JsonBlock.java b/src/tim/prune/load/json/JsonBlock.java
new file mode 100644 (file)
index 0000000..83486c7
--- /dev/null
@@ -0,0 +1,227 @@
+package tim.prune.load.json;
+
+import java.util.ArrayList;
+
+/**
+ * Structure to hold the contents of a Json block during parsing.
+ * This will be held within the current [] or {} block
+ */
+public class JsonBlock
+{
+       private String _latitude = null;
+       private String _longitude = null;
+       private String _altitude = null;
+
+       private Expectation _nextField = Expectation.NONE;
+       private BlockType _type = BlockType.NONE;
+       private boolean _hasNonNumbers = false;
+       private ArrayList<String> _pointCoords = null;
+       private ArrayList<ArrayList<String>> _coordList = null;
+
+
+       private enum BlockType {
+               NONE, FIELDS, POINTCOORDS, ISPOINTLIST, HASPOINTLIST
+       }
+       private enum Expectation {
+               NONE, LATITUDE, LONGITUDE, ALTITUDE, COORDS
+       }
+
+       /** Internal method to remove quotes and NaNs from altitude strings */
+       private String modifyAltitudeString(String inAlt)
+       {
+               if (inAlt == null || inAlt.equals("") || inAlt.equals("\"\"")) {
+                       return null;
+               }
+               String result = inAlt;
+               if (inAlt.length() > 2 && inAlt.startsWith("\"") && inAlt.endsWith("\""))
+               {
+                       result = inAlt.substring(1, inAlt.length()-1);
+               }
+               if (result.equals("NaN")) {return null;}
+               return result;
+       }
+
+       /**
+        * Receive a token to this block
+        * @param inToken token from Json source
+        */
+       public void addToken(String inToken)
+       {
+               if (inToken == null || inToken.isEmpty()) {return;}
+               if (!_hasNonNumbers && !looksLikeNumber(inToken)) {
+                       _hasNonNumbers = true;
+               }
+               if (inToken.equals("\"latitude\"")) {
+                       _nextField = Expectation.LATITUDE;
+               }
+               else if (inToken.equals("\"longitude\"")) {
+                       _nextField = Expectation.LONGITUDE;
+               }
+               else if (inToken.equals("\"altitude\"")) {
+                       _nextField = Expectation.ALTITUDE;
+               }
+               else if (inToken.equals("\"coordinates\"")) {
+                       _nextField = Expectation.COORDS;
+               }
+               else
+               {
+                       switch (_nextField)
+                       {
+                               case LATITUDE:
+                                       _latitude = inToken;
+                                       _type = BlockType.FIELDS;
+                                       break;
+                               case LONGITUDE:
+                                       _longitude = inToken;
+                                       _type = BlockType.FIELDS;
+                                       break;
+                               case ALTITUDE:
+                                       _altitude = modifyAltitudeString(inToken);
+                                       _type = BlockType.FIELDS;
+                                       break;
+                               default:
+                                       if (!_hasNonNumbers && looksLikeNumber(inToken))
+                                       {
+                                               if (_pointCoords == null) {
+                                                       _pointCoords = new ArrayList<String>();
+                                               }
+                                               _pointCoords.add(inToken);
+                                               _type = BlockType.POINTCOORDS;
+                                       }
+                                       break;
+                       }
+                       _nextField = Expectation.NONE;
+               }
+       }
+
+       /** @return true if String can be parsed as a double */
+       private static boolean looksLikeNumber(String inToken)
+       {
+               double value = Double.NaN;
+               try {
+                       value = Double.parseDouble(inToken);
+               }
+               catch (NumberFormatException nfe) {}
+               return !Double.isNaN(value);
+       }
+
+       /** @return true if named fields are valid */
+       public boolean areFieldsValid() {
+               return _type == BlockType.FIELDS && _latitude != null && _longitude != null;
+       }
+
+       /** @return true if list of 2 or 3 doubles for a single point is valid */
+       public boolean areSingleCoordsValid()
+       {
+               return !_hasNonNumbers && _pointCoords != null
+                       && _pointCoords.size() >= 2 && _pointCoords.size() <= 3;
+       }
+
+       /**
+        * @param inNewSegment new segment flag
+        * @return created point
+        */
+       public JsonPoint createSinglePoint(boolean inNewSegment)
+       {
+               return new JsonPoint(_latitude, _longitude, _altitude, inNewSegment);
+       }
+
+       /**
+        * Child block has finished processing a single set of point coordinates
+        * @param inChild child block
+        */
+       public void addSingleCoordsFrom(JsonBlock inChild)
+       {
+               if (_type == BlockType.NONE && _nextField == Expectation.COORDS
+                       && inChild._type == BlockType.POINTCOORDS)
+               {
+                       // Named coordinates field
+                       _type = BlockType.FIELDS;
+                       _longitude = inChild.getSinglePointCoords(0);
+                       _latitude = inChild.getSinglePointCoords(1);
+                       _altitude = inChild.getSinglePointCoords(2);
+               }
+               else if ((_type == BlockType.NONE || _type == BlockType.ISPOINTLIST)
+                       && !_hasNonNumbers && _nextField == Expectation.NONE
+                       && inChild._type == BlockType.POINTCOORDS)
+               {
+                       // add coordinates to list
+                       _type = BlockType.ISPOINTLIST;
+                       if (_coordList == null) {
+                               _coordList = new ArrayList<ArrayList<String>>();
+                       }
+                       _coordList.add(inChild._pointCoords);
+               }
+       }
+
+       /** @return point coordinate for given index, or null if not present */
+       private String getSinglePointCoords(int inIndex)
+       {
+               if (_pointCoords == null || inIndex < 0 || inIndex >= _pointCoords.size()) {
+                       return null;
+               }
+               return _pointCoords.get(inIndex);
+       }
+
+       /** @return true if list of point coords is valid */
+       public boolean isCoordListValid()
+       {
+               return !_hasNonNumbers && _type == BlockType.ISPOINTLIST
+                       && _coordList != null;
+       }
+
+       /** @return true if this block has a valid list of point coords */
+       public boolean hasValidCoordList()
+       {
+               return _type == BlockType.HASPOINTLIST && _coordList != null;
+       }
+
+       /**
+        * Child block has finished processing a list of point coordinates
+        * @param inChild child block
+        */
+       public void addCoordListFrom(JsonBlock inChild)
+       {
+               if (_type == BlockType.NONE && _nextField == Expectation.COORDS
+                       && inChild._type == BlockType.ISPOINTLIST)
+               {
+                       _coordList = inChild._coordList;
+                       _type = BlockType.HASPOINTLIST;
+               }
+               else if ((_type == BlockType.NONE || _type == BlockType.ISPOINTLIST)
+                       && !_hasNonNumbers && inChild._type == BlockType.ISPOINTLIST)
+               {
+                       if (_coordList == null) {
+                               _coordList = new ArrayList<ArrayList<String>>();
+                       }
+                       _coordList.addAll(inChild._coordList);
+                       _type = BlockType.ISPOINTLIST;
+               }
+       }
+
+       /**
+        * @return number of points in the list, if this block has a list
+        */
+       public int getNumPoints()
+       {
+               if (hasValidCoordList()) {
+                       return _coordList.size();
+               }
+               return 0;
+       }
+
+       /**
+        * @param inIndex point index within list
+        * @return created point for specified index
+        */
+       public JsonPoint createPointFromList(int inIndex)
+       {
+               if (inIndex < 0 || inIndex >= getNumPoints()) {return null;}
+               ArrayList<String> pointCoords = _coordList.get(inIndex);
+               if (pointCoords.size() < 2 || pointCoords.size() > 3) {return null;}
+               final String longitude = pointCoords.get(0);
+               final String latitude = pointCoords.get(1);
+               final String altitude = ((pointCoords.size() == 3) ? pointCoords.get(3) : null);
+               return new JsonPoint(latitude, longitude, altitude, inIndex == 0);
+       }
+}
diff --git a/src/tim/prune/load/json/JsonFileLoader.java b/src/tim/prune/load/json/JsonFileLoader.java
new file mode 100644 (file)
index 0000000..e1df666
--- /dev/null
@@ -0,0 +1,170 @@
+package tim.prune.load.json;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Stack;
+
+import tim.prune.App;
+import tim.prune.data.Field;
+import tim.prune.data.SourceInfo;
+
+
+/**
+ * Class to handle the loading of GeoJSON files
+ */
+public class JsonFileLoader
+{
+       /** App for callback of file loading */
+       private App _app = null;
+       /** Stack of blocks */
+       private Stack<JsonBlock> _jsonBlocks = null;
+       /** List of points extracted */
+       private ArrayList<JsonPoint> _jsonPoints = null;
+       private boolean _newSegment = true;
+
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public JsonFileLoader(App inApp)
+       {
+               _app = inApp;
+               _jsonBlocks = new Stack<JsonBlock>();
+               _jsonPoints = new ArrayList<JsonPoint>();
+       }
+
+       /**
+        * Open the selected file
+        * @param inFile File to open
+        */
+       public void openFile(File inFile)
+       {
+               BufferedReader reader = null;
+               try
+               {
+                       reader = new BufferedReader(new FileReader(inFile));
+                       String currLine = reader.readLine();
+                       while (currLine != null)
+                       {
+                               processTokensInLine(currLine);
+                               // Read next line, if any
+                               currLine = reader.readLine();
+                       }
+               }
+               catch (IOException ioe) {
+                       _app.showErrorMessage("error.load.dialogtitle", "error.load.noread");
+               }
+               finally
+               {
+                       // close file ignoring errors
+                       try {
+                               if (reader != null) reader.close();
+                       }
+                       catch (Exception e) {}
+               }
+               if (_jsonPoints.size() > 0)
+               {
+                       Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE,
+                               Field.NEW_SEGMENT};
+                       _app.informDataLoaded(fields, makeDataArray(),
+                               null, new SourceInfo(inFile, SourceInfo.FILE_TYPE.JSON), null);
+               }
+               // TODO: Show message if nothing was found?
+       }
+
+       /** Split the given line from the json into tokens
+        *  and process them one by one */
+       private void processTokensInLine(String inLine)
+       {
+               if (inLine == null) {return;}
+               String line = inLine.strip();
+               StringBuilder currToken = new StringBuilder();
+               boolean insideQuotes = false;
+               boolean previousSlash = false;
+               for (char x : line.toCharArray())
+               {
+                       if (insideQuotes || x=='"') {
+                               currToken.append(x);
+                       }
+                       else
+                       {
+                               if (" :,".indexOf(x) >= 0) {
+                                       processToken(currToken.toString());
+                                       currToken.setLength(0);
+                               }
+                               else if ("[{".indexOf(x) >= 0) {
+                                       // start of a new block
+                                       _jsonBlocks.add(new JsonBlock());
+                               }
+                               else if ("]}".indexOf(x) >= 0)
+                               {
+                                       processToken(currToken.toString());
+                                       currToken.setLength(0);
+                                       // end of the current block
+                                       processBlock(_jsonBlocks.pop());
+                               }
+                               else {
+                                       currToken.append(x);
+                               }
+                       }
+                       if (x == '"' && !previousSlash) {insideQuotes = !insideQuotes;}
+                       previousSlash = (x == '\\') && !previousSlash;
+               }
+               processToken(currToken.toString());
+       }
+
+       private void processToken(String inToken)
+       {
+               if (inToken == null || inToken.isBlank()) {return;}
+               if (inToken.equals("\"coordinates\"")) {
+                       _newSegment = true;
+               }
+               _jsonBlocks.peek().addToken(inToken);
+       }
+
+       /** Process the end of the given block */
+       private void processBlock(JsonBlock inBlock)
+       {
+               if (inBlock.areFieldsValid())
+               {
+                       _jsonPoints.add(inBlock.createSinglePoint(_newSegment));
+                       _newSegment = false;
+               }
+               else if (inBlock.areSingleCoordsValid())
+               {
+                       // block contains a single point - pass to parent list
+                       _jsonBlocks.peek().addSingleCoordsFrom(inBlock);
+               }
+               else if (inBlock.isCoordListValid())
+               {
+                       // block contains a list of point coords
+                       _jsonBlocks.peek().addCoordListFrom(inBlock);
+               }
+               else if (inBlock.hasValidCoordList())
+               {
+                       for (int i=0; i<inBlock.getNumPoints(); i++)
+                       {
+                               _jsonPoints.add(inBlock.createPointFromList(i));
+                       }
+                       _newSegment = true;
+               }
+       }
+
+       /**
+        * Make an object array from the data list
+        * @return object array for loading
+        */
+       private Object[][] makeDataArray()
+       {
+               Object[][] result = new Object[_jsonPoints.size()][];
+               for (int i=0; i<_jsonPoints.size(); i++) {
+                       JsonPoint point = _jsonPoints.get(i);
+                       result[i] = new String[] {point._latitude, point._longitude, point._altitude, point._newSegment?"1":"0"};
+               }
+               return result;
+       }
+}
diff --git a/src/tim/prune/load/json/JsonPoint.java b/src/tim/prune/load/json/JsonPoint.java
new file mode 100644 (file)
index 0000000..a02bb15
--- /dev/null
@@ -0,0 +1,25 @@
+package tim.prune.load.json;
+
+/**
+ * Structure for holding a single point extracted from the Json
+ */
+public class JsonPoint
+{
+       public String _latitude = null, _longitude = null, _altitude = null;
+       public boolean _newSegment = false;
+
+       /**
+        * Constructor
+        * @param inLat latitude string
+        * @param inLon longitude string
+        * @param inAlt altitude string
+        * @param inNewSegment true if this point starts a new segment
+        */
+       public JsonPoint(String inLat, String inLon, String inAlt, boolean inNewSegment)
+       {
+               _latitude = inLat;
+               _longitude = inLon;
+               _altitude = inAlt;
+               _newSegment = inNewSegment;
+       }
+}
index 6f63080ceb98461a89ada946fda0d9f35b4fc6f4..ebd106ab59cd77657a7ce7ec1b1943f863146679 100644 (file)
@@ -24,7 +24,7 @@ public class GpxHandler extends XmlHandler
        private String _latitude = null, _longitude = null;
        private GpxTag _elevation = new GpxTag(), _time = new GpxTag();
        private GpxTag _type = new GpxTag(), _description = new GpxTag();
-       private GpxTag _link = new GpxTag();
+       private GpxTag _link = new GpxTag(), _comment = new GpxTag();
        private GpxTag _currentTag = null;
        private ArrayList<String[]> _pointList = new ArrayList<String[]>();
        private ArrayList<String> _linkList = new ArrayList<String>();
@@ -58,6 +58,7 @@ public class GpxHandler extends XmlHandler
                        _type.setValue(null);
                        _link.setValue(null);
                        _description.setValue(null);
+                       _comment.setValue(null);
                }
                else if (tag.equals("ele")) {
                        _currentTag = _elevation;
@@ -84,6 +85,9 @@ public class GpxHandler extends XmlHandler
                else if (tag.equals("description") || tag.equals("desc")) {
                        _currentTag = _description;
                }
+               else if (tag.equals("cmt")) {
+                       _currentTag = _comment;
+               }
                else if (tag.equals("link")) {
                        _link.setValue(attributes.getValue("href"));
                }
@@ -153,7 +157,7 @@ public class GpxHandler extends XmlHandler
        private void processPoint()
        {
                // Put the values into a String array matching the order in getFieldArray()
-               String[] values = new String[8];
+               String[] values = new String[9];
                values[0] = _latitude;
                values[1] = _longitude;
                values[2] = _elevation.getValue();
@@ -166,6 +170,7 @@ public class GpxHandler extends XmlHandler
                }
                values[6] = _type.getValue();
                values[7] = _description.getValue();
+               values[8] = _comment.getValue();
                _pointList.add(values);
                _trackNameList.addPoint(_trackNum, _trackName.getValue(), _isTrackPoint);
                _linkList.add(_link.getValue());
@@ -179,7 +184,7 @@ public class GpxHandler extends XmlHandler
        {
                final Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE,
                        Field.WAYPT_NAME, Field.TIMESTAMP, Field.NEW_SEGMENT,
-                       Field.WAYPT_TYPE, Field.DESCRIPTION};
+                       Field.WAYPT_TYPE, Field.DESCRIPTION, Field.COMMENT};
                return fields;
        }
 
index 0c5bf5c33987915d36f012cc929f77710549a950..2c30761268d941bad44eb0c810542ca40d7f6d5b 100644 (file)
@@ -1,11 +1,11 @@
-GpsPrune version 19.2
+GpsPrune version 20.4
 =====================
 
 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-2018 activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
+GpsPrune is copyright 2006-2021 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_19.2.jar
+   java -jar gpsprune_20.4.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,46 +25,72 @@ 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_19.2.jar --lang=DE
+   java -jar gpsprune_20.4.jar --lang=DE
 
 
-New with version 19.2
+New with version 20.4
 =====================
-The following fixes and additions were made since version 19.1:
-  - Fix right-click-and-drag bug for zooming with Java 9 and Java 10
-  - Fix export of timestamps when photo points don't have timestamps
-  - Lighting of 3d views from the northwest (thanks, PeHar)
-  - Remember the name tag from a loaded gpx file, suggest it again for gpx export
-  - Removal of the Thunderforest tile sources (OpenCycleMap, Outdoors)
-New with version 19.1
+The following fixes and additions were made since version 20.3:
+  - Loading of GeoJSON files, either directly or via GPSBabel
+  - Fix for calculations when some timestamps are missing (fperrin)
+  - Make 3d rotations more intuitive, avoid rotating upside-down (Issue #34)
+
+New with version 20.3
+=====================
+The following fixes and additions were made since version 20.2:
+  - If a waypoint is selected, the distances function should use it
+  - If a point list has been pasted, closing should prompt about unsaved data
+  - Some additional translations
+  - Switching SRTM downloads to use kurviger.de instead of usgs.gov (thanks, kurviger.de!)
+
+New with version 20.2
+=====================
+The following fixes and additions were made since version 20.1:
+  - Fix for intermittent startup problems
+  - Addition of Swedish (erikiiofph7)
+  - Allow four digits for adding day offsets (fperrin)
+
+New with version 20.1
 =====================
+The following fixes and additions were made since version 20:
+  - Starting with an empty map (fperrin)
+  - Reading and writing <cmt> tags from gpx files (Willy)
+  - Add option to use GTK look-and-feel (fperrin)
+  - Translation updates (Carlos, Tche333)
+  - Solve issue #29/973910 regarding missing tile cache (Peter Gervai)
+  - Remove "Download SRTM" function and just do it automatically
+  - Tweak waypoint icons
+  - Fix problem with a recently-used file which has disappeared
+  - Fix problem with moving / renaming downloaded tile files
+
+New with version 20
+===================
 The following fixes and additions were made since version 19:
-  - Performance improvements regarding scrolling through points
-  - Keyboard shortcut to access point edit dialog (part of Github issue #10)
-  - Online services Inlinemap.net and Graphhopper.com (routing)
-  - Cardinals in java3d view get billboard behaviour to always face camera (thanks, PeHar)
-  - Fix for PeakFinder urls
+  - Add option to use Nimbus look-and-feel (wishlist 77)
+  - Extend the marker waypoints function to include half the distance, half the climb and half the descent
+  - Remove Ukrainian language
+  - Add support for entering Pluscodes
+  - Add way to copy point details or range details to clipboard (wishlist 76)
+  - Project point using bearing and distance (wishlist 35)
+  - Possibility to paste a list of coordinates instead of just one
+  - Removal of Gpsies functions (both download and upload)
 
 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
-  - 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
+  - Keyboard shortcut to access point edit dialog (part of Github issue #10)
+  - Online services Inlinemap.net and Graphhopper.com (routing)
+  - Cardinals in java3d view get billboard behaviour to always face camera (thanks, PeHar)
+  - Lighting of 3d views from the northwest (thanks, PeHar)
+  - Remember the name tag from a loaded gpx file, suggest it again for gpx export
 
 New with version 18
 ===================
@@ -279,7 +305,7 @@ Further information and updates
 ===============================
 
 To obtain the source code (if it wasn't included in your jar file), or for further information,
-please visit the website:  http://gpsprune.activityworkshop.net/
+please visit the website:  https://gpsprune.activityworkshop.net/
 
 You will find there user guides, screenshots and demo videos illustrating the major features.
 As GpsPrune is further developed, subsequent versions of the program will also be made freely
index 8e7e9ae39ddeebd905f9561d78c34f7d1de20945..0623b1f72db89714f4ad7006e4bbca3c02d734da 100644 (file)
@@ -142,7 +142,7 @@ public class BaseImageConfigDialog implements Runnable
                {
                        for (int i=0; i<_zoomDropdown.getItemCount(); i++)
                        {
-                               String item = _zoomDropdown.getItemAt(i).toString();
+                               String item = _zoomDropdown.getItemAt(i);
                                try {
                                        if (Integer.parseInt(item) == zoomLevel)
                                        {
index 88ac7914c29a1b92a235ff426e782f07ca4fcae3..f7de8f73485ee87fba0789730a4c0e20b85243eb 100644 (file)
@@ -347,6 +347,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                                 + " " + _exportFile.getAbsolutePath());
                        // export successful so need to close dialog and return
                        _dialog.dispose();
+                       _app.informDataSaved();
                        return;
                }
                catch (IOException ioe)
@@ -583,6 +584,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                        }
                        source = replaceGpxTags(source, "<desc>", "</desc>",
                                XmlUtils.fixCdata(inPoint.getFieldValue(Field.DESCRIPTION)));
+                       source = replaceGpxTags(source, "<cmt>", "</cmt>", inPoint.getFieldValue(Field.COMMENT));
                }
                // photo / audio links
                if (source != null && (inPoint.hasMedia() || source.indexOf("</link>") > 0)) {
@@ -733,13 +735,21 @@ public class GpxExporter extends GenericFunction implements Runnable
                inWriter.write(XmlUtils.fixCdata(inPoint.getWaypointName().trim()));
                inWriter.write("</name>\n");
                // description, if any
-               String desc = XmlUtils.fixCdata(inPoint.getFieldValue(Field.DESCRIPTION));
+               final String desc = XmlUtils.fixCdata(inPoint.getFieldValue(Field.DESCRIPTION));
                if (desc != null && !desc.equals(""))
                {
                        inWriter.write("\t\t<desc>");
                        inWriter.write(desc);
                        inWriter.write("</desc>\n");
                }
+               // comment, if any
+               final String comment = XmlUtils.fixCdata(inPoint.getFieldValue(Field.COMMENT));
+               if (comment != null && !comment.equals(""))
+               {
+                       inWriter.write("\t\t<cmt>");
+                       inWriter.write(comment);
+                       inWriter.write("</cmt>\n");
+               }
                // Media links, if any
                if (inSettings.getExportPhotoPoints() && inPoint.getPhoto() != null)
                {
index ea188921d8b75b4612de63cd1c9335771216d2ed..eb5e6ef2d7dbba711f31a29eee6fb1feb84e5325 100644 (file)
@@ -300,6 +300,7 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer
                final Track track = _app.getTrackInfo().getTrack();
                final int numPoints = track.getNumPoints();
                int prevX = 0, prevY = 0;
+               boolean gotPreviousPoint = false;
                for (int i=0; i<numPoints; i++)
                {
                        DataPoint point = track.getPoint(i);
@@ -316,7 +317,7 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer
                                // use zoom level to calculate pixel coords on image
                                int px = (int) (x * zoomFactor * 256), py = (int) (y * zoomFactor * 256);
                                // System.out.println("Point: x=" + x + ", px=" + px + ", y=" + y + ", py=" + py);
-                               if (!point.getSegmentStart()) {
+                               if (!point.getSegmentStart() && gotPreviousPoint) {
                                        // draw from previous point to this one
                                        g.drawLine(prevX, prevY, px, py);
                                }
@@ -327,6 +328,7 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer
                                }
                                // save coordinates
                                prevX = px; prevY = py;
+                               gotPreviousPoint = true;
                        }
                }
 
@@ -364,7 +366,7 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer
                }
                // Set text size according to input
                int fontScalePercent = _textScaleField.getValue();
-               if (fontScalePercent > 10 && fontScalePercent <= 999)
+               if (fontScalePercent > 0 && fontScalePercent <= 999)
                {
                        Font gFont = g.getFont();
                        g.setFont(gFont.deriveFont((float) (gFont.getSize() * 0.01 * fontScalePercent)));
@@ -395,7 +397,7 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer
                for (int i=0; i<numPoints; i++)
                {
                        DataPoint point = track.getPoint(i);
-                       if (point.isWaypoint())
+                       if (point.isWaypoint() && fontScalePercent > 0)
                        {
                                double x = track.getX(i) - xRange.getMinimum();
                                double y = track.getY(i) - yRange.getMinimum();
index 263a3067f4902b5916c2b5e43d63bfeccdb21c5c..28b65ea0dddacd1b731eda3fdc9e7f8b80b5b7c4 100644 (file)
@@ -108,14 +108,11 @@ public class MapGrouter implements TileConsumer
                                                // Wait until tile is available (loaded asynchronously)
                                                while (tile.getWidth(null) < 0 && !inDownload)
                                                {
-                                                       // System.out.println("Wait for tile width");
                                                        try {
-                                                               Thread.sleep(inDownload ? 500 : 100);
+                                                               Thread.sleep(100);
                                                        }
                                                        catch (InterruptedException ie) {}
                                                }
-                                               // work out where to copy it to, paint it
-                                               // System.out.println("Painting tile " + x + "," + y + " at " + xOffset + "," + yOffset);
                                                numTilesUsed++;
                                                g.drawImage(tile, xOffset, yOffset, null);
                                        }
@@ -177,4 +174,10 @@ public class MapGrouter implements TileConsumer
        {
                // Doesn't need any action
        }
+
+       /** React to cache problem */
+       public void reportCacheFailure()
+       {
+               // Doesn't need any action
+       }
 }
index f5b324390139f6f4af96271e19ce702a87ee52af..75c1c1d73021480ed0b18917dc15d1a85f9681d4 100644 (file)
@@ -54,7 +54,6 @@ import tim.prune.gui.map.MapSourceLibrary;
 import tim.prune.save.GroutedImage;
 import tim.prune.save.MapGrouter;
 
-import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
 import com.sun.j3d.utils.geometry.Box;
 import com.sun.j3d.utils.geometry.Cylinder;
 import com.sun.j3d.utils.geometry.GeometryInfo;
@@ -73,7 +72,7 @@ public class Java3DWindow implements ThreeDWindow
        private JFrame _parentFrame = null;
        private JFrame _frame = null;
        private ThreeDModel _model = null;
-       private OrbitBehavior _orbit = null;
+       private UprightOrbiter _orbit = null;
        private double _altFactor = -1.0;
        private ImageDefinition _imageDefinition = null;
        private GroutedImage _baseImage = null;
@@ -204,7 +203,7 @@ public class Java3DWindow implements ThreeDWindow
                u.getViewingPlatform().setNominalViewingTransform();
 
                // Add behaviour to rotate using mouse
-               _orbit = new OrbitBehavior(canvas, OrbitBehavior.REVERSE_ALL | OrbitBehavior.STOP_ZOOM);
+               _orbit = new UprightOrbiter(canvas, INITIAL_X_ROTATION);
                BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
                _orbit.setSchedulingBounds(bounds);
                u.getViewingPlatform().setViewPlatformBehavior(_orbit);
@@ -359,7 +358,6 @@ public class Java3DWindow implements ThreeDWindow
                                // Store this back in the cache, maybe we'll need it again
                                TerrainCache.storeTerrainTrack(terrainTrack, _dataStatus, _terrainDefinition);
                        }
-                       // else System.out.println("Yay - reusing the cached track!");
 
                        // Give the terrain definition to the _model as well
                        _model.setTerrain(terrainTrack);
diff --git a/src/tim/prune/threedee/UprightOrbiter.java b/src/tim/prune/threedee/UprightOrbiter.java
new file mode 100644 (file)
index 0000000..dd00ec7
--- /dev/null
@@ -0,0 +1,326 @@
+package tim.prune.threedee;
+
+import java.awt.event.InputEvent;
+
+/*
+ * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ * 
+ * Copyright (c) 2021 ActivityWorkshop simplifications and renamings,
+ * and restriction to upright orientations.
+ */
+
+import java.awt.event.MouseEvent;
+import java.awt.AWTEvent;
+
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.Canvas3D;
+
+import javax.vecmath.Vector3d;
+import javax.vecmath.Point3d;
+import javax.vecmath.Matrix3d;
+
+import com.sun.j3d.utils.behaviors.vp.ViewPlatformAWTBehavior;
+import com.sun.j3d.utils.universe.ViewingPlatform;
+
+
+/**
+ * Moves the View around a point of interest when the mouse is dragged with
+ * a mouse button pressed.  Includes rotation, zoom, and translation
+ * actions. Zooming can also be obtained by using mouse wheel.
+ * <p>
+ * The rotate action rotates the ViewPlatform around the point of interest
+ * when the mouse is moved with the main mouse button pressed.  The
+ * rotation is in the direction of the mouse movement, with a default
+ * rotation of 0.01 radians for each pixel of mouse movement.
+ * <p>
+ * The zoom action moves the ViewPlatform closer to or further from the
+ * point of interest when the mouse is moved with the middle mouse button
+ * pressed (or Alt-main mouse button on systems without a middle mouse button).
+ * The default zoom action is to translate the ViewPlatform 0.01 units for each
+ * pixel of mouse movement.  Moving the mouse up moves the ViewPlatform closer,
+ * moving the mouse down moves the ViewPlatform further away.
+ * <p>
+ * The translate action translates the ViewPlatform when the mouse is moved
+ * with the right mouse button pressed.  The translation is in the direction
+ * of the mouse movement, with a default translation of 0.01 units for each
+ * pixel of mouse movement.
+ * <p>
+ * The actions can be reversed using the <code>REVERSE_</code><i>ACTION</i>
+ * constructor flags.  The default action moves the ViewPlatform around the
+ * objects in the scene.  The <code>REVERSE_</code><i>ACTION</i> flags can
+ * make the objects in the scene appear to be moving in the direction
+ * of the mouse movement.
+ */
+public class UprightOrbiter extends ViewPlatformAWTBehavior
+{
+       private Transform3D _longitudeTransform = new Transform3D();
+       private Transform3D _latitudeTransform = new Transform3D();
+       private Transform3D _rotateTransform = new Transform3D();
+
+       // needed for integrateTransforms but don't want to new every time
+       private Transform3D _temp1 = new Transform3D();
+       private Transform3D _temp2 = new Transform3D();
+       private Transform3D _translation = new Transform3D();
+       private Vector3d _transVector = new Vector3d();
+       private Vector3d _distanceVector = new Vector3d();
+       private Vector3d _centerVector = new Vector3d();
+       private Vector3d _invertCenterVector = new Vector3d();
+
+       private double _deltaYaw = 0.0, _deltaPitch = 0.0;
+       private double _startDistanceFromCenter = 20.0;
+       private double _distanceFromCenter = 20.0;
+       private Point3d _rotationCenter = new Point3d();
+       private Matrix3d _rotMatrix = new Matrix3d();
+
+       private int _mouseX = 0, _mouseY = 0;
+
+       private double _xtrans = 0.0, _ytrans = 0.0, _ztrans = 0.0;
+
+       private static final double MIN_RADIUS = 0.0;
+
+       // the factor to be applied to wheel zooming so that it does not 
+       // look much different with mouse movement zooming. 
+       private static final float wheelZoomFactor = 50.0f;
+
+       private static final double NOMINAL_ZOOM_FACTOR = .01;
+       private static final double NOMINAL_ROT_FACTOR = .008;
+       private static final double NOMINAL_TRANS_FACTOR = .003;
+
+       private double _pitchAngle = 0.0;
+
+
+       /**
+        * Creates a new OrbitBehaviour
+        * @param inCanvas The Canvas3D to add the behaviour to
+        * @param inInitialPitch pitch angle in degrees
+        */
+       public UprightOrbiter(Canvas3D inCanvas, double inInitialPitch)
+       {
+               super(inCanvas, MOUSE_LISTENER | MOUSE_MOTION_LISTENER | MOUSE_WHEEL_LISTENER );
+               _pitchAngle = Math.toRadians(inInitialPitch);
+       }
+
+       protected synchronized void processAWTEvents( final AWTEvent[] events )
+       {
+               motion = false;
+               for(int i=0; i<events.length; i++)
+                       if (events[i] instanceof MouseEvent) 
+                               processMouseEvent( (MouseEvent)events[i] );
+       }
+
+       protected void processMouseEvent( final MouseEvent evt )
+       {
+               if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
+                       _mouseX = evt.getX();
+                       _mouseY = evt.getY();
+                       motion = true;
+               }
+               else if (evt.getID() == MouseEvent.MOUSE_DRAGGED)
+               {
+                       int xchange = evt.getX() - _mouseX;
+                       int ychange = evt.getY() - _mouseY;
+                       // rotate
+                       if (isRotateEvent(evt))
+                       {
+                               _deltaYaw -= xchange * NOMINAL_ROT_FACTOR;
+                               _deltaPitch -= ychange * NOMINAL_ROT_FACTOR;
+                       }
+                       // translate
+                       else if (isTranslateEvent(evt))
+                       {
+                               _xtrans -= xchange * NOMINAL_TRANS_FACTOR;
+                               _ytrans += ychange * NOMINAL_TRANS_FACTOR;
+                       }
+                       // zoom
+                       else if (isZoomEvent(evt)) {
+                               doZoomOperations( ychange );
+                       }
+                       _mouseX = evt.getX();
+                       _mouseY = evt.getY();
+                       motion = true;
+               }
+               else if (evt.getID() == MouseEvent.MOUSE_WHEEL )
+               {
+                       if (isZoomEvent(evt))
+                       {
+                               // if zooming is done through mouse wheel, the number of wheel increments
+                               // is multiplied by the wheelZoomFactor, to make zoom speed look natural
+                               if ( evt instanceof java.awt.event.MouseWheelEvent)
+                               {
+                                       int zoom = ((int)(((java.awt.event.MouseWheelEvent)evt).getWheelRotation()
+                                               * wheelZoomFactor));
+                                       doZoomOperations( zoom );
+                                       motion = true;
+                               }
+                       }
+               }
+       }
+
+       /*
+        * zoom but stop at MIN_RADIUS
+        */
+       private void doZoomOperations( int ychange )
+       {
+               if ((_distanceFromCenter - ychange * NOMINAL_ZOOM_FACTOR) > MIN_RADIUS) {
+                       _distanceFromCenter -= ychange * NOMINAL_ZOOM_FACTOR;
+               }
+               else {
+                       _distanceFromCenter = MIN_RADIUS;
+               }
+       }
+
+       /**
+        * Sets the ViewingPlatform for this behaviour.  This method is
+        * called by the ViewingPlatform.
+        * If a sub-calls overrides this method, it must call
+        * super.setViewingPlatform(vp).
+        * NOTE: Applications should <i>not</i> call this method.
+        */
+       @Override
+       public void setViewingPlatform(ViewingPlatform vp)
+       {
+               super.setViewingPlatform( vp );
+
+               if (vp != null) {
+                       resetView();
+                       integrateTransforms();
+               }
+       }
+
+       /**
+        * Reset the orientation and distance of this behaviour to the current
+        * values in the ViewPlatform Transform Group
+        */
+       private void resetView()
+       {
+               Vector3d centerToView = new Vector3d();
+
+               targetTG.getTransform( targetTransform );
+
+               targetTransform.get( _rotMatrix, _transVector );
+               centerToView.sub( _transVector, _rotationCenter );
+               _distanceFromCenter = centerToView.length();
+               _startDistanceFromCenter = _distanceFromCenter;
+
+               targetTransform.get( _rotMatrix );
+               _rotateTransform.set( _rotMatrix );
+
+               // compute the initial x/y/z offset
+               _temp1.set(centerToView);
+               _rotateTransform.invert();
+               _rotateTransform.mul(_temp1);
+               _rotateTransform.get(centerToView);
+               _xtrans = centerToView.x;
+               _ytrans = centerToView.y;
+               _ztrans = centerToView.z;
+
+               // reset rotMatrix
+               _rotateTransform.set( _rotMatrix );
+       }
+
+       protected synchronized void integrateTransforms()
+       {
+               // Check if the transform has been changed by another behaviour
+               Transform3D currentXfm = new Transform3D();
+               targetTG.getTransform(currentXfm);
+               if (! targetTransform.equals(currentXfm))
+                       resetView();
+
+               // Three-step rotation process, firstly undo the pitch and apply the delta yaw
+               _latitudeTransform.rotX(_pitchAngle);
+               _rotateTransform.mul(_rotateTransform, _latitudeTransform);
+               _longitudeTransform.rotY( _deltaYaw );
+               _rotateTransform.mul(_rotateTransform, _longitudeTransform);
+               // Now update pitch angle according to delta and apply
+               _pitchAngle = Math.min(Math.max(0.0, _pitchAngle - _deltaPitch), Math.PI/2.0);
+               _latitudeTransform.rotX(-_pitchAngle);
+               _rotateTransform.mul(_rotateTransform, _latitudeTransform);
+
+               _distanceVector.z = _distanceFromCenter - _startDistanceFromCenter;
+
+               _temp1.set(_distanceVector);
+               _temp1.mul(_rotateTransform, _temp1);
+
+               // want to look at rotationCenter
+               _transVector.x = _rotationCenter.x + _xtrans;
+               _transVector.y = _rotationCenter.y + _ytrans;
+               _transVector.z = _rotationCenter.z + _ztrans;
+
+               _translation.set(_transVector);
+               targetTransform.mul(_temp1, _translation);
+
+               // handle rotationCenter
+               _temp1.set(_centerVector);
+               _temp1.mul(targetTransform);
+
+               _invertCenterVector.x = -_centerVector.x;
+               _invertCenterVector.y = -_centerVector.y;
+               _invertCenterVector.z = -_centerVector.z;
+
+               _temp2.set(_invertCenterVector);
+               targetTransform.mul(_temp1, _temp2);
+
+               Vector3d finalTranslation = new Vector3d();
+               targetTransform.get(finalTranslation);
+
+               targetTG.setTransform(targetTransform);
+
+               // reset yaw and pitch deltas
+               _deltaYaw = 0.0;
+               _deltaPitch = 0.0;
+       }
+
+       private boolean isRotateEvent(MouseEvent evt)
+       {
+               final boolean isRightDrag = (evt.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) > 0;
+               return !evt.isAltDown() && !isRightDrag;
+       }
+
+       private boolean isZoomEvent(MouseEvent evt)
+       {
+               if (evt instanceof java.awt.event.MouseWheelEvent) {
+                       return true;
+               }
+               return evt.isAltDown() && !evt.isMetaDown();
+       }
+
+       private boolean isTranslateEvent(MouseEvent evt)
+       {
+               final boolean isRightDrag = (evt.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) > 0;
+               return !evt.isAltDown() && isRightDrag;
+       }
+}
diff --git a/test/tim/prune/data/RangeStatsTest.java b/test/tim/prune/data/RangeStatsTest.java
new file mode 100644 (file)
index 0000000..d068ac1
--- /dev/null
@@ -0,0 +1,197 @@
+package tim.prune.data;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * JUnit tests for calculation of moving time of a range
+ * based on different timestamp availability
+ * @author fperrin
+ */
+class RangeStatsTest
+{
+       @Test
+       void movingTime()
+       {
+               Track track = new Track();
+               DataPoint[] points = {
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:00",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:05",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:07",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+               };
+               track.appendPoints(points);
+
+               RangeStats range = new RangeStats(track, 0, track.getNumPoints() - 1);
+               assertEquals(7, range.getMovingDurationInSeconds());
+               assertEquals(7, range.getTotalDurationInSeconds());
+               assertFalse(range.getTimestampsIncomplete());
+               assertFalse(range.getTimestampsOutOfSequence());
+       }
+
+       @Test
+       void movingTimeWithGap()
+       {
+               Track track = new Track();
+               DataPoint[] points = {
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:00",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+                       new DataPoint(
+                               new String[] {},
+                               new FieldList(new Field[] {}),
+                               null),
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:05",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:07",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+               };
+               track.appendPoints(points);
+
+               RangeStats range = new RangeStats(track, 0, track.getNumPoints() - 1);
+               assertEquals(7, range.getMovingDurationInSeconds());
+               assertEquals(7, range.getTotalDurationInSeconds());
+               assertTrue(range.getTimestampsIncomplete());
+               assertFalse(range.getTimestampsOutOfSequence());
+       }
+
+       @Test
+       void movingTimeSeveralSegments()
+       {
+               Track track = new Track();
+               DataPoint[] points = {
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:01:00",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+                       new DataPoint(
+                               new String[] {},
+                               new FieldList(new Field[] {}),
+                               null),
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:01:05",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:01:07",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+                       // start a second segment
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:20",
+                                       "1",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                                       Field.NEW_SEGMENT,
+                               }),
+                               null),
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:27",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+               };
+               track.appendPoints(points);
+
+               RangeStats range = new RangeStats(track, 0, track.getNumPoints() - 1);
+               assertEquals(7 + 7, range.getMovingDurationInSeconds());
+               assertEquals(47, range.getTotalDurationInSeconds());
+               assertTrue(range.getEarliestTimestamp().isEqual(new TimestampUtc("01-Jan-2020 00:00:20")));
+               assertTrue(range.getLatestTimestamp().isEqual(new TimestampUtc("01-Jan-2020 00:01:07")));
+               assertTrue(range.getTimestampsIncomplete());
+
+               // even though segment 2 is earlier than segment 1, timestamps
+               // within each segment are normally ordered
+               assertFalse(range.getTimestampsOutOfSequence());
+       }
+
+       @Test
+       void movingTimeMissingFirstTimestamp()
+       {
+               Track track = new Track();
+               DataPoint[] points = {
+                       new DataPoint(
+                               new String[] {},
+                               new FieldList(new Field[] {}),
+                               null),
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:00",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+                       new DataPoint(
+                               new String[] {
+                                       "01-Jan-2020 00:00:05",
+                               },
+                               new FieldList(new Field[] {
+                                       Field.TIMESTAMP,
+                               }),
+                               null),
+               };
+               track.appendPoints(points);
+
+               RangeStats range = new RangeStats(track, 0, track.getNumPoints() - 1);
+               assertEquals(5, range.getMovingDurationInSeconds());
+               assertEquals(5, range.getTotalDurationInSeconds());
+               assertTrue(range.getTimestampsIncomplete());
+               assertFalse(range.getTimestampsOutOfSequence());
+       }
+}
diff --git a/test/tim/prune/function/cache/TileSetTest.java b/test/tim/prune/function/cache/TileSetTest.java
new file mode 100644 (file)
index 0000000..4ff108c
--- /dev/null
@@ -0,0 +1,44 @@
+package tim.prune.function.cache;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * JUnit tests for tile name checks
+ */
+class TileSetTest
+{
+       @Test
+       void testIsNumeric()
+       {
+               // not numeric, should be false
+               assertFalse(TileSet.isNumeric(null));
+               assertFalse(TileSet.isNumeric(""));
+               assertFalse(TileSet.isNumeric("a"));
+               assertFalse(TileSet.isNumeric(" "));
+               assertFalse(TileSet.isNumeric("155a"));
+               assertFalse(TileSet.isNumeric("-2"));
+               // numeric, should be true
+               assertTrue(TileSet.isNumeric("1"));
+               assertTrue(TileSet.isNumeric("155"));
+       }
+
+       @Test
+       void testIsNumericUntilDot()
+       {
+               // not numeric, should be false
+               assertFalse(TileSet.isNumericUntilDot(null));
+               assertFalse(TileSet.isNumericUntilDot(""));
+               assertFalse(TileSet.isNumericUntilDot("."));
+               assertFalse(TileSet.isNumericUntilDot(".abc"));
+               assertFalse(TileSet.isNumericUntilDot("a3."));
+               assertFalse(TileSet.isNumericUntilDot("4a"));
+               assertFalse(TileSet.isNumericUntilDot("215327h.png"));
+               // numeric but no dot, should be false
+               assertFalse(TileSet.isNumericUntilDot("1234"));
+               // numeric, should be true
+               System.out.println(TileSet.isNumericUntilDot("44.jpg"));
+               System.out.println(TileSet.isNumericUntilDot("0."));
+       }
+}
diff --git a/test/tim/prune/function/olc/OlcDecoderTest.java b/test/tim/prune/function/olc/OlcDecoderTest.java
new file mode 100644 (file)
index 0000000..951a081
--- /dev/null
@@ -0,0 +1,75 @@
+package tim.prune.function.olc;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * JUnit tests for decoding of Open Location Codes (Pluscodes)
+ */
+class OlcDecoderTest
+{
+
+       @Test
+       void testDecodeStringsTooShort()
+       {
+               OlcArea area = OlcDecoder.decode(null);
+               assertEquals(area, null, "Decoding null gives null");
+               area = OlcDecoder.decode("");
+               assertEquals(area, null, "Decoding \"\" gives null");
+               area = OlcDecoder.decode("9");
+               assertEquals(area, null, "Decoding \"9\" gives null");
+               area = OlcDecoder.decode("9999999");
+               assertEquals(area, null, "Decoding \"9999999\" gives null");
+       }
+
+       @Test
+       void testDecodeStringsInvalid()
+       {
+               OlcArea area = OlcDecoder.decode("11111111");
+               assertEquals(area, null, "Decoding lots of 1s gives null");
+               area = OlcDecoder.decode("99999991");
+               assertEquals(area, null, "Decoding with a single 1 gives null");
+               area = OlcDecoder.decode("99999999");
+               assertNotEquals(area, null, "Decoding with all 9s gives non-null");
+               area = OlcDecoder.decode("00000000");
+               assertEquals(area, null, "Decoding with all padding gives null");
+               area = OlcDecoder.decode("99000000");
+               assertNotEquals(area, null, "Decoding with some padding gives non-null");
+       }
+
+       @Test
+       void testDecodeZeroes()
+       {
+               OlcArea area = OlcDecoder.decode("22000000");
+               assertNotEquals(area, null, "Decoding with padding gives non-null");
+               assertEquals(-90.0, area.minLat, 0.0, "South 90");
+               assertEquals(-70.0, area.maxLat, 0.0, "South 70");
+               assertEquals(-180.0, area.minLon, 0.0, "West 180");
+               assertEquals(-160.0, area.maxLon, 0.0, "West 160");
+       }
+
+       @Test
+       void testDecodeZeroes2()
+       {
+               OlcArea area = OlcDecoder.decode("22220000");
+               assertNotEquals(area, null, "Decoding with padding gives non-null");
+               assertEquals(-90.0, area.minLat, 0.0, "South 90");
+               assertEquals(-89.0, area.maxLat, 0.0, "South 89");
+               assertEquals(-180.0, area.minLon, 0.0, "West 180");
+               assertEquals(-179.0, area.maxLon, 0.0, "West 179");
+       }
+
+       @Test
+       void testMountainView()
+       {
+               OlcArea area = OlcDecoder.decode("6PH57VP3+PR6");
+               assertNotEquals(area, null, "Decoding with separator gives non-null");
+               System.out.println("Min lat: " + area.minLat);
+               System.out.println("Max lat: " + area.maxLat);
+               System.out.println("Min lon: " + area.minLon);
+               System.out.println("Max lon: " + area.maxLon);
+               assertTrue(area.maxLat > area.minLat, "latitude range");
+               assertTrue(area.maxLon > area.minLon, "longitude range");
+       }
+}
diff --git a/test/tim/prune/function/weather/SingleForecastTest.java b/test/tim/prune/function/weather/SingleForecastTest.java
new file mode 100644 (file)
index 0000000..372ab47
--- /dev/null
@@ -0,0 +1,52 @@
+package tim.prune.function.weather;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * JUnit tests for weather icons
+ */
+class SingleForecastTest
+{
+
+       @Test
+       void testWeatherIcons()
+       {
+               testIconName(null, "100", "");
+               testIconName("storm.png", "200", null);
+               testIconName("storm.png", "204", "");
+               testIconName("lightrain.png", "300", null);
+               testIconName("lightrain.png", "301", null);
+               testIconName(null, "400", null);
+               testIconName("lightrain.png", "500", null);
+               testIconName("rain.png", "501", null);
+               testIconName("rain.png", "599", null);
+               testIconName("hail.png", "511", null);
+               testIconName("snow.png", "600", null);
+               testIconName("fog.png", "700", null);
+               testIconName("clear-day.png", "800", null);
+               testIconName("clear-day.png", "800", "");
+               testIconName("clear-day.png", "800", "01d");
+               testIconName("clear-night.png", "800", "01n");
+               testIconName("clouds-day.png", "802", "01d");
+               testIconName("clouds-night.png", "802", "01n");
+               testIconName("clouds.png", "804", "01n");
+               testIconName("extreme.png", "900", "01d");
+               testIconName("hail.png", "906", "01n");
+       }
+
+       /**
+        * Test getting an icon name according to code and image
+        */
+       private static void testIconName(String inExpect, String inCode, String inImage)
+       {
+               String icon = SingleForecast.getIconName(inCode, inImage);
+               assertEquals(inExpect, icon, showString(inCode) + ", " + showString(inImage));
+       }
+
+       private static String showString(String inString)
+       {
+               return inString == null ? "null" : inString;
+       }
+}
diff --git a/test/tim/prune/gui/map/MapSourceTest.java b/test/tim/prune/gui/map/MapSourceTest.java
new file mode 100644 (file)
index 0000000..ff947c3
--- /dev/null
@@ -0,0 +1,31 @@
+package tim.prune.gui.map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * JUnit tests for manipulating base Urls
+ */
+class MapSourceTest
+{
+       @Test
+       void testFixBaseUrls()
+       {
+               // Should succeed
+               testUrlFix("8bitcities.s3.amazonaws.com", "http://8bitcities.s3.amazonaws.com/");
+               testUrlFix("8bitcities.s3.amazonaws.com/", "http://8bitcities.s3.amazonaws.com/");
+               testUrlFix("http://8bitcities.s3.amazonaws.com/", "http://8bitcities.s3.amazonaws.com/");
+               testUrlFix("something.com/ok", "http://something.com/ok/");
+
+               // These should fail and return null
+               testUrlFix("something/wrong", null);
+               testUrlFix("protocol://something.com/16/", null);
+       }
+
+       private void testUrlFix(String inStart, String inExpected)
+       {
+               String result = MapSource.fixBaseUrl(inStart);
+               assertEquals(inExpected, result);
+       }
+}
diff --git a/test/tim/prune/gui/map/SiteNameUtilsTest.java b/test/tim/prune/gui/map/SiteNameUtilsTest.java
new file mode 100644 (file)
index 0000000..e01ed52
--- /dev/null
@@ -0,0 +1,63 @@
+package tim.prune.gui.map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.HashSet;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * JUnit tests for site name utils
+ */
+class SiteNameUtilsTest
+{
+
+       @Test
+       void testPickServerNameWithoutWildcards()
+       {
+               testPickSingleUrl("abc", "abc");
+               testPickSingleUrl("ab[]c", "abc");
+               testPickSingleUrl("[]abc", "abc");
+               testPickSingleUrl("abc[]", "abc");
+       }
+
+       /**
+        * Test a pattern without wildcards which should always produce the expected result
+        * @param inPattern pattern for site name
+        * @param inExpected expected resolved name
+        */
+       private void testPickSingleUrl(String inPattern, String inExpected)
+       {
+               for (int i=0; i<20; i++)
+               {
+                       String resolved = SiteNameUtils.pickServerUrl(inPattern);
+                       assertEquals(inExpected, resolved, "Failed: " + inPattern);
+               }
+       }
+
+       @Test
+       void testPickUsingWildcards()
+       {
+               testRandomPick("ab[123]c", new String[]{"ab1c", "ab2c", "ab3c"});
+               testRandomPick("1234.[abcd]", new String[]{"1234.a", "1234.b", "1234.c", "1234.d"});
+       }
+
+       /**
+        * Test a pattern with wildcards which should produce several different results randomly
+        * @param inPattern pattern for site name
+        * @param inExpected array of expected resolved names
+        */
+       private void testRandomPick(String inPattern, String[] inExpected)
+       {
+               HashSet<String> results = new HashSet<String>();
+               for (int i=0; i<30; i++)
+               {
+                       results.add(SiteNameUtils.pickServerUrl(inPattern));
+               }
+               // Check that all expected results were returned
+               assertEquals(inExpected.length, results.size());
+               for (String expec : inExpected) {
+                       assertTrue(results.contains(expec));
+               }
+       }
+}
diff --git a/test/tim/prune/jpeg/drew/RationalTest.java b/test/tim/prune/jpeg/drew/RationalTest.java
new file mode 100644 (file)
index 0000000..ae97e0f
--- /dev/null
@@ -0,0 +1,71 @@
+package tim.prune.jpeg.drew;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * JUnit tests for the Rational values used by the Exif
+ */
+class RationalTest
+{
+       @Test
+       void testManyInts()
+       {
+               testIntVal(0, 0, 0);
+               testIntVal(1, 0, 0);
+               testIntVal(0, 1, 0);
+               for (int i=0; i<16000; i++)
+               {
+                       testIntVal(0, i, 0);
+                       testIntVal(i, 0, 0);
+                       testIntVal(i, 1, i);
+                       testIntVal(-i, 1, -i);
+                       testIntVal(i*2, 2, i);
+                       testIntVal(i*2+1, 2, i);        // rounding down the 0.5
+                       testIntVal(-i*2, 2, -i);
+                       testIntVal(i*2, -2, -i);
+                       testIntVal(-i*2, -2, i);
+               }
+       }
+
+       /**
+        * Check that a rational converts to an integer properly
+        * @param inTop number on top of the rational (numerator)
+        * @param inBottom number on bottom of the rational (denominator)
+        * @param inExpected expected int value
+        */
+       private void testIntVal(long inTop, long inBottom, int inExpected)
+       {
+               Rational value = new Rational(inTop, inBottom);
+               assertEquals(inExpected, value.intValue(), "" + inTop + "/" + inBottom);
+       }
+
+       @Test
+       void testManyDoubles()
+       {
+               for (int i=0; i<16000; i++)
+               {
+                       testDoubleVal(0, i, 0.0);
+                       testDoubleVal(i, 0, 0.0);
+                       testDoubleVal(i, 1, i);
+                       testDoubleVal(i*2, 2, i);
+                       testDoubleVal(i*2+1, 2, i+0.5);
+                       testDoubleVal(i*2, -2, -i);
+               }
+
+               testDoubleVal(123, 3, 123.0/3.0);
+       }
+
+       /**
+        * Check that a rational converts to a double properly
+        * @param inTop number on top of the rational (numerator)
+        * @param inBottom number on bottom of the rational (denominator)
+        * @param inExpected expected double value (exact)
+        */
+       private void testDoubleVal(long inTop, long inBottom, double inExpected)
+       {
+               Rational value = new Rational(inTop, inBottom);
+               assertEquals(inExpected, value.doubleValue(), 0.0, "" + inTop + "/" + inBottom);
+       }
+}