From a6197ddcaac11c0b943183da7d46169742d024af Mon Sep 17 00:00:00 2001 From: activityworkshop Date: Wed, 18 Feb 2015 21:49:14 +0100 Subject: [PATCH] Version 17, September 2014 --- tim/prune/App.java | 40 ++- tim/prune/FunctionLibrary.java | 13 +- tim/prune/GpsPrune.java | 28 ++- tim/prune/UpdateMessageBroker.java | 2 +- tim/prune/config/Config.java | 34 +++ tim/prune/data/AltitudeRange.java | 147 +++++++++-- tim/prune/data/Checker.java | 56 ++++- tim/prune/data/Coordinate.java | 21 +- tim/prune/data/FileInfo.java | 15 ++ tim/prune/data/GradientCalculator.java | 53 ++++ tim/prune/data/SourceInfo.java | 8 +- tim/prune/data/SpeedCalculator.java | 5 +- tim/prune/data/Timestamp.java | 90 +++++-- tim/prune/data/Track.java | 60 +---- .../sort}/PhotoComparer.java | 12 +- tim/prune/data/sort/SortMode.java | 11 + tim/prune/data/sort/WaypointComparer.java | 74 ++++++ tim/prune/function/ChooseSingleParameter.java | 158 ++++++++++++ tim/prune/function/GetWikipediaFunction.java | 2 +- tim/prune/function/InterpolateFunction.java | 57 ++--- tim/prune/function/RearrangeFunction.java | 207 ++++++++++++++++ .../function/RearrangePhotosFunction.java | 173 ++++--------- .../function/RearrangeWaypointsFunction.java | 160 ++++++++++-- tim/prune/function/RemoveAudioFunction.java | 5 +- tim/prune/function/RemovePhotoFunction.java | 5 +- tim/prune/function/SaveConfig.java | 1 + tim/prune/function/SearchWikipediaNames.java | 2 +- tim/prune/function/SelectSegmentFunction.java | 47 ++++ tim/prune/function/SetAltitudeTolerance.java | 75 ++++++ tim/prune/function/SetColours.java | 68 +++--- tim/prune/function/SetLineWidth.java | 52 ++-- tim/prune/function/ShowThreeDFunction.java | 6 +- .../SingleNumericParameterFunction.java | 36 +++ tim/prune/function/charts/Charter.java | 5 +- .../compress/CompressTrackFunction.java | 23 +- .../compress/MarkAndDeleteFunction.java | 51 ++++ .../MarkPointsInRectangleFunction.java | 28 +-- tim/prune/function/deletebydate/DateInfo.java | 112 +++++++++ .../function/deletebydate/DateInfoList.java | 121 +++++++++ .../deletebydate/DeleteByDateFunction.java | 215 ++++++++++++++++ .../deletebydate/DeletionTableModel.java | 140 +++++++++++ .../function/distance/DistanceTableModel.java | 14 +- tim/prune/gui/DetailsDisplay.java | 71 ++++-- tim/prune/gui/IconManager.java | 2 +- tim/prune/gui/MenuManager.java | 62 ++--- tim/prune/gui/TerrainDefinitionPanel.java | 3 +- tim/prune/gui/WholeNumberField.java | 7 + tim/prune/gui/colour/AltitudeColourer.java | 70 ++++++ tim/prune/gui/{ => colour}/ColourChooser.java | 2 +- tim/prune/gui/{ => colour}/ColourPatch.java | 3 +- tim/prune/gui/colour/ColourerCaretaker.java | 58 +++++ tim/prune/gui/colour/ColourerFactory.java | 205 ++++++++++++++++ .../gui/colour/ColourerSelectorPanel.java | 230 ++++++++++++++++++ .../gui/colour/ContinuousPointColourer.java | 74 ++++++ tim/prune/gui/colour/DateColourer.java | 111 +++++++++ .../gui/colour/DiscretePointColourer.java | 124 ++++++++++ tim/prune/gui/colour/FileColourer.java | 70 ++++++ tim/prune/gui/colour/GradientColourer.java | 32 +++ tim/prune/gui/colour/PatchListener.java | 36 +++ tim/prune/gui/colour/PointColourer.java | 119 +++++++++ tim/prune/gui/colour/ProfileDataColourer.java | 57 +++++ tim/prune/gui/colour/SegmentColourer.java | 52 ++++ tim/prune/gui/colour/SpeedColourer.java | 32 +++ tim/prune/gui/colour/VertSpeedColourer.java | 32 +++ tim/prune/gui/images/add_photo_icon.png | Bin tim/prune/gui/images/add_textfile_icon.png | Bin tim/prune/gui/images/window_icon.png | Bin 261 -> 0 bytes tim/prune/gui/images/window_icon_128.png | Bin 0 -> 7176 bytes tim/prune/gui/images/window_icon_16.png | Bin 0 -> 662 bytes tim/prune/gui/images/window_icon_20.png | Bin 0 -> 819 bytes tim/prune/gui/images/window_icon_32.png | Bin 0 -> 1354 bytes tim/prune/gui/images/window_icon_64.png | Bin 0 -> 3073 bytes tim/prune/gui/map/MapCanvas.java | 124 ++++++---- tim/prune/gui/map/MapSource.java | 2 + tim/prune/gui/profile/GradientData.java | 66 +++++ tim/prune/gui/profile/SpeedData.java | 4 +- tim/prune/gui/profile/VerticalSpeedData.java | 4 +- tim/prune/lang/prune-texts_af.properties | 17 +- tim/prune/lang/prune-texts_cz.properties | 18 +- tim/prune/lang/prune-texts_da.properties | 4 +- tim/prune/lang/prune-texts_de.properties | 50 +++- tim/prune/lang/prune-texts_de_CH.properties | 54 ++-- tim/prune/lang/prune-texts_en.properties | 46 +++- tim/prune/lang/prune-texts_en_US.properties | 8 + tim/prune/lang/prune-texts_es.properties | 27 +- tim/prune/lang/prune-texts_fa.properties | 8 +- tim/prune/lang/prune-texts_fr.properties | 22 +- tim/prune/lang/prune-texts_hu.properties | 17 +- tim/prune/lang/prune-texts_it.properties | 39 ++- tim/prune/lang/prune-texts_ja.properties | 16 +- tim/prune/lang/prune-texts_ko.properties | 16 +- tim/prune/lang/prune-texts_nl.properties | 50 +++- tim/prune/lang/prune-texts_pl.properties | 86 +++++-- tim/prune/lang/prune-texts_pt.properties | 20 +- tim/prune/lang/prune-texts_ro.properties | 203 ++++++++++++++-- tim/prune/lang/prune-texts_ru.properties | 19 +- tim/prune/lang/prune-texts_sv.properties | 8 +- tim/prune/lang/prune-texts_tr.properties | 8 +- tim/prune/lang/prune-texts_uk.properties | 8 +- tim/prune/lang/prune-texts_zh.properties | 47 +++- tim/prune/load/JpegLoader.java | 2 +- tim/prune/readme.txt | 64 ++--- tim/prune/save/FileSaver.java | 16 +- tim/prune/save/GpxExporter.java | 6 +- tim/prune/save/ImageExporter.java | 18 +- tim/prune/save/KmlExporter.java | 6 +- tim/prune/save/PovExporter.java | 5 +- tim/prune/undo/UndoDeleteOperation.java | 22 +- 108 files changed, 4196 insertions(+), 818 deletions(-) create mode 100644 tim/prune/data/GradientCalculator.java rename tim/prune/{function => data/sort}/PhotoComparer.java (91%) create mode 100644 tim/prune/data/sort/SortMode.java create mode 100644 tim/prune/data/sort/WaypointComparer.java create mode 100644 tim/prune/function/ChooseSingleParameter.java create mode 100644 tim/prune/function/RearrangeFunction.java create mode 100644 tim/prune/function/SelectSegmentFunction.java create mode 100644 tim/prune/function/SetAltitudeTolerance.java create mode 100644 tim/prune/function/SingleNumericParameterFunction.java create mode 100644 tim/prune/function/compress/MarkAndDeleteFunction.java create mode 100644 tim/prune/function/deletebydate/DateInfo.java create mode 100644 tim/prune/function/deletebydate/DateInfoList.java create mode 100644 tim/prune/function/deletebydate/DeleteByDateFunction.java create mode 100644 tim/prune/function/deletebydate/DeletionTableModel.java create mode 100644 tim/prune/gui/colour/AltitudeColourer.java rename tim/prune/gui/{ => colour}/ColourChooser.java (99%) rename tim/prune/gui/{ => colour}/ColourPatch.java (90%) create mode 100644 tim/prune/gui/colour/ColourerCaretaker.java create mode 100644 tim/prune/gui/colour/ColourerFactory.java create mode 100644 tim/prune/gui/colour/ColourerSelectorPanel.java create mode 100644 tim/prune/gui/colour/ContinuousPointColourer.java create mode 100644 tim/prune/gui/colour/DateColourer.java create mode 100644 tim/prune/gui/colour/DiscretePointColourer.java create mode 100644 tim/prune/gui/colour/FileColourer.java create mode 100644 tim/prune/gui/colour/GradientColourer.java create mode 100644 tim/prune/gui/colour/PatchListener.java create mode 100644 tim/prune/gui/colour/PointColourer.java create mode 100644 tim/prune/gui/colour/ProfileDataColourer.java create mode 100644 tim/prune/gui/colour/SegmentColourer.java create mode 100644 tim/prune/gui/colour/SpeedColourer.java create mode 100644 tim/prune/gui/colour/VertSpeedColourer.java mode change 100755 => 100644 tim/prune/gui/images/add_photo_icon.png mode change 100755 => 100644 tim/prune/gui/images/add_textfile_icon.png delete mode 100644 tim/prune/gui/images/window_icon.png create mode 100644 tim/prune/gui/images/window_icon_128.png create mode 100644 tim/prune/gui/images/window_icon_16.png create mode 100644 tim/prune/gui/images/window_icon_20.png create mode 100644 tim/prune/gui/images/window_icon_32.png create mode 100644 tim/prune/gui/images/window_icon_64.png create mode 100644 tim/prune/gui/profile/GradientData.java diff --git a/tim/prune/App.java b/tim/prune/App.java index e57dcf6..c69d73e 100644 --- a/tim/prune/App.java +++ b/tim/prune/App.java @@ -35,6 +35,8 @@ import tim.prune.gui.MenuManager; import tim.prune.gui.SidebarController; import tim.prune.gui.UndoManager; import tim.prune.gui.Viewport; +import tim.prune.gui.colour.ColourerCaretaker; +import tim.prune.gui.colour.PointColourer; import tim.prune.load.FileLoader; import tim.prune.load.JpegLoader; import tim.prune.load.MediaLinkInfo; @@ -61,6 +63,7 @@ public class App private JpegLoader _jpegLoader = null; private FileSaver _fileSaver = null; private UndoStack _undoStack = null; + private ColourerCaretaker _colCaretaker = null; private boolean _mangleTimestampsConfirmed = false; private Viewport _viewport = null; private ArrayList _dataFiles = null; @@ -83,6 +86,9 @@ public class App _track = new Track(); _trackInfo = new TrackInfo(_track); FunctionLibrary.initialise(this); + _colCaretaker = new ColourerCaretaker(this); + UpdateMessageBroker.addSubscriber(_colCaretaker); + _colCaretaker.setColourer(Config.getPointColourer()); } @@ -120,6 +126,24 @@ public class App return _undoStack; } + /** + * Update the system's point colourer using the one in the Config + */ + public void updatePointColourer() + { + if (_colCaretaker != null) { + _colCaretaker.setColourer(Config.getPointColourer()); + } + } + + /** + * @return colourer object, or null + */ + public PointColourer getPointColourer() + { + if (_colCaretaker == null) {return null;} + return _colCaretaker.getColourer(); + } /** * Show the specified tip if appropriate @@ -319,8 +343,10 @@ public class App int audioIndex = _trackInfo.getAudioList().getAudioIndex(currentPoint.getAudio()); DataPoint nextTrackPoint = _trackInfo.getTrack().getNextTrackPoint(pointIndex + 1); // Construct Undo object - UndoOperation undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex, + UndoDeletePoint undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex, audioIndex, nextTrackPoint != null && nextTrackPoint.getSegmentStart()); + undo.setAtBoundaryOfSelectedRange(pointIndex == _trackInfo.getSelection().getStart() || + pointIndex == _trackInfo.getSelection().getEnd()); // call track to delete point if (_trackInfo.deletePoint()) { @@ -498,12 +524,22 @@ public class App * @param inPoint point to add */ public void createPoint(DataPoint inPoint) + { + createPoint(inPoint, true); + } + + /** + * Create a new point at the end of the track + * @param inPoint point to add + * @param inNewSegment true for a single point, false for a continuation + */ + public void createPoint(DataPoint inPoint, boolean inNewSegment) { // create undo object UndoCreatePoint undo = new UndoCreatePoint(); _undoStack.add(undo); // add point to track - inPoint.setSegmentStart(true); + inPoint.setSegmentStart(inNewSegment); _track.appendPoints(new DataPoint[] {inPoint}); // ensure track's field list contains point's fields _track.extendFieldList(inPoint.getFieldList()); diff --git a/tim/prune/FunctionLibrary.java b/tim/prune/FunctionLibrary.java index 9c51c8d..a8d7e0e 100644 --- a/tim/prune/FunctionLibrary.java +++ b/tim/prune/FunctionLibrary.java @@ -6,6 +6,7 @@ import tim.prune.function.*; import tim.prune.function.charts.Charter; import tim.prune.function.compress.CompressTrackFunction; import tim.prune.function.compress.MarkPointsInRectangleFunction; +import tim.prune.function.deletebydate.DeleteByDateFunction; import tim.prune.function.distance.DistanceFunction; import tim.prune.function.edit.PointNameEditor; import tim.prune.function.estimate.EstimateTime; @@ -42,7 +43,8 @@ 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 RearrangeWaypointsFunction FUNCTION_REARRANGE_WAYPOINTS = null; + public static GenericFunction FUNCTION_REARRANGE_WAYPOINTS = null; + public static GenericFunction FUNCTION_SELECT_SEGMENT = null; public static GenericFunction FUNCTION_SPLIT_SEGMENTS = null; public static GenericFunction FUNCTION_SEW_SEGMENTS = null; public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null; @@ -50,7 +52,8 @@ public abstract class FunctionLibrary public static GenericFunction FUNCTION_DELETE_RANGE = null; public static GenericFunction FUNCTION_CROP_TRACK = null; public static GenericFunction FUNCTION_MARK_IN_RECTANGLE = null; - public static GenericFunction FUNCTION_INTERPOLATE = null; + public static GenericFunction FUNCTION_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_LOOKUP_WIKIPEDIA = null; @@ -90,8 +93,9 @@ public abstract class FunctionLibrary public static GenericFunction FUNCTION_SET_DISK_CACHE = null; public static GenericFunction FUNCTION_SET_PATHS = null; public static GenericFunction FUNCTION_SET_COLOURS = null; - public static GenericFunction FUNCTION_SET_LINE_WIDTH = null; + public static SingleNumericParameterFunction FUNCTION_SET_LINE_WIDTH = null; public static GenericFunction FUNCTION_SET_LANGUAGE = null; + public static SingleNumericParameterFunction FUNCTION_SET_ALTITUDE_TOLERANCE = null; public static GenericFunction FUNCTION_HELP = null; public static GenericFunction FUNCTION_SHOW_KEYS = null; public static GenericFunction FUNCTION_ABOUT = null; @@ -115,6 +119,7 @@ public abstract class FunctionLibrary FUNCTION_SAVECONFIG = new SaveConfig(inApp); FUNCTION_EDIT_WAYPOINT_NAME = new PointNameEditor(inApp); FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp); + FUNCTION_SELECT_SEGMENT = new SelectSegmentFunction(inApp); FUNCTION_SPLIT_SEGMENTS = new SplitSegmentsFunction(inApp); FUNCTION_SEW_SEGMENTS = new SewTrackSegmentsFunction(inApp); FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp); @@ -122,6 +127,7 @@ public abstract class FunctionLibrary FUNCTION_DELETE_RANGE = new DeleteSelectedRangeFunction(inApp); FUNCTION_CROP_TRACK = new CropToSelection(inApp); FUNCTION_MARK_IN_RECTANGLE = new MarkPointsInRectangleFunction(inApp); + FUNCTION_DELETE_BY_DATE = new DeleteByDateFunction(inApp); FUNCTION_INTERPOLATE = new InterpolateFunction(inApp); FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp); FUNCTION_DOWNLOAD_SRTM = new DownloadSrtmFunction(inApp); @@ -164,6 +170,7 @@ public abstract class FunctionLibrary FUNCTION_SET_COLOURS = new SetColours(inApp); FUNCTION_SET_LINE_WIDTH = new SetLineWidth(inApp); FUNCTION_SET_LANGUAGE = new SetLanguage(inApp); + FUNCTION_SET_ALTITUDE_TOLERANCE = new SetAltitudeTolerance(inApp); FUNCTION_HELP = new HelpScreen(inApp); FUNCTION_SHOW_KEYS = new ShowKeysScreen(inApp); FUNCTION_ABOUT = new AboutScreen(inApp); diff --git a/tim/prune/GpsPrune.java b/tim/prune/GpsPrune.java index dd90765..e382170 100644 --- a/tim/prune/GpsPrune.java +++ b/tim/prune/GpsPrune.java @@ -3,6 +3,7 @@ package tim.prune; import java.awt.event.WindowAdapter; import java.awt.BorderLayout; import java.awt.Component; +import java.awt.Image; import java.awt.event.WindowEvent; import java.io.File; import java.io.FileNotFoundException; @@ -35,9 +36,9 @@ import tim.prune.gui.profile.ProfileChart; public class GpsPrune { /** Version number of application, used in about screen and for version check */ - public static final String VERSION_NUMBER = "16.3"; + public static final String VERSION_NUMBER = "17"; /** Build number, just used for about screen */ - public static final String BUILD_NUMBER = "303c"; + public static final String BUILD_NUMBER = "320"; /** Static reference to App object */ private static App APP = null; @@ -228,11 +229,26 @@ public class GpsPrune // Avoid automatically shutting down if window closed frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - // set icon - try { - frame.setIconImage(IconManager.getImageIcon(IconManager.WINDOW_ICON).getImage()); + // set window icons of different resolutions (1.6+) + try + { + ArrayList icons = new ArrayList(); + String[] resolutions = {"_16", "_20", "_32", "_64", "_128"}; + for (String r : resolutions) { + icons.add(IconManager.getImageIcon(IconManager.WINDOW_ICON + r + ".png").getImage()); + } + Class d = java.awt.Window.class; + // This is the same as frame.setIconImages(icons) but is compilable also for java1.5 where this isn't available + d.getDeclaredMethod("setIconImages", new Class[]{java.util.List.class}).invoke(frame, icons); + } + catch (Exception e) + { + // setting a list of icon images didn't work, so try with just one image instead + try { + frame.setIconImage(IconManager.getImageIcon(IconManager.WINDOW_ICON + "_16.png").getImage()); + } + catch (Exception e2) {} } - catch (Exception e) {} // ignore // Set up drag-and-drop handler to accept dropped files frame.setTransferHandler(new FileDropHandler(APP)); diff --git a/tim/prune/UpdateMessageBroker.java b/tim/prune/UpdateMessageBroker.java index c032359..b584ed8 100644 --- a/tim/prune/UpdateMessageBroker.java +++ b/tim/prune/UpdateMessageBroker.java @@ -6,7 +6,7 @@ package tim.prune; */ public abstract class UpdateMessageBroker { - private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 6; + private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 7; /** Array of all subscribers */ private static DataSubscriber[] _subscribers = new DataSubscriber[MAXIMUM_NUMBER_SUBSCRIBERS]; /** Counter of the number of subscribers added so far */ diff --git a/tim/prune/config/Config.java b/tim/prune/config/Config.java index 35f5a31..ad3cc9c 100644 --- a/tim/prune/config/Config.java +++ b/tim/prune/config/Config.java @@ -7,6 +7,8 @@ import java.util.Properties; import tim.prune.data.RecentFileList; import tim.prune.data.UnitSet; import tim.prune.data.UnitSetLibrary; +import tim.prune.gui.colour.ColourerFactory; +import tim.prune.gui.colour.PointColourer; import tim.prune.gui.map.MapSourceLibrary; @@ -22,6 +24,8 @@ public abstract class Config private static Properties _configValues = null; /** Colour scheme object is also part of config */ private static ColourScheme _colourScheme = new ColourScheme(); + /** Point colourer object, if any */ + private static PointColourer _pointColourer = null; /** Recently-used file list */ private static RecentFileList _recentFiles = new RecentFileList(); /** Current unit set */ @@ -73,6 +77,8 @@ public abstract class Config public static final String KEY_EXIFTOOL_PATH = "prune.exiftoolpath"; /** Key for colour scheme */ public static final String KEY_COLOUR_SCHEME = "prune.colourscheme"; + /** Key for point colourer */ + public static final String KEY_POINT_COLOURER = "prune.pointcolourer"; /** Key for line width used for drawing */ public static final String KEY_LINE_WIDTH = "prune.linewidth"; /** Key for kml track colour */ @@ -85,6 +91,10 @@ public abstract class Config public static final String KEY_ESTIMATION_PARAMS = "prune.estimationparams"; /** Key for 3D exaggeration factor */ public static final String KEY_HEIGHT_EXAGGERATION = "prune.heightexaggeration"; + /** Key for terrain grid size */ + public static final String KEY_TERRAIN_GRID_SIZE = "prune.terraingridsize"; + /** Key for altitude tolerance */ + public static final String KEY_ALTITUDE_TOLERANCE = "prune.altitudetolerance"; /** Initialise the default properties */ @@ -145,6 +155,7 @@ public abstract class Config // Save all properties from file _configValues.putAll(props); _colourScheme.loadFromHex(_configValues.getProperty(KEY_COLOUR_SCHEME)); + _pointColourer = ColourerFactory.createColourer(_configValues.getProperty(KEY_POINT_COLOURER)); _recentFiles = new RecentFileList(_configValues.getProperty(KEY_RECENT_FILES)); _unitSet = UnitSetLibrary.getUnitSet(_configValues.getProperty(KEY_UNITSET_KEY)); // Adjust map source index if necessary @@ -176,6 +187,8 @@ public abstract class Config props.put(KEY_AUTOSAVE_SETTINGS, "0"); // autosave false by default props.put(KEY_UNITSET_KEY, "unitset.kilometres"); // metric by default props.put(KEY_HEIGHT_EXAGGERATION, "100"); // 100%, no exaggeration + props.put(KEY_TERRAIN_GRID_SIZE, "50"); + props.put(KEY_ALTITUDE_TOLERANCE, "0"); // 0, all exact as before return props; } @@ -237,6 +250,14 @@ public abstract class Config return _colourScheme; } + /** + * @return the current point colourer, if any + */ + public static PointColourer getPointColourer() + { + return _pointColourer; + } + /** * @return list of recently used files */ @@ -337,6 +358,16 @@ public abstract class Config setConfigString(KEY_COLOUR_SCHEME, _colourScheme.toString()); } + /** + * Update the point colourer from the given colourer + * @param inColourer point colourer object, or null + */ + public static void updatePointColourer(PointColourer inColourer) + { + _pointColourer = inColourer; + setConfigString(KEY_POINT_COLOURER, ColourerFactory.PointColourerToString(_pointColourer)); + } + /** * @return the current unit set */ @@ -344,6 +375,9 @@ public abstract class Config return _unitSet; } + /** + * @param inIndex index of unit set to select + */ public static void selectUnitSet(int inIndex) { _unitSet = UnitSetLibrary.getUnitSet(inIndex); diff --git a/tim/prune/data/AltitudeRange.java b/tim/prune/data/AltitudeRange.java index d53223e..1703631 100644 --- a/tim/prune/data/AltitudeRange.java +++ b/tim/prune/data/AltitudeRange.java @@ -1,5 +1,7 @@ package tim.prune.data; +import tim.prune.config.Config; + /** * Represents a range of altitudes, taking units into account. * Values assumed to be >= 0. @@ -8,14 +10,18 @@ public class AltitudeRange { /** Range of altitudes in metres */ private IntegerRange _range = new IntegerRange(); - /** Empty flag */ - private boolean _empty; + /** Flag for whether previous value exists or not */ + private boolean _gotPreviousValue; /** Previous metric value */ - private int _prevValue; + private int _previousValue; /** Total climb in metres */ - private double _climb; + private int _climb; /** Total descent in metres */ - private double _descent; + private int _descent; + /** Flags for whether minimum or maximum has been found */ + private boolean _gotPreviousMinimum = false, _gotPreviousMaximum = false; + /** Integer values of previous minimum and maximum, if any */ + private int _previousExtreme = 0; /** @@ -31,10 +37,11 @@ public class AltitudeRange public void clear() { _range.clear(); - _climb = 0.0; - _descent = 0.0; - _empty = true; - _prevValue = 0; + _climb = _descent = 0; + _gotPreviousValue = false; + _previousValue = 0; + _gotPreviousMinimum = _gotPreviousMaximum = false; + _previousExtreme = 0; } @@ -44,20 +51,91 @@ public class AltitudeRange */ public void addValue(Altitude inAltitude) { + final int wiggleLimit = Config.getConfigInt(Config.KEY_ALTITUDE_TOLERANCE) / 100; + if (inAltitude != null && inAltitude.isValid()) { int altValue = (int) inAltitude.getMetricValue(); _range.addValue(altValue); // Compare with previous value if any - if (!_empty) + if (_gotPreviousValue) + { + if (altValue != _previousValue) + { + // Got an altitude value which is different from the previous one + final boolean locallyUp = (altValue > _previousValue); + final boolean overallUp = _gotPreviousMinimum && _previousValue > _previousExtreme; + final boolean overallDn = _gotPreviousMaximum && _previousValue < _previousExtreme; + final boolean moreThanWiggle = Math.abs(altValue - _previousValue) > wiggleLimit; + // Do we know whether we're going up or down yet? + if (!_gotPreviousMinimum && !_gotPreviousMaximum) + { + // we don't know whether we're going up or down yet - check limit + if (moreThanWiggle) + { + if (locallyUp) {_gotPreviousMinimum = true;} + else {_gotPreviousMaximum = true;} + _previousExtreme = _previousValue; + _previousValue = altValue; + _gotPreviousValue = true; + } + } + else if (overallUp) + { + if (locallyUp) { + // we're still going up - do nothing + _previousValue = altValue; + } + else if (moreThanWiggle) + { + // we're going up but have dropped over a maximum + // Add the climb from _previousExtreme up to _previousValue + _climb += (_previousValue - _previousExtreme); + _previousExtreme = _previousValue; + _gotPreviousMinimum = false; _gotPreviousMaximum = true; + _previousValue = altValue; + _gotPreviousValue = true; + } + } + else if (overallDn) + { + if (locallyUp) { + if (moreThanWiggle) + { + // we're going down but have climbed up from a minimum + // Add the descent from _previousExtreme down to _previousValue + _descent += (_previousExtreme - _previousValue); + _previousExtreme = _previousValue; + _gotPreviousMinimum = true; _gotPreviousMaximum = false; + _previousValue = altValue; + _gotPreviousValue = true; + } + } + else { + // we're still going down - do nothing + _previousValue = altValue; + _gotPreviousValue = true; + } + } + // TODO: Behaviour when WIGGLE_LIMIT == 0 should be same as before, all differences cumulated + } + } + else { - if (altValue > _prevValue) - _climb += (altValue - _prevValue); - else - _descent += (_prevValue - altValue); + // we haven't got a previous value at all, so it's the start of a new segment + _previousValue = altValue; + _gotPreviousValue = true; } - _prevValue = altValue; - _empty = false; + +// if (!_empty) +// { +// if (altValue > _previousValue) +// _climb += (altValue - _previousValue); +// else +// _descent += (_previousValue - altValue); +// } +// _previousValue = altValue; +// _empty = false; } } @@ -67,9 +145,24 @@ public class AltitudeRange */ public void ignoreValue(Altitude inAltitude) { - // If we set the empty flag to true, that has the same effect as restarting a segment - _empty = true; - addValue(inAltitude); + // Process the previous value, if any, to update climb/descent as that's the end of the previous segment + if (_gotPreviousValue && _gotPreviousMinimum && _previousValue > _previousExtreme) { + _climb += (_previousValue - _previousExtreme); + } + else if (_gotPreviousValue && _gotPreviousMaximum && _previousValue < _previousExtreme) { + _descent += (_previousExtreme - _previousValue); + } + // Eliminate the counting values to start the new segment + _gotPreviousMinimum = _gotPreviousMaximum = false; + _gotPreviousValue = false; + // Now process this value if there is one + if (inAltitude != null && inAltitude.isValid()) + { + final int altValue = (int) inAltitude.getMetricValue(); + _range.addValue(altValue); + _previousValue = altValue; + _gotPreviousValue = true; + } } /** @@ -107,7 +200,12 @@ public class AltitudeRange */ public int getClimb(Unit inUnit) { - return (int) (_climb * inUnit.getMultFactorFromStd()); + // May need to add climb from last segment + int lastSegmentClimb = 0; + if (_gotPreviousValue && _gotPreviousMinimum && _previousValue > _previousExtreme) { + lastSegmentClimb = _previousValue - _previousExtreme; + } + return (int) ((_climb + lastSegmentClimb) * inUnit.getMultFactorFromStd()); } /** @@ -116,7 +214,12 @@ public class AltitudeRange */ public int getDescent(Unit inUnit) { - return (int) (_descent * inUnit.getMultFactorFromStd()); + // May need to add descent from last segment + int lastSegmentDescent = 0; + if (_gotPreviousValue && _gotPreviousMaximum && _previousValue < _previousExtreme) { + lastSegmentDescent = _previousExtreme - _previousValue; + } + return (int) ((_descent + lastSegmentDescent) * inUnit.getMultFactorFromStd()); } /** @@ -124,6 +227,6 @@ public class AltitudeRange */ public double getMetricHeightDiff() { - return _climb - _descent; + return getClimb(UnitSetLibrary.UNITS_METRES) - getDescent(UnitSetLibrary.UNITS_METRES); } } diff --git a/tim/prune/data/Checker.java b/tim/prune/data/Checker.java index 085f50f..9d84fab 100644 --- a/tim/prune/data/Checker.java +++ b/tim/prune/data/Checker.java @@ -44,7 +44,7 @@ public abstract class Checker { int i = inIndex + 1; DataPoint point = null; - while ((point=inTrack.getPoint(i)) != null && !point.getSegmentStart()) { + while ((point=inTrack.getPoint(i)) != null && (point.isWaypoint() || !point.getSegmentStart())) { i++; } return Math.min(i, inTrack.getNumPoints()-1); @@ -60,9 +60,59 @@ public abstract class Checker { int i = inIndex - 1; DataPoint point = null; - while ((point=inTrack.getPoint(i)) != null && !point.getSegmentStart()) { + while ((point=inTrack.getPoint(i)) != null && (point.isWaypoint() || !point.getSegmentStart())) { i--; } - return Math.max(i, 0); + // Have we gone past the beginning of the track? + i = Math.max(i, 0); + // count forwards past the waypoints if necessary + while ((point=inTrack.getPoint(i)) != null && point.isWaypoint()) { + i++; + } + return i; + } + + /** + * Find the index of the last track point in the current segment + * @param inTrack track object + * @param inIndex current index + * @return index of next segment end + */ + public static int getNextSegmentEnd(Track inTrack, int inIndex) + { + // First, go to start of following segment, or the end of the track + int i = getNextSegmentStart(inTrack, inIndex); + // If it's the next segment, subtract one + DataPoint point = inTrack.getPoint(i); + if (point == null || point.getSegmentStart()) + { + i--; + } + // Now we may be on a waypoint, so count back to get the last track point + while ((point=inTrack.getPoint(i)) != null && point.isWaypoint()) { + i--; + } + return Math.min(i, inTrack.getNumPoints()-1); + } + + + /** + * @param inTrack track object + * @return true if there is at least one waypoint with a timestamp + */ + public static boolean haveWaypointsGotTimestamps(Track inTrack) + { + if (inTrack != null) + { + for (int i=0; i= 0) { + return source; + } + } + return null; + } + /** * Clone contents of file info */ diff --git a/tim/prune/data/GradientCalculator.java b/tim/prune/data/GradientCalculator.java new file mode 100644 index 0000000..97a691f --- /dev/null +++ b/tim/prune/data/GradientCalculator.java @@ -0,0 +1,53 @@ +package tim.prune.data; + +/** + * Abstract class to hold static calculation functions + * for gradient (like glide slope) + */ +public abstract class GradientCalculator +{ + /** + * Calculate the gradient value of the track at the specified index + * @param inTrack track object + * @param inIndex index of point to calculate gradient for + * @param inValue object in which to place result of calculation + */ + public static void calculateGradient(Track inTrack, int inIndex, SpeedValue inValue) + { + inValue.setInvalid(); + if (inTrack == null || inIndex < 0 || inValue == null) + { + System.err.println("Cannot calculate gradient for index " + inIndex); + return; + } + + // If no altitude or it's a waypoint then no gradient either + DataPoint point = inTrack.getPoint(inIndex); + if (point == null || !point.hasAltitude() || point.isWaypoint()) { + return; + } + + // If the point has horizontal and vertical speeds already then just use those + if (point.hasHSpeed() && point.hasVSpeed()) { + inValue.setValue(point.getVSpeed().getValueInMetresPerSec() / point.getHSpeed().getValueInMetresPerSec()); + } + else if (!point.getSegmentStart()) + { + // Use the previous track point and the next track point + DataPoint p = inTrack.getPreviousTrackPoint(inIndex-1); + DataPoint q = inTrack.getNextTrackPoint(inIndex+1); + if (p != null && q != null && !q.getSegmentStart() + && p.hasAltitude() && q.hasAltitude()) + { + final double horizRads = DataPoint.calculateRadiansBetween(p, point) + + DataPoint.calculateRadiansBetween(point, q); + final double horizDist = Distance.convertRadiansToDistance(horizRads, UnitSetLibrary.UNITS_METRES); + final double heightDiff = q.getAltitude().getMetricValue() - p.getAltitude().getMetricValue(); + // Get gradient in radians + final double gradient = Math.atan2(heightDiff, horizDist); + inValue.setValue(gradient); + } + } + // otherwise, just leave value as invalid + } +} diff --git a/tim/prune/data/SourceInfo.java b/tim/prune/data/SourceInfo.java index e78d575..b9609f1 100644 --- a/tim/prune/data/SourceInfo.java +++ b/tim/prune/data/SourceInfo.java @@ -122,8 +122,12 @@ public class SourceInfo public int getIndex(DataPoint inPoint) { int idx = -1; - for (int i=0; i<_points.length && (idx < 0); i++) { - if (_points[i] == inPoint) {idx = i;} + for (int i=0; i<_points.length; i++) + { + if (_points[i] == inPoint) { + idx = i; + break; + } } if (idx == -1) {return idx;} // point not found if (_pointIndices == null) {return idx;} // All points loaded diff --git a/tim/prune/data/SpeedCalculator.java b/tim/prune/data/SpeedCalculator.java index f6f429c..e80e214 100644 --- a/tim/prune/data/SpeedCalculator.java +++ b/tim/prune/data/SpeedCalculator.java @@ -16,11 +16,12 @@ public abstract class SpeedCalculator */ public static void calculateSpeed(Track inTrack, int inIndex, SpeedValue inValue) { - if (inTrack == null || inIndex < 0 || inValue == null) { + inValue.setInvalid(); + if (inTrack == null || inIndex < 0 || inValue == null) + { System.err.println("Cannot calculate speed for index " + inIndex); return; } - inValue.setInvalid(); DataPoint point = inTrack.getPoint(inIndex); if (point == null) {return;} diff --git a/tim/prune/data/Timestamp.java b/tim/prune/data/Timestamp.java index 623d804..d175c64 100644 --- a/tim/prune/data/Timestamp.java +++ b/tim/prune/data/Timestamp.java @@ -18,11 +18,13 @@ public class Timestamp private boolean _valid = false; private long _milliseconds = 0L; private String _text = null; - private String _timeText = null; - private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateTimeInstance(); + private static final DateFormat DEFAULT_DATETIME_FORMAT = DateFormat.getDateTimeInstance(); + private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance(); private static final DateFormat DEFAULT_TIME_FORMAT = DateFormat.getTimeInstance(); + private static boolean MillisAddedToTimeFormat = false; private static final DateFormat ISO_8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + private static final DateFormat ISO_8601_FORMAT_WITH_MILLIS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); private static final DateFormat ISO_8601_FORMAT_NOZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); private static DateFormat[] ALL_DATE_FORMATS = null; private static Calendar CALENDAR = null; @@ -38,12 +40,13 @@ public class Timestamp private static long TWENTY_YEARS_IN_SECS = 0L; private static final long GARTRIP_OFFSET = 631065600L; - /** Specifies original timestamp format */ - public static final int FORMAT_ORIGINAL = 0; - /** Specifies locale-dependent timestamp format */ - public static final int FORMAT_LOCALE = 1; - /** Specifies ISO 8601 timestamp format */ - public static final int FORMAT_ISO_8601 = 2; + /** Possible formats for parsing and displaying timestamps */ + public enum Format + { + ORIGINAL, + LOCALE, + ISO8601 + } /** Identifier for the parsing strategy to use */ private enum ParseType @@ -58,13 +61,15 @@ public class Timestamp FIXED_FORMAT4, FIXED_FORMAT5, FIXED_FORMAT6, + FIXED_FORMAT7, GENERAL_STRING } /** Array of parse types to loop through (first one is changed to last successful type) */ private static ParseType[] ALL_PARSE_TYPES = {ParseType.NONE, ParseType.ISO8601_FRACTIONAL, ParseType.LONG, ParseType.FIXED_FORMAT0, ParseType.FIXED_FORMAT1, ParseType.FIXED_FORMAT2, ParseType.FIXED_FORMAT3, - ParseType.FIXED_FORMAT4, ParseType.FIXED_FORMAT5, ParseType.FIXED_FORMAT6, ParseType.GENERAL_STRING}; + ParseType.FIXED_FORMAT4, ParseType.FIXED_FORMAT5, ParseType.FIXED_FORMAT6, ParseType.FIXED_FORMAT7, + ParseType.GENERAL_STRING}; // Static block to initialise offsets static @@ -80,16 +85,21 @@ public class Timestamp TWENTY_YEARS_IN_SECS = (MSECS_SINCE_1970 - MSECS_SINCE_1990) / 1000L; // Set timezone for output ISO_8601_FORMAT.setTimeZone(gmtZone); - DEFAULT_DATE_FORMAT.setTimeZone(gmtZone); + ISO_8601_FORMAT_WITH_MILLIS.setTimeZone(gmtZone); + DEFAULT_DATETIME_FORMAT.setTimeZone(gmtZone); // Date formats ALL_DATE_FORMATS = new DateFormat[] { - DEFAULT_DATE_FORMAT, + DEFAULT_DATETIME_FORMAT, new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy"), new SimpleDateFormat("HH:mm:ss dd MMM yyyy"), new SimpleDateFormat("dd MMM yyyy HH:mm:ss"), + new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss"), new SimpleDateFormat("yyyy MMM dd HH:mm:ss"), ISO_8601_FORMAT, ISO_8601_FORMAT_NOZ }; + for (DateFormat df : ALL_DATE_FORMATS) { + df.setLenient(false); + } } @@ -100,6 +110,7 @@ public class Timestamp public Timestamp(String inString) { _valid = false; + _text = null; if (inString != null && !inString.equals("")) { // Try each of the parse types in turn @@ -109,6 +120,7 @@ public class Timestamp { ALL_PARSE_TYPES[0] = type; _valid = true; + _text = inString; return; } } @@ -167,6 +179,7 @@ public class Timestamp case FIXED_FORMAT4: return parseString(inString, ALL_DATE_FORMATS[4]); case FIXED_FORMAT5: return parseString(inString, ALL_DATE_FORMATS[5]); case FIXED_FORMAT6: return parseString(inString, ALL_DATE_FORMATS[6]); + case FIXED_FORMAT7: return parseString(inString, ALL_DATE_FORMATS[7]); case GENERAL_STRING: if (inString.length() == 19) @@ -201,7 +214,6 @@ public class Timestamp */ private boolean parseString(String inString, DateFormat inDateFormat) { - inDateFormat.setLenient(false); ParsePosition pPos = new ParsePosition(0); Date date = inDateFormat.parse(inString, pPos); if (date != null && inString.length() == pPos.getIndex()) // require use of _all_ the string, not just the beginning @@ -338,6 +350,13 @@ public class Timestamp return _valid; } + /** + * @return true if the timestamp has non-zero milliseconds + */ + public boolean hasMilliseconds() + { + return isValid() && (_milliseconds % 1000L) > 0; + } /** * @param inOther other Timestamp * @return true if this one is at least a second after the other @@ -432,38 +451,61 @@ public class Timestamp */ public String getText() { - return getText(FORMAT_LOCALE); + return getText(Format.LOCALE); } /** * @param inFormat format of timestamp * @return Description of timestamp in required format */ - public String getText(int inFormat) + public String getText(Format inFormat) { if (!_valid) {return "";} - if (inFormat == FORMAT_ISO_8601) { - return format(ISO_8601_FORMAT); - } - if (_text == null) { - _text = format(DEFAULT_DATE_FORMAT); + switch (inFormat) + { + case ORIGINAL: + if (_text != null) {return _text;} + // otherwise fallthrough to default + //$FALL-THROUGH$ + case LOCALE: + return format(DEFAULT_DATETIME_FORMAT); + case ISO8601: + return format(hasMilliseconds() ? ISO_8601_FORMAT_WITH_MILLIS : ISO_8601_FORMAT); } return _text; } + /** + * @return date part of timestamp in locale-specific format + */ + public String getDateText() + { + if (!_valid) return ""; + return format(DEFAULT_DATE_FORMAT); + } + /** * @return Description of time part of timestamp in locale-specific format */ public String getTimeText() { - if (_timeText == null) + if (!_valid) return ""; + // Maybe we should add milliseconds to this format? + if (hasMilliseconds() && !MillisAddedToTimeFormat) { - if (_valid) { - _timeText = format(DEFAULT_TIME_FORMAT); + try + { + SimpleDateFormat sdf = (SimpleDateFormat) DEFAULT_TIME_FORMAT; + String pattern = sdf.toPattern(); + if (pattern.indexOf("ss") > 0 && pattern.indexOf("SS") < 0) + { + sdf.applyPattern(pattern.replaceFirst("s+", "$0.SSS")); + MillisAddedToTimeFormat = true; + } } - else _timeText = ""; + catch (ClassCastException cce) {} } - return _timeText; + return format(DEFAULT_TIME_FORMAT); } /** diff --git a/tim/prune/data/Track.java b/tim/prune/data/Track.java index 45810e2..c56f9f5 100644 --- a/tim/prune/data/Track.java +++ b/tim/prune/data/Track.java @@ -87,6 +87,11 @@ public class Track _dataPoints[pointIndex] = point; pointIndex++; } + else + { + // TODO: Maybe report this somehow? + // System.out.println("point is not valid!"); + } } _numPoints = pointIndex; // Set first track point to be start of segment @@ -363,61 +368,6 @@ public class Track } - /** - * Collect all waypoints to the start or end of the track - * @param inAtStart true to collect at start, false for end - * @return true if successful, false if no change - */ - public boolean collectWaypoints(boolean inAtStart) - { - // Check for mixed data, numbers of waypoints & nons - int numWaypoints = 0, numNonWaypoints = 0; - boolean wayAfterNon = false, nonAfterWay = false; - DataPoint[] waypoints = new DataPoint[_numPoints]; - DataPoint[] nonWaypoints = new DataPoint[_numPoints]; - DataPoint point = null; - for (int i=0; i<_numPoints; i++) - { - point = _dataPoints[i]; - if (point.isWaypoint()) - { - waypoints[numWaypoints] = point; - numWaypoints++; - wayAfterNon |= (numNonWaypoints > 0); - } - else - { - nonWaypoints[numNonWaypoints] = point; - numNonWaypoints++; - nonAfterWay |= (numWaypoints > 0); - } - } - // Exit if the data is already in the specified order - if (numWaypoints == 0 || numNonWaypoints == 0 - || (inAtStart && !wayAfterNon && nonAfterWay) - || (!inAtStart && wayAfterNon && !nonAfterWay)) - { - return false; - } - - // Copy the arrays back into _dataPoints in the specified order - if (inAtStart) - { - System.arraycopy(waypoints, 0, _dataPoints, 0, numWaypoints); - System.arraycopy(nonWaypoints, 0, _dataPoints, numWaypoints, numNonWaypoints); - } - else - { - System.arraycopy(nonWaypoints, 0, _dataPoints, 0, numNonWaypoints); - System.arraycopy(waypoints, 0, _dataPoints, numNonWaypoints, numWaypoints); - } - // needs to be scaled again - _scaled = false; - UpdateMessageBroker.informSubscribers(); - return true; - } - - /** * Interleave all waypoints by each nearest track point * @return true if successful, false if no change diff --git a/tim/prune/function/PhotoComparer.java b/tim/prune/data/sort/PhotoComparer.java similarity index 91% rename from tim/prune/function/PhotoComparer.java rename to tim/prune/data/sort/PhotoComparer.java index b15d57c..5a20a90 100644 --- a/tim/prune/function/PhotoComparer.java +++ b/tim/prune/data/sort/PhotoComparer.java @@ -1,20 +1,18 @@ -package tim.prune.function; +package tim.prune.data.sort; import java.util.Comparator; import tim.prune.data.DataPoint; + /** * Class for comparing photos to sort them by name or timestamp */ public class PhotoComparer implements Comparator { - public enum SortMode { - SORTBY_NAME, SORTBY_TIME - }; - /** Sort mode */ - private SortMode _sortMode = SortMode.SORTBY_NAME; + private SortMode _sortMode; + /** * Constructor @@ -79,7 +77,7 @@ public class PhotoComparer implements Comparator if (!inP2.hasTimestamp()) return -1; if (!inP1.hasTimestamp()) return 1; // Compare the timestamps - long secDiff = inP1.getPhoto().getTimestamp().getSecondsSince(inP2.getPhoto().getTimestamp()); + long secDiff = inP1.getPhoto().getTimestamp().getMillisecondsSince(inP2.getPhoto().getTimestamp()); return (secDiff<0?-1:(secDiff==0?0:1)); } diff --git a/tim/prune/data/sort/SortMode.java b/tim/prune/data/sort/SortMode.java new file mode 100644 index 0000000..9816fee --- /dev/null +++ b/tim/prune/data/sort/SortMode.java @@ -0,0 +1,11 @@ +package tim.prune.data.sort; + +/** + * Enumeration for possible sort modes + */ +public enum SortMode +{ + DONT_SORT, + SORTBY_NAME, + SORTBY_TIME +} diff --git a/tim/prune/data/sort/WaypointComparer.java b/tim/prune/data/sort/WaypointComparer.java new file mode 100644 index 0000000..b1c3e2a --- /dev/null +++ b/tim/prune/data/sort/WaypointComparer.java @@ -0,0 +1,74 @@ +package tim.prune.data.sort; + +import java.util.Comparator; + +import tim.prune.data.DataPoint; + + +/** + * Class for comparing waypoints to sort them by name or timestamp + */ +public class WaypointComparer implements Comparator +{ + /** Sort mode */ + private SortMode _sortMode; + + + /** + * Constructor + * @param inMode sort mode + */ + public WaypointComparer(SortMode inMode) + { + _sortMode = inMode; + } + + /** + * Main compare method + */ + public int compare(DataPoint inP1, DataPoint inP2) + { + if (inP2 == null || !inP2.isWaypoint()) return -1; // all nulls at end + if (inP1 == null || !inP1.isWaypoint()) return 1; + + // Sort by time, if requested + int result = 0; + if (_sortMode == SortMode.SORTBY_TIME) { + result = compareTimes(inP1, inP2); + } + // check names if names requested or if times didn't work + if (result == 0) { + result = inP1.getWaypointName().compareTo(inP2.getWaypointName()); + } + // names and times equal, try longitude + if (result == 0) { + result = inP1.getLongitude().getDouble() > inP2.getLongitude().getDouble() ? 1 : -1; + } + // and latitude + if (result == 0) { + result = inP1.getLatitude().getDouble() > inP2.getLatitude().getDouble() ? 1 : -1; + } + return result; + } + + /** + * Compare the timestamps of the two waypoints + * @param inP1 first point + * @param inP2 second point + * @return compare value (-1,0,1) + */ + private int compareTimes(DataPoint inP1, DataPoint inP2) + { + // Points might not have timestamps + if (inP1.hasTimestamp() && !inP2.hasTimestamp()) return 1; + if (!inP1.hasTimestamp() && inP2.hasTimestamp()) return -1; + if (inP1.hasTimestamp() && inP2.hasTimestamp()) + { + // Compare the timestamps + long secDiff = inP1.getTimestamp().getMillisecondsSince(inP2.getTimestamp()); + return (secDiff<0?-1:(secDiff==0?0:1)); + } + // neither has a timestamp + return 0; + } +} diff --git a/tim/prune/function/ChooseSingleParameter.java b/tim/prune/function/ChooseSingleParameter.java new file mode 100644 index 0000000..5e23d74 --- /dev/null +++ b/tim/prune/function/ChooseSingleParameter.java @@ -0,0 +1,158 @@ +package tim.prune.function; + +import java.awt.BorderLayout; +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 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.gui.WholeNumberField; + +/** + * First step of functions which just require a single numeric + * parameter in order to run + */ +public class ChooseSingleParameter extends GenericFunction +{ + /** Parent function which needs this parameter */ + private SingleNumericParameterFunction _parent = null; + /** dialog */ + private JDialog _dialog = null; + /** label which might need to be changed */ + private JLabel _descLabel = null; + /** entry field */ + private WholeNumberField _numberField = null; + /** ok button */ + private JButton _okButton = null; + + + /** Constructor */ + public ChooseSingleParameter(App inApp, SingleNumericParameterFunction inFunction) + { + super(inApp); + _parent = inFunction; + } + + @Override + public String getNameKey() { + return _parent.getNameKey(); + } + + @Override + public void begin() + { + // Make dialog window + if (_dialog == null) + { + _dialog = new JDialog(_parentFrame, I18nManager.getText(_parent.getNameKey()), true); + _dialog.setLocationRelativeTo(_parentFrame); + _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + _dialog.getContentPane().add(makeDialogComponents()); + _dialog.pack(); + } + // refresh and show the dialog + _descLabel.setText(I18nManager.getText(_parent.getDescriptionKey())); + int param = _parent.getCurrentParamValue(); + if (param > 0) { + _numberField.setValue(param); + } + else { + _numberField.setText(""); + } + _dialog.setVisible(true); + enableOkButton(); + } + + /** + * Create dialog components + * @return Panel containing all gui elements in dialog + */ + private JPanel makeDialogComponents() + { + JPanel dialogPanel = new JPanel(); + dialogPanel.setLayout(new BorderLayout()); + // label + _descLabel = new JLabel(I18nManager.getText(_parent.getDescriptionKey())); + dialogPanel.add(_descLabel, BorderLayout.NORTH); + // Centre panel with number entry field + JPanel centrePanel = new JPanel(); + centrePanel.setLayout(new BorderLayout(8, 8)); + _numberField = new WholeNumberField(4); + centrePanel.add(_numberField, BorderLayout.NORTH); + dialogPanel.add(centrePanel, BorderLayout.CENTER); + + // Listener to enable/disable ok button + _numberField.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + enableOkButton(); + } + }); + _numberField.addKeyListener(new KeyAdapter() { + public void keyReleased(KeyEvent inE) + { + int eCode = inE.getKeyCode(); + if (eCode == KeyEvent.VK_ESCAPE) {_dialog.dispose();} + else if (eCode == KeyEvent.VK_ENTER) {finish();} + super.keyReleased(inE); + } + }); + + // button panel at bottom + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + _okButton = new JButton(I18nManager.getText("button.ok")); + ActionListener okListener = new ActionListener() { + public void actionPerformed(ActionEvent e) + { + finish(); + } + }; + _okButton.addActionListener(okListener); + _okButton.setEnabled(false); + buttonPanel.add(_okButton); + JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + _dialog.dispose(); + } + }); + buttonPanel.add(cancelButton); + dialogPanel.add(buttonPanel, BorderLayout.SOUTH); + dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15)); + return dialogPanel; + } + + + /** + * Enable or disable the OK button as appropriate + */ + private void enableOkButton() + { + _okButton.setEnabled(_numberField.getValue() >= _parent.getMinAllowedValue() + && _numberField.getValue() <= _parent.getMaxAllowedValue()); + } + + /** + * The OK button (or Enter) has been pressed + */ + private void finish() + { + if (_numberField.getValue() >= _parent.getMinAllowedValue() + && _numberField.getValue() <= _parent.getMaxAllowedValue()) + { + _parent.completeFunction(_numberField.getValue()); + _dialog.dispose(); + } + } +} diff --git a/tim/prune/function/GetWikipediaFunction.java b/tim/prune/function/GetWikipediaFunction.java index 4c11bf9..3ffdd06 100644 --- a/tim/prune/function/GetWikipediaFunction.java +++ b/tim/prune/function/GetWikipediaFunction.java @@ -91,7 +91,7 @@ public class GetWikipediaFunction extends GenericDownloaderFunction // Set status label according to error or "none found", leave blank if ok if (_errorMessage == null && _trackListModel.isEmpty()) { - _errorMessage = I18nManager.getText("dialog.gpsies.nonefound"); + _errorMessage = I18nManager.getText("dialog.wikipedia.nonefound"); } _statusLabel.setText(_errorMessage == null ? "" : _errorMessage); } diff --git a/tim/prune/function/InterpolateFunction.java b/tim/prune/function/InterpolateFunction.java index 8482614..221fdaf 100644 --- a/tim/prune/function/InterpolateFunction.java +++ b/tim/prune/function/InterpolateFunction.java @@ -3,7 +3,6 @@ package tim.prune.function; import javax.swing.JOptionPane; import tim.prune.App; -import tim.prune.GenericFunction; import tim.prune.I18nManager; import tim.prune.data.DataPoint; import tim.prune.data.Track; @@ -12,14 +11,14 @@ import tim.prune.undo.UndoInterpolate; /** * Function to interpolate between the points in a range */ -public class InterpolateFunction extends GenericFunction +public class InterpolateFunction extends SingleNumericParameterFunction { /** * Constructor * @param inApp app object */ public InterpolateFunction(App inApp) { - super(inApp); + super(inApp, 1, 1000); } /** @return name key */ @@ -27,10 +26,28 @@ public class InterpolateFunction extends GenericFunction return "function.interpolate"; } + /** @return description key for input parameter */ + public String getDescriptionKey() { + return "dialog.interpolate.parameter.text"; + } + + /** @return current (or default) parameter value */ + public int getCurrentParamValue() { + return 0; + } + /** * Perform the operation */ public void begin() + { + // not needed, we just use the completeFunction method instead + } + + /** + * Complete the function after the input parameter has been chosen + */ + public void completeFunction(int inParam) { // Firstly, work out whether the selected range only contains waypoints or not final int startIndex = _app.getTrackInfo().getSelection().getStart(); @@ -49,24 +66,12 @@ public class InterpolateFunction extends GenericFunction betweenWaypoints = true; } - // Get number of points to add - Object numPointsStr = JOptionPane.showInputDialog(_parentFrame, - I18nManager.getText("dialog.interpolate.parameter.text"), - I18nManager.getText(getNameKey()), - JOptionPane.QUESTION_MESSAGE, null, null, ""); - if (numPointsStr == null) {return;} - int numToAdd = parseNumber(numPointsStr); - if (numToAdd <= 0 || numToAdd > 1000) - { - _app.showErrorMessage(getNameKey(), "error.interpolate.invalidparameter"); - return; - } - if (startIndex < 0 || endIndex < 0 || endIndex <= startIndex) { return; } // construct new point array with the interpolated points + final int numToAdd = inParam; final Track track = _app.getTrackInfo().getTrack(); final int maxToAdd = (endIndex-startIndex) * numToAdd; final int extendedSize = track.getNumPoints() + maxToAdd; @@ -141,24 +146,4 @@ public class InterpolateFunction extends GenericFunction } return false; } - - /** - * Helper method to parse an Object into an integer - * @param inObject object, eg from dialog - * @return int value given - */ - private static int parseNumber(Object inObject) - { - int num = 0; - if (inObject != null) - { - try - { - num = Integer.parseInt(inObject.toString()); - } - catch (NumberFormatException nfe) - {} - } - return num; - } } diff --git a/tim/prune/function/RearrangeFunction.java b/tim/prune/function/RearrangeFunction.java new file mode 100644 index 0000000..01cd1d1 --- /dev/null +++ b/tim/prune/function/RearrangeFunction.java @@ -0,0 +1,207 @@ +package tim.prune.function; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; + +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.data.sort.SortMode; + +/** + * Abstract superclass for the functions which rearrange points, + * such as waypoints or photo points + */ +public abstract class RearrangeFunction extends GenericFunction +{ + /** Function dialog */ + private JDialog _dialog = null; + /** Radio buttons for start/end/nearest */ + private JRadioButton[] _positionRadios = null; + /** Radio buttons for sorting */ + private JRadioButton[] _sortRadios = null; + /** Is the "nearest" option available? */ + private boolean _nearestAvailable = false; + + + /** Enumeration for rearrange commands */ + protected enum Rearrange + { + /** Rearrange all waypoints to start */ + TO_START, + /** Rearrange all waypoints to end */ + TO_END, + /** Rearrange each waypoint to nearest track point */ + TO_NEAREST + } + + + /** + * Constructor + * @param inApp app object + * @param isNearestAvailable true if nearest option is visible + */ + public RearrangeFunction(App inApp, boolean isNearestAvailable) + { + super(inApp); + _nearestAvailable = isNearestAvailable; + } + + /** + * Begin the function by showing the dialog + */ + 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(); + } + // If sorting by time isn't available, then disable radio button + _sortRadios[2].setEnabled(isSortByTimeAllowed()); + // Show dialog + _dialog.setVisible(true); + } + + + /** + * Create dialog components + * @return Panel containing all gui elements in dialog + */ + private JPanel makeDialogComponents() + { + JPanel dialogPanel = new JPanel(); + dialogPanel.setLayout(new BorderLayout()); + JLabel descLabel = new JLabel(I18nManager.getText(getDescriptionKey())); + descLabel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + dialogPanel.add(descLabel, BorderLayout.NORTH); + // Radios for position (start / end / nearest) + _positionRadios = new JRadioButton[3]; + final String[] posNames = {"tostart", "toend", "tonearest"}; + ButtonGroup posGroup = new ButtonGroup(); + JPanel posPanel = new JPanel(); + posPanel.setLayout(new FlowLayout(FlowLayout.CENTER)); + for (int i=0; i 1) { - sortPhotos(photos, _sortRadios[1].isSelected()); - } - // Put the non-photo points and photo points together - DataPoint[] neworder = new DataPoint[numPoints]; - if (_positionRadios[0].isSelected()) { - // photos at front - System.arraycopy(photos, 0, neworder, 0, numPhotos); - System.arraycopy(nonPhotos, 0, neworder, numPhotos, numNonPhotos); - } - else { - // photos at end - System.arraycopy(nonPhotos, 0, neworder, 0, numNonPhotos); - System.arraycopy(photos, 0, neworder, numNonPhotos, numPhotos); + boolean pointsChanged = false; + if (numPhotos > 0) + { + Rearrange rearrangeOption = getRearrangeOption(); + SortMode sortOption = getSortMode(); + // Sort photos if necessary + if (sortOption != SortMode.DONT_SORT && numPhotos > 1) { + sortPhotos(photos, sortOption); + } + // Put the non-photo points and photo points together + DataPoint[] neworder = new DataPoint[numPoints]; + if (rearrangeOption == Rearrange.TO_START) + { + // photos at front + System.arraycopy(photos, 0, neworder, 0, numPhotos); + System.arraycopy(nonPhotos, 0, neworder, numPhotos, numNonPhotos); + } + else + { + // photos at end + System.arraycopy(nonPhotos, 0, neworder, 0, numNonPhotos); + System.arraycopy(photos, 0, neworder, numNonPhotos, numPhotos); + } + + // Give track the new point order + pointsChanged = track.replaceContents(neworder); } - // Give track the new point order - if (track.replaceContents(neworder)) + // did anything change? + if (pointsChanged) { _app.getTrackInfo().getSelection().clearAll(); _app.completeFunction(undo, I18nManager.getText("confirm.rearrangephotos")); @@ -187,12 +110,12 @@ public class RearrangePhotosFunction extends GenericFunction /** * Sort the given photo list either by filename or by time * @param inPhotos array of DataPoint objects to sort - * @param inSortByFile true to sort by filename, false to sort by timestamp + * @param inSortOrder sort order * @return sorted array */ - private static void sortPhotos(DataPoint[] inPhotos, boolean inSortByFile) + private static void sortPhotos(DataPoint[] inPhotos, SortMode inSortMode) { - PhotoComparer comparer = new PhotoComparer(inSortByFile ? PhotoComparer.SortMode.SORTBY_NAME : PhotoComparer.SortMode.SORTBY_TIME); + PhotoComparer comparer = new PhotoComparer(inSortMode); Arrays.sort(inPhotos, comparer); } } diff --git a/tim/prune/function/RearrangeWaypointsFunction.java b/tim/prune/function/RearrangeWaypointsFunction.java index e04aea0..17e9370 100644 --- a/tim/prune/function/RearrangeWaypointsFunction.java +++ b/tim/prune/function/RearrangeWaypointsFunction.java @@ -1,61 +1,69 @@ package tim.prune.function; +import java.util.Arrays; + import javax.swing.JOptionPane; import tim.prune.App; -import tim.prune.GenericFunction; import tim.prune.I18nManager; +import tim.prune.data.Checker; +import tim.prune.data.DataPoint; import tim.prune.data.Track; +import tim.prune.data.sort.SortMode; +import tim.prune.data.sort.WaypointComparer; import tim.prune.undo.UndoRearrangeWaypoints; /** * Class to provide the function for rearranging waypoints */ -public class RearrangeWaypointsFunction extends GenericFunction +public class RearrangeWaypointsFunction extends RearrangeFunction { - /** Enumeration for rearrange commands */ - public enum Rearrange - { - /** Rearrange all waypoints to start */ - TO_START, - /** Rearrange all waypoints to end */ - TO_END, - /** Rearrange each waypoint to nearest track point */ - TO_NEAREST - } - /** * Constructor * @param inApp app object */ public RearrangeWaypointsFunction(App inApp) { - super(inApp); + super(inApp, true); + } + + /** Get the name key */ + public String getNameKey() { + return "function.rearrangewaypoints"; } - /** Begin the rearrange (not needed) */ - public void begin() { + /** Get whether sorting by time is allowed or not */ + protected boolean isSortByTimeAllowed() { + return Checker.haveWaypointsGotTimestamps(_app.getTrackInfo().getTrack()); } - /** Get the name key (not needed) */ - public String getNameKey() { - return null; + /** Get the description key */ + public String getDescriptionKey() { + return "dialog.rearrangewaypoints.desc"; + } + + /** Sort by name key */ + protected String getSortNameKey() { + return "sortbyname"; } /** - * Rearrange the waypoints into track order - * @param inFunction nearest point, all to end or all to start + * Perform the rearrange and sort according to the radio buttons */ - public void rearrangeWaypoints(Rearrange inFunction) + protected void finish() { Track track = _app.getTrackInfo().getTrack(); + // Figure out what is required from the radio buttons + Rearrange rearrangeOption = getRearrangeOption(); + SortMode sortOption = getSortMode(); + UndoRearrangeWaypoints undo = new UndoRearrangeWaypoints(track); boolean success = false; - if (inFunction == Rearrange.TO_START || inFunction == Rearrange.TO_END) + if (rearrangeOption == Rearrange.TO_START || rearrangeOption == Rearrange.TO_END) { // Collect the waypoints to the start or end of the track - success = track.collectWaypoints(inFunction == Rearrange.TO_START); + success = collectWaypoints(rearrangeOption, sortOption); } else { @@ -74,4 +82,108 @@ public class RearrangeWaypointsFunction extends GenericFunction } } + + /** + * Do the collection and sorting of the waypoints + * @param inRearrangeOption beginning or end + * @param inSortOption optional sort criterion + * @return true on success + */ + private boolean collectWaypoints(Rearrange inRearrangeOption, SortMode inSortOption) + { + // Check for mixed data, numbers of waypoints & nons + int numWaypoints = 0, numNonWaypoints = 0; + boolean wayAfterNon = false, nonAfterWay = false; + Track track = _app.getTrackInfo().getTrack(); + final int numPoints = track.getNumPoints(); + DataPoint[] waypoints = new DataPoint[numPoints]; + DataPoint[] nonWaypoints = new DataPoint[numPoints]; + DataPoint point = null; + for (int i=0; i 0); + } + else + { + nonWaypoints[numNonWaypoints] = point; + numNonWaypoints++; + nonAfterWay |= (numWaypoints > 0); + } + } + + // 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 + || (wpsToStart && !wayAfterNon && nonAfterWay && !doSort) + || (!wpsToStart && wayAfterNon && !nonAfterWay && !doSort) + || inRearrangeOption == Rearrange.TO_NEAREST) + { + return false; + } + // Note: it could still be that the rearrange and sort has no effect, but we don't know yet + // Make a copy of the waypoints array first so we can compare it with after the sort + DataPoint[] origWaypoints = new DataPoint[numPoints]; + System.arraycopy(waypoints, 0, origWaypoints, 0, numPoints); + + if (doSort && numWaypoints > 1) + { + // Sort the waypoints array + WaypointComparer comparer = new WaypointComparer(inSortOption); + Arrays.sort(waypoints, comparer); + final boolean sortDidNothing = areArraysSame(origWaypoints, waypoints); + if (sortDidNothing && (numNonWaypoints == 0 + || (wpsToStart && !wayAfterNon && nonAfterWay) + || (!wpsToStart && wayAfterNon && !nonAfterWay))) + { + return false; + } + } + + // Copy the arrays into an array in the specified order + DataPoint[] neworder = new DataPoint[numPoints]; + if (wpsToStart) + { + System.arraycopy(waypoints, 0, neworder, 0, numWaypoints); + System.arraycopy(nonWaypoints, 0, neworder, numWaypoints, numNonWaypoints); + } + else + { + System.arraycopy(nonWaypoints, 0, neworder, 0, numNonWaypoints); + System.arraycopy(waypoints, 0, neworder, numNonWaypoints, numWaypoints); + } + // Give track the new point order + return track.replaceContents(neworder); + } + + /** + * Compare two arrays of DataPoints and see if they're identical or not + * @param inOriginal original array of points + * @param inSorted array of points after sorting + * @return true if the two arrays have the same points in the same order + */ + private static boolean areArraysSame(DataPoint[] inOriginal, DataPoint[] inSorted) + { + if (inOriginal == null && inSorted == null) return true; // both null + if (inOriginal == null || inSorted == null) return false; // only one of them null + if (inOriginal.length != inSorted.length) return false; + // Loop over all points + for (int i=0; i startIndex) { + _app.getTrackInfo().getSelection().selectRange(startIndex, endIndex); + } + } + } + + /** @return name key */ + public String getNameKey() { + return "function.selectsegment"; + } +} diff --git a/tim/prune/function/SetAltitudeTolerance.java b/tim/prune/function/SetAltitudeTolerance.java new file mode 100644 index 0000000..c51a56a --- /dev/null +++ b/tim/prune/function/SetAltitudeTolerance.java @@ -0,0 +1,75 @@ +package tim.prune.function; + +import tim.prune.App; +import tim.prune.DataSubscriber; +import tim.prune.UpdateMessageBroker; +import tim.prune.config.Config; +import tim.prune.data.Unit; + +/** + * Function to set the tolerance for the altitude range calculations + */ +public class SetAltitudeTolerance extends SingleNumericParameterFunction +{ + + /** + * Constructor + * @param inApp App object + */ + public SetAltitudeTolerance(App inApp) { + super(inApp, 0, 100); + } + + /** @return name key */ + public String getNameKey() { + return "function.setaltitudetolerance"; + } + + /** + * @return description key + */ + public String getDescriptionKey() + { + // Two different keys for feet and metres + final boolean isMetres = Config.getUnitSet().getAltitudeUnit().isStandard(); + return "dialog.setaltitudetolerance.text." + (isMetres ? "metres" : "feet"); + } + + /** + * @return the current value to display + */ + public int getCurrentParamValue() + { + int configVal = Config.getConfigInt(Config.KEY_ALTITUDE_TOLERANCE); + // Convert this to feet if necessary + Unit altUnit = Config.getUnitSet().getAltitudeUnit(); + if (altUnit.isStandard()) { + return configVal / 100; + } + return (int) (configVal * altUnit.getMultFactorFromStd() / 100.0); + } + + /** + * Run function + */ + public void begin() + { + // Not required, because this function is started from a ChooseSingleParameter function + // and goes directly to the completeFunction method. + } + + /** + * Complete the function using the given tolerance parameter + */ + public void completeFunction(int inTolerance) + { + // Convert back from feet into metres again + Unit altUnit = Config.getUnitSet().getAltitudeUnit(); + int configVal = inTolerance * 100; + if (!altUnit.isStandard()) { + configVal = (int) (inTolerance * 100.0 / altUnit.getMultFactorFromStd()); + } + Config.setConfigInt(Config.KEY_ALTITUDE_TOLERANCE, configVal); + UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); + } +} diff --git a/tim/prune/function/SetColours.java b/tim/prune/function/SetColours.java index 1329d05..33b4357 100644 --- a/tim/prune/function/SetColours.java +++ b/tim/prune/function/SetColours.java @@ -1,15 +1,11 @@ package tim.prune.function; import java.awt.BorderLayout; -import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; @@ -24,8 +20,11 @@ import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.ColourScheme; import tim.prune.config.Config; -import tim.prune.gui.ColourChooser; -import tim.prune.gui.ColourPatch; +import tim.prune.gui.colour.ColourChooser; +import tim.prune.gui.colour.ColourPatch; +import tim.prune.gui.colour.ColourerSelectorPanel; +import tim.prune.gui.colour.PatchListener; +import tim.prune.gui.colour.PointColourer; /** * Class to show the popup window for setting the colours @@ -36,6 +35,8 @@ public class SetColours extends GenericFunction private JButton _okButton = null; /** Array of 8 colour patches */ private ColourPatch[] _patches = null; + /** colourer selection panel */ + private ColourerSelectorPanel _colourerSelector = null; /** Single colour chooser */ private ColourChooser _colourChooser = null; @@ -49,25 +50,6 @@ public class SetColours extends GenericFunction ColourScheme.IDX_TEXT, ColourScheme.IDX_LINES }; - /** - * Inner class to react to patch clicks - */ - class PatchListener extends MouseAdapter - { - /** Associated patch */ - private ColourPatch _patch = null; - /** Constructor */ - public PatchListener(ColourPatch inPatch) { - _patch = inPatch; - } - /** React to mouse clicks */ - public void mouseClicked(MouseEvent e) - { - _colourChooser.showDialog(_patch.getBackground()); - Color colour = _colourChooser.getChosenColour(); - if (colour != null) _patch.setColour(colour); - } - } /** * Constructor @@ -94,11 +76,17 @@ public class SetColours extends GenericFunction JPanel mainPanel = new JPanel(); mainPanel.setLayout(new BorderLayout(0, 10)); - JLabel intro = new JLabel(I18nManager.getText("dialog.setcolours.intro")); - intro.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0)); - mainPanel.add(intro, BorderLayout.NORTH); + JLabel introLabel = new JLabel(I18nManager.getText("dialog.setcolours.intro")); + introLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0)); + mainPanel.add(introLabel, BorderLayout.NORTH); + + // Panel in centre, to hold both the patch panel and the colourer panel (and maybe introLabel too?) JPanel centralPanel = new JPanel(); - centralPanel.setLayout(new GridLayout()); + centralPanel.setLayout(new BoxLayout(centralPanel, BoxLayout.Y_AXIS)); + + // Make panel for 8 colour patches + JPanel patchPanel = new JPanel(); + patchPanel.setLayout(new GridLayout()); _patches = new ColourPatch[8]; ColourScheme scheme = Config.getColourScheme(); @@ -111,7 +99,7 @@ public class SetColours extends GenericFunction // Top label and patch colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2]))); patch = new ColourPatch(scheme.getColour(INDICES[i*2])); - patch.addMouseListener(new PatchListener(patch)); + patch.addMouseListener(new PatchListener(patch, _colourChooser)); colPanel.add(patch); _patches[i*2] = patch; // separator @@ -119,14 +107,23 @@ public class SetColours extends GenericFunction // Bottom label and patch colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2+1]))); patch = new ColourPatch(scheme.getColour(INDICES[i*2+1])); - patch.addMouseListener(new PatchListener(patch)); + patch.addMouseListener(new PatchListener(patch, _colourChooser)); colPanel.add(patch); _patches[i*2+1] = patch; // Add column to panel colPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); - centralPanel.add(colPanel); + patchPanel.add(colPanel); } + patchPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + centralPanel.add(patchPanel); + centralPanel.add(Box.createVerticalStrut(15)); + + // now the colourer selector + _colourerSelector = new ColourerSelectorPanel(_colourChooser); + _colourerSelector.setAlignmentX(Component.LEFT_ALIGNMENT); + centralPanel.add(_colourerSelector); + // add the central panel to the main one mainPanel.add(centralPanel, BorderLayout.CENTER); // Buttons at the bottom @@ -176,15 +173,17 @@ public class SetColours extends GenericFunction { _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey())); _dialog.setLocationRelativeTo(_parentFrame); + _colourChooser = new ColourChooser(_dialog); _dialog.getContentPane().add(makeContents()); _dialog.pack(); - _colourChooser = new ColourChooser(_dialog); } // Reset colours to current ones ColourScheme scheme = Config.getColourScheme(); for (int i=0; i<8; i++) { _patches[i].setColour(scheme.getColour(INDICES[i])); } + PointColourer colourer = Config.getPointColourer(); + _colourerSelector.init(colourer, scheme.getColour(ColourScheme.IDX_POINT)); _dialog.setVisible(true); _okButton.requestFocus(); } @@ -200,6 +199,9 @@ public class SetColours extends GenericFunction scheme.setColour(INDICES[i], _patches[i].getBackground()); } Config.updateColourScheme(); + PointColourer colourer = _colourerSelector.getSelectedColourer(); + Config.updatePointColourer(colourer); + _app.updatePointColourer(); UpdateMessageBroker.informSubscribers(); } } diff --git a/tim/prune/function/SetLineWidth.java b/tim/prune/function/SetLineWidth.java index 5ac64ae..feb49c2 100644 --- a/tim/prune/function/SetLineWidth.java +++ b/tim/prune/function/SetLineWidth.java @@ -1,15 +1,14 @@ package tim.prune.function; -import javax.swing.JOptionPane; - import tim.prune.App; import tim.prune.DataSubscriber; -import tim.prune.GenericFunction; -import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.Config; -public class SetLineWidth extends GenericFunction +/** + * Function to set the width with which lines are drawn + */ +public class SetLineWidth extends SingleNumericParameterFunction { /** @@ -17,7 +16,7 @@ public class SetLineWidth extends GenericFunction * @param inApp App object */ public SetLineWidth(App inApp) { - super(inApp); + super(inApp, 1, 4); } /** @return name key */ @@ -25,32 +24,35 @@ public class SetLineWidth extends GenericFunction return "function.setlinewidth"; } + /** @return description key */ + public String getDescriptionKey() { + return "dialog.setlinewidth.text"; + } + + /** @return the current value to display */ + public int getCurrentParamValue() { + return Config.getConfigInt(Config.KEY_LINE_WIDTH); + } /** * Run function */ public void begin() { - int currLineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH); - if (currLineWidth < 1 || currLineWidth > 4) { - currLineWidth = 2; - } - Object lineWidthStr = JOptionPane.showInputDialog(_app.getFrame(), - I18nManager.getText("dialog.setlinewidth.text"), - I18nManager.getText(getNameKey()), - JOptionPane.QUESTION_MESSAGE, null, null, "" + currLineWidth); - if (lineWidthStr != null) + // Not required, because this function is started from a ChooseSingleParameter function + // and goes directly to the completeFunction method. + } + + /** + * Complete the function using the given line width parameter + */ + public void completeFunction(int inLineWidth) + { + final int currLineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH); + if (inLineWidth >= 1 && inLineWidth <= 4 && inLineWidth != currLineWidth) { - int lineWidth = 2; - try { - lineWidth = Integer.parseInt(lineWidthStr.toString()); - if (lineWidth >= 1 && lineWidth <= 4 && lineWidth != currLineWidth) - { - Config.setConfigInt(Config.KEY_LINE_WIDTH, lineWidth); - UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); - } - } - catch (NumberFormatException nfe) {}; + Config.setConfigInt(Config.KEY_LINE_WIDTH, inLineWidth); + UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); } } } diff --git a/tim/prune/function/ShowThreeDFunction.java b/tim/prune/function/ShowThreeDFunction.java index 40c4e5a..c2e285b 100644 --- a/tim/prune/function/ShowThreeDFunction.java +++ b/tim/prune/function/ShowThreeDFunction.java @@ -156,8 +156,12 @@ public class ShowThreeDFunction extends GenericFunction */ private void finish() { - // Store exaggeration factor in config + // Store exaggeration factor and grid size in config Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_exaggField.getValue() * 100)); + int terrainGridSize = _terrainPanel.getGridSize(); + if (terrainGridSize < 20) {terrainGridSize = 20;} + Config.setConfigInt(Config.KEY_TERRAIN_GRID_SIZE, terrainGridSize); + ThreeDWindow window = WindowFactory.getWindow(_parentFrame); if (window != null) { diff --git a/tim/prune/function/SingleNumericParameterFunction.java b/tim/prune/function/SingleNumericParameterFunction.java new file mode 100644 index 0000000..3f6062b --- /dev/null +++ b/tim/prune/function/SingleNumericParameterFunction.java @@ -0,0 +1,36 @@ +package tim.prune.function; + +import tim.prune.App; +import tim.prune.GenericFunction; + +/** + * Abstract superclass of Functions which just take a + * single numeric parameter + */ +public abstract class SingleNumericParameterFunction extends GenericFunction +{ + /** Minimum and maximum allowed values */ + protected int _minAllowedValue, _maxAllowedValue; + + /** Constructor */ + public SingleNumericParameterFunction(App inApp, int inMinValue, int inMaxValue) + { + super(inApp); + _minAllowedValue = inMinValue; + _maxAllowedValue = inMaxValue; + } + + /** Get the current value for display in the dialog */ + public abstract int getCurrentParamValue(); + + /** Get the key for the description label */ + public abstract String getDescriptionKey(); + + /** Callback to trigger the rest of the function once the parameter has been chosen */ + public abstract void completeFunction(int inParam); + + /** @return minimum allowed value */ + public int getMinAllowedValue() {return _minAllowedValue;} + /** @return maximum allowed value */ + public int getMaxAllowedValue() {return _maxAllowedValue;} +} diff --git a/tim/prune/function/charts/Charter.java b/tim/prune/function/charts/Charter.java index 842084d..ef3cc95 100644 --- a/tim/prune/function/charts/Charter.java +++ b/tim/prune/function/charts/Charter.java @@ -284,6 +284,9 @@ public class Charter extends GenericFunction + getSvgValue(_svgHeightField, DEFAULT_SVG_HEIGHT) + "\n"); writer.write("set out '" + svgFile.getAbsolutePath() + "'\n"); } + else { + // For screen output, gnuplot should use the default terminal (windows or x11 or wxt or something) + } if (numCharts > 1) { writer.write("set multiplot layout " + numCharts + ",1\n"); } @@ -374,7 +377,7 @@ public class Charter extends GenericFunction break; } // Make a temporary data file for the output (one per subchart) - File tempFile = File.createTempFile("prunedata", null); + File tempFile = File.createTempFile("gpsprunedata", null); tempFile.deleteOnExit(); // write out values for x and y to temporary file FileWriter tempFileWriter = null; diff --git a/tim/prune/function/compress/CompressTrackFunction.java b/tim/prune/function/compress/CompressTrackFunction.java index ce0203c..8e89360 100644 --- a/tim/prune/function/compress/CompressTrackFunction.java +++ b/tim/prune/function/compress/CompressTrackFunction.java @@ -15,7 +15,6 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import tim.prune.App; -import tim.prune.GenericFunction; import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.data.DataPoint; @@ -24,15 +23,13 @@ import tim.prune.data.Track; /** * Class to provide the function for track compression */ -public class CompressTrackFunction extends GenericFunction +public class CompressTrackFunction extends MarkAndDeleteFunction { private Track _track = null; private JDialog _dialog = null; private JButton _okButton = null; private CompressionAlgorithm[] _algorithms = null; private SummaryLabel _summaryLabel = null; - /** flag to remember whether the automatic deletion has been set to always */ - private boolean _automaticallyDelete = false; /** @@ -187,23 +184,7 @@ public class CompressTrackFunction extends GenericFunction // Show confirmation dialog with OK button (not status bar message) if (numMarked > 0) { - // Allow calling of delete function with one click - final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"), - I18nManager.getText("button.always")}; - int answer = _automaticallyDelete ? JOptionPane.YES_OPTION : - JOptionPane.showOptionDialog(_parentFrame, - I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked), - I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]); - if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option - if (_automaticallyDelete || answer == JOptionPane.YES_OPTION) - { - new Thread(new Runnable() { - public void run() { - _app.finishCompressTrack(); - } - }).start(); - } + optionallyDeleteMarkedPoints(numMarked); } else { diff --git a/tim/prune/function/compress/MarkAndDeleteFunction.java b/tim/prune/function/compress/MarkAndDeleteFunction.java new file mode 100644 index 0000000..2189ecb --- /dev/null +++ b/tim/prune/function/compress/MarkAndDeleteFunction.java @@ -0,0 +1,51 @@ +package tim.prune.function.compress; + +import javax.swing.JOptionPane; + +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; + +/** + * Superclass of those functions which mark points for deletion + * (and optionally delete them automatically) + */ +public abstract class MarkAndDeleteFunction extends GenericFunction +{ + /** flag to remember whether the automatic deletion has been set to always */ + private boolean _automaticallyDelete = false; + + + /** + * Constructor + * @param inApp App object + */ + public MarkAndDeleteFunction(App inApp) + { + super(inApp); + } + + /** + * optionally delete the marked points + */ + protected void optionallyDeleteMarkedPoints(int inNumMarked) + { + // Allow calling of delete function with one click + final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"), + I18nManager.getText("button.always")}; + int answer = _automaticallyDelete ? JOptionPane.YES_OPTION : + JOptionPane.showOptionDialog(_parentFrame, + I18nManager.getTextWithNumber("dialog.compress.confirm", inNumMarked), + I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]); + if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option + if (_automaticallyDelete || answer == JOptionPane.YES_OPTION) + { + new Thread(new Runnable() { + public void run() { + _app.finishCompressTrack(); + } + }).start(); + } + } +} diff --git a/tim/prune/function/compress/MarkPointsInRectangleFunction.java b/tim/prune/function/compress/MarkPointsInRectangleFunction.java index f6ffe20..2ea0f8c 100644 --- a/tim/prune/function/compress/MarkPointsInRectangleFunction.java +++ b/tim/prune/function/compress/MarkPointsInRectangleFunction.java @@ -1,24 +1,18 @@ package tim.prune.function.compress; -import javax.swing.JOptionPane; - import tim.prune.App; -import tim.prune.GenericFunction; -import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.data.DataPoint; /** * Function to mark all the points in the selected rectangle */ -public class MarkPointsInRectangleFunction extends GenericFunction +public class MarkPointsInRectangleFunction extends MarkAndDeleteFunction { /** Minimum and maximum latitude values of rectangle */ private double _minLat = 0.0, _maxLat = 0.0; /** Minimum and maximum longitude values of rectangle */ private double _minLon = 0.0, _maxLon = 0.0; - /** flag to remember whether the automatic deletion has been set to always */ - private boolean _automaticallyDelete = false; /** @@ -86,7 +80,7 @@ public class MarkPointsInRectangleFunction extends GenericFunction final double pointLat = point.getLatitude().getDouble(); final boolean insideRect = (pointLon >= _minLon && pointLon <= _maxLon && pointLat >= _minLat && pointLat <= _maxLat); - // If so, then mark it + // Mark it accordingly (also resetting points outside the rect to false) point.setMarkedForDeletion(insideRect); if (insideRect) { numMarked++; @@ -98,23 +92,7 @@ public class MarkPointsInRectangleFunction extends GenericFunction // Confirm message showing how many marked if (numMarked > 0) { - // Allow calling of delete function with one click - final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"), - I18nManager.getText("button.always")}; - int answer = _automaticallyDelete ? JOptionPane.YES_OPTION : - JOptionPane.showOptionDialog(_parentFrame, - I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked), - I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]); - if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option - if (_automaticallyDelete || answer == JOptionPane.YES_OPTION) - { - new Thread(new Runnable() { - public void run() { - _app.finishCompressTrack(); - } - }).start(); - } + optionallyDeleteMarkedPoints(numMarked); } } } diff --git a/tim/prune/function/deletebydate/DateInfo.java b/tim/prune/function/deletebydate/DateInfo.java new file mode 100644 index 0000000..0c2cedb --- /dev/null +++ b/tim/prune/function/deletebydate/DateInfo.java @@ -0,0 +1,112 @@ +package tim.prune.function.deletebydate; + +import java.text.DateFormat; +import java.util.Date; + +/** + * Class to hold the information about a date, + * including how many points correspond to the date + * and whether it has been selected for deletion or not + */ +public class DateInfo implements Comparable +{ + /** Date, or null for no date - used for earlier/later comparison */ + private Date _date = null; + /** String representation of date, for equality comparison */ + private String _dateString = null; + /** Number of points with this date */ + private int _numPoints = 0; + /** Flag for deletion or retention */ + private boolean _toDelete = false; + + // Doesn't really matter what format is used here, as long as dates are different + private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance(); + + /** + * Constructor + * @param inDate date object from timestamp + */ + public DateInfo(Date inDate) + { + _date = inDate; + if (_date == null) { + _dateString = ""; + } + else { + _dateString = DEFAULT_DATE_FORMAT.format(_date); + } + _numPoints = 0; + _toDelete = false; + } + + /** + * @return true if this info is for dateless points (points without timestamp) + */ + public boolean isDateless() { + return (_date == null); + } + + /** + * @return date object, or null + */ + public Date getDate() { + return _date; + } + + /** + * Compare with a given Date object to see if they represent the same date + * @param inDate date to compare + * @return true if they're the same date + */ + public boolean isSameDate(Date inDate) + { + if (inDate == null) { + return (_date == null); + } + else if (_dateString == null) { + return false; + } + String otherDateString = DEFAULT_DATE_FORMAT.format(inDate); + return _dateString.equals(otherDateString); + } + + /** + * Increment the point count + */ + public void incrementCount() { + _numPoints++; + } + + /** + * @return point count + */ + public int getPointCount() { + return _numPoints; + } + + /** + * @param inFlag true to delete, false to keep + */ + public void setDeleteFlag(boolean inFlag) { + _toDelete = inFlag; + } + + /** + * @return true to delete, false to keep + */ + public boolean getDeleteFlag() { + return _toDelete; + } + + /** + * Compare with another DateInfo object for sorting + */ + public int compareTo(DateInfo inOther) + { + // Dateless goes first + if (_date == null || _dateString == null) {return -1;} + if (inOther._date == null || inOther._dateString == null) {return 1;} + // Just compare dates + return _date.compareTo(inOther._date); + } +} diff --git a/tim/prune/function/deletebydate/DateInfoList.java b/tim/prune/function/deletebydate/DateInfoList.java new file mode 100644 index 0000000..7628667 --- /dev/null +++ b/tim/prune/function/deletebydate/DateInfoList.java @@ -0,0 +1,121 @@ +package tim.prune.function.deletebydate; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * List of date info objects for use by the table model + */ +public class DateInfoList +{ + /** list of info about points according to date */ + private List _infoList = new ArrayList(); + /** previously used dateinfo object to reduce list searching */ + private DateInfo _previousInfo = null; + /** true if the list has been sorted, false otherwise */ + private boolean _hasBeenSorted = false; + + + /** + * Add a point to the corresponding dateinfo + * @param inDate date of current point, or null if no timestamp + */ + public void addPoint(Date inDate) + { + if (_previousInfo != null && _previousInfo.isSameDate(inDate)) + { + // found it + _previousInfo.incrementCount(); + } + else + { + // loop through list, seeing if date already present + boolean foundDate = false; + for (DateInfo info : _infoList) + { + if (info.isSameDate(inDate)) + { + info.incrementCount(); + _previousInfo = info; + foundDate = true; + break; + } + } + // create new info if necessary + if (!foundDate) + { + _previousInfo = new DateInfo(inDate); + _previousInfo.incrementCount(); + _infoList.add(_previousInfo); + _hasBeenSorted = false; + } + } + } + + /** + * Clear the whole list + */ + public void clearAll() + { + _infoList.clear(); + _previousInfo = null; + _hasBeenSorted = true; + } + + /** + * not used, can be removed + * @return true if any points without dates were found + */ + public boolean hasDatelessPoints() + { + if (_infoList.isEmpty()) {return false;} + sort(); + DateInfo firstInfo = _infoList.get(0); + return (firstInfo != null && firstInfo.isDateless() && firstInfo.getPointCount() > 0); + } + + /** + * @return number of entries in the list, including dateless points + */ + public int getNumEntries() + { + return _infoList.size(); + } + + /** + * @return the total number of points found, which should match the track size + */ + public int getTotalNumPoints() + { + int total = 0; + for (DateInfo info : _infoList) { + total += info.getPointCount(); + } + return total; + } + + /** + * Sort the info list by ascending timestamps + */ + private void sort() + { + if (!_hasBeenSorted) + { + Collections.sort(_infoList); + _hasBeenSorted = true; + } + } + + /** + * Get the DateInfo object at the given index + * @param inIndex index in (sorted) list + * @return corresponding object (may throw exception if out of range) + */ + public DateInfo getDateInfo(int inIndex) + { + sort(); + return _infoList.get(inIndex); + } +} diff --git a/tim/prune/function/deletebydate/DeleteByDateFunction.java b/tim/prune/function/deletebydate/DeleteByDateFunction.java new file mode 100644 index 0000000..3085aa3 --- /dev/null +++ b/tim/prune/function/deletebydate/DeleteByDateFunction.java @@ -0,0 +1,215 @@ +package tim.prune.function.deletebydate; + +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.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.Date; + +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.JTable; + +import tim.prune.App; +import tim.prune.DataSubscriber; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.data.DataPoint; +import tim.prune.function.compress.MarkAndDeleteFunction; + +/** + * Function to select a date or dates, + * and delete the corresponding points + */ +public class DeleteByDateFunction extends MarkAndDeleteFunction +{ + /** dialog for selecting dates */ + private JDialog _dialog = null; + /** Ok button */ + private JButton _okButton = null; + /** date info list */ + private DateInfoList _infoList = new DateInfoList(); + + + /** + * Constructor + * @param inApp App object + */ + public DeleteByDateFunction(App inApp) + { + super(inApp); + } + + @Override + public String getNameKey() { + return "function.deletebydate"; + } + + @Override + public void begin() + { + // Make a list of which dates are present in the track + _infoList.clearAll(); + final int numPoints = _app.getTrackInfo().getTrack().getNumPoints(); + for (int i=0; i 0) { + optionallyDeleteMarkedPoints(numMarked); + } + else { + // Do nothing //System.out.println("Nothing selected to delete!"); + // delete flags might have been reset, so refresh display + UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); + } + _dialog.dispose(); + } +} diff --git a/tim/prune/function/deletebydate/DeletionTableModel.java b/tim/prune/function/deletebydate/DeletionTableModel.java new file mode 100644 index 0000000..202e01e --- /dev/null +++ b/tim/prune/function/deletebydate/DeletionTableModel.java @@ -0,0 +1,140 @@ +package tim.prune.function.deletebydate; + +import java.text.DateFormat; +import javax.swing.table.AbstractTableModel; +import tim.prune.I18nManager; + +/** + * Table model for selecting which dates to delete + */ +public class DeletionTableModel extends AbstractTableModel +{ + /** info list, one for each row of table */ + private DateInfoList _infoList = null; + + /** Formatter, determining how dates appear in the table */ + private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance(); + /** Column heading for date */ + private static final String COLUMN_HEADING_DATE = I18nManager.getText("fieldname.date"); + /** Column heading for number of points */ + private static final String COLUMN_HEADING_NUMPOINTS = I18nManager.getText("details.track.points"); + /** Column heading for keep */ + private static final String COLUMN_HEADING_KEEP = I18nManager.getText("dialog.deletebydate.column.keep"); + /** Column heading for delete */ + private static final String COLUMN_HEADING_DELETE = I18nManager.getText("dialog.deletebydate.column.delete"); + + + /** + * Constructor + * @param inList date info list from function + */ + public DeletionTableModel(DateInfoList inList) + { + _infoList = inList; + } + + /** + * @return column count + */ + public int getColumnCount() + { + return 4; // always fixed (date, numpoints, keep, delete) + } + + /** + * @return row count + */ + public int getRowCount() + { + if (_infoList == null) {return 0;} // shouldn't happen + return _infoList.getNumEntries(); + } + + /** + * Get the name of the column + * @param inColNum column number + * @return column name + */ + public String getColumnName(int inColNum) + { + if (inColNum == 0) return COLUMN_HEADING_DATE; + else if (inColNum == 1) return COLUMN_HEADING_NUMPOINTS; + else if (inColNum == 2) return COLUMN_HEADING_KEEP; + else if (inColNum == 3) return COLUMN_HEADING_DELETE; + return "unknown column!"; + } + + /** + * Get the class of objects in the given column + * @see javax.swing.table.AbstractTableModel#getColumnClass(int) + */ + public Class getColumnClass(int inColumnIndex) + { + if (inColumnIndex == 1) {return Integer.class;} + if (inColumnIndex > 1) {return Boolean.class;} + return super.getColumnClass(inColumnIndex); + } + + /** + * Get whether the given cell is editable + * @see javax.swing.table.AbstractTableModel#isCellEditable(int, int) + */ + public boolean isCellEditable(int inRowIndex, int inColumnIndex) + { + return (inColumnIndex > 1); + } + + /** + * Set the value at the given table cell + * @see javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object, int, int) + */ + public void setValueAt(Object inValue, int inRowIndex, int inColumnIndex) + { + // can only edit the keep and delete columns + final boolean isKeep = (inColumnIndex == 2); + final boolean isDelete = (inColumnIndex == 3); + // ignore all events for other columns + if (isKeep || isDelete) + { + try { + boolean setFlag = ((Boolean) inValue).booleanValue(); + if (setFlag) + { + _infoList.getDateInfo(inRowIndex).setDeleteFlag(isDelete); + // make sure the other cell (keep or delete) on the same row is updated too + fireTableCellUpdated(inRowIndex, 5 - inColumnIndex); + } + } + catch (ClassCastException cce) {} + } + } + + /** + * @return cell contents at the given row, column inded + */ + public Object getValueAt(int inRowIndex, int inColIndex) + { + try { + DateInfo info = _infoList.getDateInfo(inRowIndex); + if (info != null) + { + switch (inColIndex) + { + case 0: // date + if (info.isDateless()) { + return I18nManager.getText("dialog.deletebydate.nodate"); + } + return DEFAULT_DATE_FORMAT.format(info.getDate()); + case 1: // number of points + return info.getPointCount(); + case 2: // keep + return !info.getDeleteFlag(); + case 3: // delete + return info.getDeleteFlag(); + } + } + } + catch (IndexOutOfBoundsException obe) {} // ignore, fallthrough + return null; + } +} diff --git a/tim/prune/function/distance/DistanceTableModel.java b/tim/prune/function/distance/DistanceTableModel.java index faa059e..2c1d83d 100644 --- a/tim/prune/function/distance/DistanceTableModel.java +++ b/tim/prune/function/distance/DistanceTableModel.java @@ -17,6 +17,8 @@ public class DistanceTableModel extends GenericTableModel private static final String _toColLabel = I18nManager.getText("dialog.distances.column.to"); /** Column heading (depends on metric/imperial settings) */ private String _distanceLabel = null; + /** Previous distance units */ + private Unit _previousDistUnit = null; /** * @return column count @@ -69,6 +71,9 @@ public class DistanceTableModel extends GenericTableModel Unit distUnit = Config.getUnitSet().getDistanceUnit(); _distanceLabel = I18nManager.getText("fieldname.distance") + " (" + I18nManager.getText(distUnit.getShortnameKey()) + ")"; + final boolean distUnitsChanged = (distUnit != _previousDistUnit); + _previousDistUnit = distUnit; + // Initialize array of distances int numRows = getRowCount(); if (_distances == null || _distances.length != numRows) { @@ -84,7 +89,12 @@ public class DistanceTableModel extends GenericTableModel _distances[i] = Distance.convertRadiansToDistance(rads); } } - // Let table know that it has to refresh data (and might as well refresh column headings too) - fireTableStructureChanged(); + // Let table know that it has to refresh data, and maybe the whole table too + if (distUnitsChanged) { + fireTableStructureChanged(); + } + else { + fireTableDataChanged(); + } } } diff --git a/tim/prune/gui/DetailsDisplay.java b/tim/prune/gui/DetailsDisplay.java index f66d99b..fb868e8 100644 --- a/tim/prune/gui/DetailsDisplay.java +++ b/tim/prune/gui/DetailsDisplay.java @@ -32,6 +32,7 @@ import tim.prune.data.DataPoint; import tim.prune.data.Field; import tim.prune.data.Photo; import tim.prune.data.Selection; +import tim.prune.data.SourceInfo; import tim.prune.data.SpeedCalculator; import tim.prune.data.SpeedValue; import tim.prune.data.TrackInfo; @@ -49,10 +50,11 @@ public class DetailsDisplay extends GenericDisplay private JLabel _indexLabel = null; private JLabel _latLabel = null, _longLabel = null; private JLabel _altLabel = null; - private JLabel _timeLabel = null; + private JLabel _ptDateLabel = null, _ptTimeLabel = null; private JLabel _descLabel = null; private JLabel _speedLabel = null, _vSpeedLabel = null; private JLabel _nameLabel = null, _typeLabel = null; + private JLabel _filenameLabel = null; // Range details private JLabel _rangeLabel = null; @@ -90,12 +92,14 @@ public class DetailsDisplay extends GenericDisplay private static final String LABEL_POINT_LATITUDE = I18nManager.getText("fieldname.latitude") + ": "; private static final String LABEL_POINT_LONGITUDE = I18nManager.getText("fieldname.longitude") + ": "; private static final String LABEL_POINT_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": "; - private static final String LABEL_POINT_TIMESTAMP = I18nManager.getText("fieldname.timestamp") + ": "; + private static final String LABEL_POINT_DATE = I18nManager.getText("fieldname.date") + ": "; + private static final String LABEL_POINT_TIME = I18nManager.getText("fieldname.timestamp") + ": "; private static final String LABEL_POINT_WAYPOINTNAME = I18nManager.getText("fieldname.waypointname") + ": "; private static final String LABEL_POINT_WAYPOINTTYPE = I18nManager.getText("fieldname.waypointtype") + ": "; private static final String LABEL_POINT_DESCRIPTION = I18nManager.getText("fieldname.description") + ": "; private static final String LABEL_POINT_SPEED = I18nManager.getText("fieldname.speed") + ": "; private static final String LABEL_POINT_VERTSPEED = I18nManager.getText("fieldname.verticalspeed") + ": "; + private static final String LABEL_POINT_FILENAME = I18nManager.getText("details.track.file") + ": "; private static final String LABEL_RANGE_SELECTED = I18nManager.getText("details.range.selected") + ": "; private static final String LABEL_RANGE_DURATION = I18nManager.getText("fieldname.duration") + ": "; private static final String LABEL_RANGE_DISTANCE = I18nManager.getText("fieldname.distance") + ": "; @@ -131,9 +135,12 @@ public class DetailsDisplay extends GenericDisplay pointDetailsPanel.add(_longLabel); _altLabel = new JLabel(""); pointDetailsPanel.add(_altLabel); - _timeLabel = new JLabel(""); - _timeLabel.setMinimumSize(new Dimension(120, 10)); - pointDetailsPanel.add(_timeLabel); + _ptDateLabel = new JLabel(""); + _ptDateLabel.setMinimumSize(new Dimension(120, 10)); + pointDetailsPanel.add(_ptDateLabel); + _ptTimeLabel = new JLabel(""); + _ptTimeLabel.setMinimumSize(new Dimension(120, 10)); + pointDetailsPanel.add(_ptTimeLabel); _descLabel = new JLabel(""); pointDetailsPanel.add(_descLabel); _speedLabel = new JLabel(""); @@ -144,6 +151,8 @@ public class DetailsDisplay extends GenericDisplay pointDetailsPanel.add(_nameLabel); _typeLabel = new JLabel(""); pointDetailsPanel.add(_typeLabel); + _filenameLabel = new JLabel(""); + pointDetailsPanel.add(_filenameLabel); pointDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); // range details panel @@ -296,12 +305,14 @@ public class DetailsDisplay extends GenericDisplay _latLabel.setText(""); _longLabel.setText(""); _altLabel.setText(""); - _timeLabel.setText(""); + _ptDateLabel.setText(""); + _ptTimeLabel.setText(""); _descLabel.setText(""); _nameLabel.setText(""); _typeLabel.setText(""); _speedLabel.setText(""); _vSpeedLabel.setText(""); + _filenameLabel.setText(""); } else { @@ -316,12 +327,12 @@ public class DetailsDisplay extends GenericDisplay I18nManager.getText(altUnit.getShortnameKey())) : ""); if (currentPoint.hasTimestamp()) { - _timeLabel.setText(LABEL_POINT_TIMESTAMP + currentPoint.getTimestamp().getText()); - _timeLabel.setToolTipText(currentPoint.getTimestamp().getText()); + _ptDateLabel.setText(LABEL_POINT_DATE + currentPoint.getTimestamp().getDateText()); + _ptTimeLabel.setText(LABEL_POINT_TIME + currentPoint.getTimestamp().getTimeText()); } else { - _timeLabel.setText(""); - _timeLabel.setToolTipText(""); + _ptDateLabel.setText(""); + _ptTimeLabel.setText(""); } // Maybe the point has a description? String pointDesc = currentPoint.getFieldValue(Field.DESCRIPTION); @@ -377,6 +388,25 @@ public class DetailsDisplay extends GenericDisplay _typeLabel.setText(LABEL_POINT_WAYPOINTTYPE + type); } else _typeLabel.setText(""); + + // File to which point belongs + final int numFiles = _trackInfo.getFileInfo().getNumFiles(); + String filename = null; + if (numFiles > 1) + { + final SourceInfo info = _trackInfo.getFileInfo().getSourceForPoint(currentPoint); + if (info != null) { + filename = info.getName(); + } + } + if (filename != null) { + _filenameLabel.setText(LABEL_POINT_FILENAME + filename); + _filenameLabel.setToolTipText(filename); + } + else { + _filenameLabel.setText(""); + _filenameLabel.setToolTipText(""); + } } // Update range details @@ -447,7 +477,7 @@ public class DetailsDisplay extends GenericDisplay String shortPath = shortenPath(fullPath); _photoPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath); _photoPathLabel.setToolTipText(currentPhoto.getFullPath()); - _photoTimestampLabel.setText(currentPhoto.hasTimestamp()?(LABEL_POINT_TIMESTAMP + currentPhoto.getTimestamp().getText()):""); + _photoTimestampLabel.setText(currentPhoto.hasTimestamp()?(LABEL_POINT_TIME + currentPhoto.getTimestamp().getText()):""); _photoConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": " + (currentPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED ? I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes"))); @@ -483,7 +513,7 @@ public class DetailsDisplay extends GenericDisplay String shortPath = shortenPath(fullPath); _audioPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath); _audioPathLabel.setToolTipText(fullPath == null ? "" : fullPath); - _audioTimestampLabel.setText(currentAudio.hasTimestamp()?(LABEL_POINT_TIMESTAMP + currentAudio.getTimestamp().getText()):""); + _audioTimestampLabel.setText(currentAudio.hasTimestamp()?(LABEL_POINT_TIME + currentAudio.getTimestamp().getText()):""); int audioLength = currentAudio.getLengthInSeconds(); _audioLengthLabel.setText(audioLength < 0?"":LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(audioLength)); _audioConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": " @@ -532,14 +562,23 @@ public class DetailsDisplay extends GenericDisplay { final int DECIMAL_PLACES = 7; if (inCoord == null) return ""; + String result = inCoord; final int dotPos = Math.max(inCoord.lastIndexOf('.'), inCoord.lastIndexOf(',')); - if (dotPos >= 0) { + if (dotPos >= 0) + { final int chopPos = dotPos + DECIMAL_PLACES; - if (chopPos < (inCoord.length()-1)) { - return inCoord.substring(0, chopPos); + 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 inCoord; + return result; } /** diff --git a/tim/prune/gui/IconManager.java b/tim/prune/gui/IconManager.java index 799e90f..eff4714 100644 --- a/tim/prune/gui/IconManager.java +++ b/tim/prune/gui/IconManager.java @@ -10,7 +10,7 @@ public abstract class IconManager { /** Icon for window */ - public static final String WINDOW_ICON = "window_icon.png"; + public static final String WINDOW_ICON = "window_icon"; /** Icon for scalebar button on main map display */ public static final String SCALEBAR_BUTTON = "scalebar.gif"; diff --git a/tim/prune/gui/MenuManager.java b/tim/prune/gui/MenuManager.java index d2be8ad..741425d 100644 --- a/tim/prune/gui/MenuManager.java +++ b/tim/prune/gui/MenuManager.java @@ -21,6 +21,7 @@ import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.Config; import tim.prune.data.AudioClip; +import tim.prune.data.DataPoint; import tim.prune.data.Field; import tim.prune.data.Photo; import tim.prune.data.RecentFile; @@ -28,7 +29,7 @@ import tim.prune.data.RecentFileList; import tim.prune.data.Selection; import tim.prune.data.Track; import tim.prune.data.TrackInfo; -import tim.prune.function.RearrangeWaypointsFunction.Rearrange; +import tim.prune.function.ChooseSingleParameter; import tim.prune.function.browser.UrlGenerator; /** @@ -60,10 +61,12 @@ public class MenuManager implements DataSubscriber private JMenuItem _compressItem = null; private JMenuItem _markRectangleItem = null; private JMenuItem _deleteMarkedPointsItem = null; + private JMenuItem _deleteByDateItem = null; private JMenuItem _interpolateItem = null; private JMenuItem _averageItem = null; private JMenuItem _selectAllItem = null; private JMenuItem _selectNoneItem = null; + private JMenuItem _selectSegmentItem = null; private JMenuItem _selectStartItem = null; private JMenuItem _selectEndItem = null; private JMenuItem _findWaypointItem = null; @@ -72,7 +75,7 @@ public class MenuManager implements DataSubscriber private JMenuItem _addTimeOffsetItem = null; private JMenuItem _addAltitudeOffsetItem = null; private JMenuItem _mergeSegmentsItem = null; - private JMenu _rearrangeMenu = null; + private JMenuItem _rearrangeWaypointsItem = null; private JMenuItem _splitSegmentsItem = null; private JMenuItem _sewSegmentsItem = null; private JMenuItem _cutAndMoveItem = null; @@ -309,35 +312,12 @@ public class MenuManager implements DataSubscriber }); _deleteMarkedPointsItem.setEnabled(false); trackMenu.add(_deleteMarkedPointsItem); + _deleteByDateItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_BY_DATE, false); + trackMenu.add(_deleteByDateItem); trackMenu.addSeparator(); // Rearrange waypoints - _rearrangeMenu = new JMenu(I18nManager.getText("menu.track.rearrange")); - _rearrangeMenu.setEnabled(false); - JMenuItem rearrangeStartItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.start")); - rearrangeStartItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_START); - } - }); - rearrangeStartItem.setEnabled(true); - _rearrangeMenu.add(rearrangeStartItem); - JMenuItem rearrangeEndItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.end")); - rearrangeEndItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_END); - } - }); - rearrangeEndItem.setEnabled(true); - _rearrangeMenu.add(rearrangeEndItem); - JMenuItem rearrangeNearestItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.nearest")); - rearrangeNearestItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_NEAREST); - } - }); - rearrangeNearestItem.setEnabled(true); - _rearrangeMenu.add(rearrangeNearestItem); - trackMenu.add(_rearrangeMenu); + _rearrangeWaypointsItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS, false); + trackMenu.add(_rearrangeWaypointsItem); // Split track segments _splitSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SPLIT_SEGMENTS, false); trackMenu.add(_splitSegmentsItem); @@ -369,6 +349,8 @@ public class MenuManager implements DataSubscriber } }); rangeMenu.add(_selectNoneItem); + _selectSegmentItem = makeMenuItem(FunctionLibrary.FUNCTION_SELECT_SEGMENT); + rangeMenu.add(_selectSegmentItem); rangeMenu.addSeparator(); _selectStartItem = new JMenuItem(I18nManager.getText("menu.range.start")); _selectStartItem.setEnabled(false); @@ -416,7 +398,7 @@ public class MenuManager implements DataSubscriber _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES, false); rangeMenu.add(_deleteFieldValuesItem); rangeMenu.addSeparator(); - _interpolateItem = makeMenuItem(FunctionLibrary.FUNCTION_INTERPOLATE, false); + _interpolateItem = makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_INTERPOLATE), false); rangeMenu.add(_interpolateItem); _averageItem = new JMenuItem(I18nManager.getText("menu.range.average")); _averageItem.addActionListener(new ActionListener() { @@ -657,9 +639,11 @@ public class MenuManager implements DataSubscriber // Set colours settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS)); // Set line width used for drawing - settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LINE_WIDTH)); + settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_LINE_WIDTH))); // Set language settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE)); + // Set altitude tolerance + settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_ALTITUDE_TOLERANCE))); settingsMenu.addSeparator(); // Save configuration settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SAVECONFIG)); @@ -877,7 +861,7 @@ public class MenuManager implements DataSubscriber _compressItem.setEnabled(hasData); _markRectangleItem.setEnabled(hasData); _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints()); - _rearrangeMenu.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints()); + _rearrangeWaypointsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints()); _splitSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3); _sewSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3); _selectAllItem.setEnabled(hasData); @@ -895,6 +879,8 @@ public class MenuManager implements DataSubscriber _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)); // is undo available? boolean hasUndo = !_app.getUndoStack().isEmpty(); @@ -902,7 +888,8 @@ public class MenuManager implements DataSubscriber _undoButton.setEnabled(hasUndo); _clearUndoItem.setEnabled(hasUndo); // is there a current point? - boolean hasPoint = (hasData && _selection.getCurrentPointIndex() >= 0); + DataPoint currPoint = _app.getTrackInfo().getCurrentPoint(); + boolean hasPoint = (currPoint != null); _editPointItem.setEnabled(hasPoint); _editPointButton.setEnabled(hasPoint); _editWaypointNameItem.setEnabled(hasPoint); @@ -913,6 +900,8 @@ public class MenuManager implements DataSubscriber _selectEndItem.setEnabled(hasPoint); _selectEndButton.setEnabled(hasPoint); _duplicatePointItem.setEnabled(hasPoint); + // is it a waypoint? + _selectSegmentItem.setEnabled(hasPoint && !currPoint.isWaypoint()); // are there any photos? boolean anyPhotos = _app.getTrackInfo().getPhotoList().getNumPhotos() > 0; _saveExifItem.setEnabled(anyPhotos && _app.getTrackInfo().getPhotoList().hasMediaWithFile()); @@ -939,7 +928,7 @@ public class MenuManager implements DataSubscriber _selectNoAudioItem.setEnabled(hasAudio); _removeAudioItem.setEnabled(hasAudio); _connectAudioItem.setEnabled(hasAudio && hasPoint && currentAudio.getDataPoint() == null); - _disconnectAudioItem.setEnabled(hasAudio && _app.getTrackInfo().getCurrentAudio().getDataPoint() != null); + _disconnectAudioItem.setEnabled(hasAudio && currentAudio.getDataPoint() != null); _correlateAudiosItem.setEnabled(anyAudios && hasData); // is there a current range? boolean hasRange = (hasData && _selection.hasRangeSelected()); @@ -983,8 +972,9 @@ public class MenuManager implements DataSubscriber for (int i=0; i maxAltitude || !altFound) {maxAltitude = altValue;} + altFound = true; + } + } + + if ((maxAltitude - minAltitude) < 1.0) + { + // not enough altitude range, set all to null + init(0); + } + else + { + // initialise the array to the right size + init(numPoints); + // loop over track points to calculate colours + for (int i=0; i 0 + && _colourer != null) + { + _colourer.calculateColours(_app.getTrackInfo()); + } + } + + /** Don't care about status */ + public void actionCompleted(String inMessage) {} +} diff --git a/tim/prune/gui/colour/ColourerFactory.java b/tim/prune/gui/colour/ColourerFactory.java new file mode 100644 index 0000000..767456e --- /dev/null +++ b/tim/prune/gui/colour/ColourerFactory.java @@ -0,0 +1,205 @@ +package tim.prune.gui.colour; + +import java.awt.Color; + +import tim.prune.config.ColourUtils; + +/** + * Factory for the creation of PointColourer objects + */ +public abstract class ColourerFactory +{ + /** Enumeration of colourer types */ + public enum ColourerId + { + NONE, + BY_FILE, + BY_SEGMENT, + BY_ALTITUDE, + BY_SPEED, + BY_VSPEED, + BY_GRADIENT, + BY_DATE + } + + /** + * Does the specified colourer need a field for maximum number of colours? + * @param inId id of colourer + * @return true if max colours required, false otherwise + */ + public static boolean isMaxColoursRequired(ColourerId inId) + { + switch (inId) + { + case NONE: return false; + case BY_FILE: return FileColourer.isMaxColoursRequired(); + case BY_SEGMENT: return SegmentColourer.isMaxColoursRequired(); + case BY_ALTITUDE: return AltitudeColourer.isMaxColoursRequired(); + case BY_SPEED: return SpeedColourer.isMaxColoursRequired(); + case BY_VSPEED: return VertSpeedColourer.isMaxColoursRequired(); + case BY_GRADIENT: return GradientColourer.isMaxColoursRequired(); + case BY_DATE: return DateColourer.isMaxColoursRequired(); + } + return false; + } + + /** + * Does the specified colourer need fields for start and end colours? + * @param inId id of colourer + * @return true if colours required, false otherwise + */ + public static boolean areColoursRequired(ColourerId inId) + { + // all of them except NONE need start and end colours + return inId != ColourerId.NONE; + } + + /** + * @param inDesc Single character used as a code (in Config string) + * @return associated ColourerId + */ + private static ColourerId getColourerId(char inDesc) + { + switch (inDesc) + { + case 'f': return ColourerId.BY_FILE; + case 's': return ColourerId.BY_SEGMENT; + case 'a': return ColourerId.BY_ALTITUDE; + case 'p': return ColourerId.BY_SPEED; + case 'v': return ColourerId.BY_VSPEED; + case 'g': return ColourerId.BY_GRADIENT; + case 'd': return ColourerId.BY_DATE; + } + return ColourerId.NONE; + } + + /** + * Create a new PointColourer object given the parameters + * @param inId id of colourer to create + * @param inStartColour start colour + * @param inEndColour end colour + * @param inMaxColours maximum number of colours + * @return PointColourer object, or null + */ + public static PointColourer createColourer(ColourerId inId, Color inStartColour, Color inEndColour, String inMaxColours) + { + try + { + switch (inId) + { + case NONE: return null; + case BY_FILE: return new FileColourer(inStartColour, inEndColour, Integer.parseInt(inMaxColours)); + case BY_SEGMENT: return new SegmentColourer(inStartColour, inEndColour, Integer.parseInt(inMaxColours)); + case BY_ALTITUDE: return new AltitudeColourer(inStartColour, inEndColour); + case BY_SPEED: return new SpeedColourer(inStartColour, inEndColour); + case BY_VSPEED: return new VertSpeedColourer(inStartColour, inEndColour); + case BY_GRADIENT: return new GradientColourer(inStartColour, inEndColour); + case BY_DATE: return new DateColourer(inStartColour, inEndColour, Integer.parseInt(inMaxColours)); + } + } + catch (NumberFormatException nfe) {} // drop out to return null + return null; + } + + /** + * Create a PointColourer object from the given description + * @param inString string from config + * @return PointColourer object, or null if string was invalid + */ + public static PointColourer createColourer(String inString) + { + try + { + String[] comps = inString.split(";"); + if (comps.length == 4) + { + ColourerId colourerType = getColourerId(comps[0].charAt(0)); + Color startColour = ColourUtils.colourFromHex(comps[1]); + Color endColour = ColourUtils.colourFromHex(comps[2]); + String maxColours = comps[3]; + return createColourer(colourerType, startColour, endColour, maxColours); + } + } + catch (NullPointerException npe) {} + catch (NumberFormatException nfe) {} + return null; + } + + /** + * Convert the given PointColourer object into a string for the config + * @param inColourer PointColourer object + * @return string describing object (for later re-creation) or null + */ + public static String PointColourerToString(PointColourer inColourer) + { + if (inColourer != null) + { + final String startColour = ColourUtils.makeHexCode(inColourer.getStartColour()); + final String endColour = ColourUtils.makeHexCode(inColourer.getEndColour()); + final int maxColours = inColourer.getMaxColours(); + if (inColourer instanceof FileColourer) { + return "f;" + startColour + ";" + endColour + ";" + maxColours; + } + else if (inColourer instanceof SegmentColourer) { + return "s;" + startColour + ";" + endColour + ";" + maxColours; + } + else if (inColourer instanceof AltitudeColourer) { + return "a;" + startColour + ";" + endColour + ";0"; + } + else if (inColourer instanceof SpeedColourer) { + return "p;" + startColour + ";" + endColour + ";0"; + } + else if (inColourer instanceof VertSpeedColourer) { + return "v;" + startColour + ";" + endColour + ";0"; + } + else if (inColourer instanceof GradientColourer) { + return "g;" + startColour + ";" + endColour + ";0"; + } + else if (inColourer instanceof DateColourer) { + return "d;" + startColour + ";" + endColour + ";" + maxColours; + } + } + return null; + } + + /** + * Get the colourer-specific end of the description key for translation + * @param inId id of colourer + * @return end of description key for combobox text + */ + public static String getDescriptionKey(ColourerId inId) + { + switch (inId) + { + case NONE: return "none"; + case BY_FILE: return "byfile"; + case BY_SEGMENT: return "bysegment"; + case BY_ALTITUDE: return "byaltitude"; + case BY_SPEED: return "byspeed"; + case BY_VSPEED: return "byvertspeed"; + case BY_GRADIENT: return "bygradient"; + case BY_DATE: return "bydate"; + } + return null; + } + + /** + * Get the id of the given colourer, according to its class + * @param inColourer point colourer object, or null + * @return id, for example for selection in dropdown + */ + public static ColourerId getId(PointColourer inColourer) + { + if (inColourer != null) + { + if (inColourer instanceof FileColourer) {return ColourerId.BY_FILE;} + if (inColourer instanceof SegmentColourer) {return ColourerId.BY_SEGMENT;} + if (inColourer instanceof AltitudeColourer) {return ColourerId.BY_ALTITUDE;} + if (inColourer instanceof SpeedColourer) {return ColourerId.BY_SPEED;} + if (inColourer instanceof VertSpeedColourer) {return ColourerId.BY_VSPEED;} + if (inColourer instanceof GradientColourer) {return ColourerId.BY_GRADIENT;} + if (inColourer instanceof DateColourer) {return ColourerId.BY_DATE;} + } + return ColourerId.NONE; + } +} diff --git a/tim/prune/gui/colour/ColourerSelectorPanel.java b/tim/prune/gui/colour/ColourerSelectorPanel.java new file mode 100644 index 0000000..85754a5 --- /dev/null +++ b/tim/prune/gui/colour/ColourerSelectorPanel.java @@ -0,0 +1,230 @@ +package tim.prune.gui.colour; + +import java.awt.Color; +import java.awt.Component; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EtchedBorder; + +import tim.prune.I18nManager; +import tim.prune.gui.GuiGridLayout; +import tim.prune.gui.colour.ColourerFactory.ColourerId; + +/** + * Class to provide a gui panel for selecting a colourer + * including which colourer, the start/end colours and + * optionally the max number of colours + */ +public class ColourerSelectorPanel extends JPanel +{ + /** Combo box for selecting the type of colourer */ + private JComboBox _typeCombo = null; + /** Array of type ids as stored in combo box */ + private ColourerId[] _typeIds = null; + /** Panel object holding the colour patches */ + private JPanel _patchPanel = null; + /** Array of colour patches for start and end */ + private ColourPatch[] _startEndPatches = null; + /** Panel holding the max colours selection */ + JPanel _maxColoursPanel = null; + private JComboBox _maxColoursCombo = null; + + /** Array of label keys for the 2 patches */ + private static final String[] LABEL_KEYS = {"start", "end"}; + + + /** + * Constructor + * @param inColourChooser colour chooser to use (needs reference to parent dialog) + */ + public ColourerSelectorPanel(ColourChooser inColourChooser) + { + _typeIds = new ColourerId[] {ColourerId.NONE, ColourerId.BY_FILE, + ColourerId.BY_SEGMENT, ColourerId.BY_DATE, ColourerId.BY_ALTITUDE, + ColourerId.BY_SPEED, ColourerId.BY_VSPEED, ColourerId.BY_GRADIENT}; + makeGuiComponents(inColourChooser); + } + + + /** + * Create all the gui components and lay them out in the panel + * @param inColourChooser colour chooser to use + */ + private void makeGuiComponents(ColourChooser inColourChooser) + { + // Etched border and vertical layout + setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4)) + ); + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + // Label at the top + JLabel introLabel = new JLabel(I18nManager.getText("dialog.colourer.intro")); + introLabel.setAlignmentX(Component.LEFT_ALIGNMENT); + add(introLabel); + + // Combo box for selecting which colourer to use + JPanel typePanel = new JPanel(); + GuiGridLayout grid = new GuiGridLayout(typePanel); + final String keyPrefix = "dialog.colourer.type."; + String[] colourerTypes = new String[_typeIds.length]; + for (int i=0; i(colourerTypes); + _typeCombo.setAlignmentX(Component.LEFT_ALIGNMENT); + _typeCombo.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + onColourerTypeChanged(); + } + }); + // Add to the panel + grid.add(new JLabel(I18nManager.getText("dialog.colourer.type"))); + grid.add(_typeCombo); + typePanel.setAlignmentX(Component.LEFT_ALIGNMENT); + add(typePanel); + + // Make panel for colour patches + _patchPanel = new JPanel(); + _patchPanel.setLayout(new GridLayout()); + _startEndPatches = new ColourPatch[2]; + + // Blank column + JPanel blankColumn = new JPanel(); + ColourPatch blankPatch = new ColourPatch(Color.BLACK); + blankPatch.setVisible(false); + blankColumn.add(blankPatch); + _patchPanel.add(blankColumn); + + // Loop over two columns of patches + for (int i=0; i<2; i++) + { + JPanel colPanel = new JPanel(); + colPanel.setLayout(new BoxLayout(colPanel, BoxLayout.Y_AXIS)); + // Top label and patch + colPanel.add(new JLabel(I18nManager.getText("dialog.colourer." + LABEL_KEYS[i]))); + ColourPatch patch = new ColourPatch(Color.BLUE); // will be set by init() method shortly + patch.addMouseListener(new PatchListener(patch, inColourChooser)); + colPanel.add(patch); + _startEndPatches[i] = patch; + + // Add column to panel + colPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); + _patchPanel.add(colPanel); + } + + // Blank column + blankColumn = new JPanel(); + blankPatch = new ColourPatch(Color.BLACK); + blankPatch.setVisible(false); + blankColumn.add(blankPatch); + _patchPanel.add(blankColumn); + + _patchPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + add(_patchPanel); + + // Combo box for selecting max colours + _maxColoursPanel = new JPanel(); + grid = new GuiGridLayout(_maxColoursPanel); + grid.add(new JLabel(I18nManager.getText("dialog.colourer.maxcolours"))); + String[] colourOptions = new String[] {"2", "3", "5", "10", "15"}; + _maxColoursCombo = new JComboBox(colourOptions); + grid.add(_maxColoursCombo); + _maxColoursPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + add(_maxColoursPanel); + } + + /** + * Init the colours from the colourer (if possible) or the default colour + * @param inColourer current colourer object, or null + * @param inDefaultColour current colour for points + */ + public void init(PointColourer inColourer, Color inDefaultColour) + { + Color startColour = null, endColour = null; + if (inColourer != null) + { + selectColourerType(ColourerFactory.getId(inColourer)); + startColour = inColourer.getStartColour(); + endColour = inColourer.getEndColour(); + _maxColoursCombo.setSelectedItem("" + inColourer.getMaxColours()); + } + else + { + // no colourer, so default to 5 colours maximum + _maxColoursCombo.setSelectedIndex(2); + } + if (startColour == null) {startColour = inDefaultColour;} + if (endColour == null) {endColour = makeDefaultEndColour(inDefaultColour);} + if (startColour != null) {_startEndPatches[0].setBackground(startColour);} + if (endColour != null) {_startEndPatches[1].setBackground(endColour);} + onColourerTypeChanged(); // make sure gui is updated + } + + /** + * Make a default end colour if there isn't one already defined + * @param inStartColour start colour + * @return end colour, with the hue shifted by a third from the start + */ + private static Color makeDefaultEndColour(Color inStartColour) + { + float[] defaultHSB = Color.RGBtoHSB(inStartColour.getRed(), inStartColour.getGreen(), inStartColour.getBlue(), null); + // add 120 degrees to the hue + defaultHSB[0] += (1.0f/3f); + return Color.getHSBColor(defaultHSB[0], defaultHSB[1], defaultHSB[2]); + } + + /** + * React to the colourer type being changed + * by showing / hiding gui elements + */ + private void onColourerTypeChanged() + { + final ColourerId id = _typeIds[_typeCombo.getSelectedIndex()]; + // Set visibility of controls according to whether they're needed for the selected type + _patchPanel.setVisible(ColourerFactory.areColoursRequired(id)); + _maxColoursPanel.setVisible(ColourerFactory.isMaxColoursRequired(id)); + } + + /** + * @return the selected colourer object, or null + */ + public PointColourer getSelectedColourer() + { + final ColourerId id = _typeIds[_typeCombo.getSelectedIndex()]; + return ColourerFactory.createColourer(id, _startEndPatches[0].getBackground(), + _startEndPatches[1].getBackground(), _maxColoursCombo.getSelectedItem().toString()); + } + + /** + * Select the appropriate item in the dropdown + * @param inId id of colourer to choose + */ + private void selectColourerType(ColourerId inId) + { + int selIndex = -1; + for (int i=0; i<_typeIds.length; i++) + { + if (_typeIds[i] == inId) { + selIndex = i; + break; + } + } + if (selIndex < 0) { + System.err.println("Id " + inId + " not found in _typeIds!"); + } + else { + _typeCombo.setSelectedIndex(selIndex); + } + } +} diff --git a/tim/prune/gui/colour/ContinuousPointColourer.java b/tim/prune/gui/colour/ContinuousPointColourer.java new file mode 100644 index 0000000..64a9583 --- /dev/null +++ b/tim/prune/gui/colour/ContinuousPointColourer.java @@ -0,0 +1,74 @@ +package tim.prune.gui.colour; + +import java.awt.Color; + +public abstract class ContinuousPointColourer extends PointColourer +{ + /** array of colours to use */ + private Color[] _colours = null; + + /** + * Constructor + * @param inStartColour start colour + * @param inEndColour end colour + */ + public ContinuousPointColourer(Color inStartColour, Color inEndColour) + { + super(inStartColour, inEndColour); + } + + /** Continuous colourers don't need a maximum count */ + public static boolean isMaxColoursRequired() { + return false; + } + + /** + * Initialise the array to the right size + * @param inNumPoints number of points in the track + */ + protected void init(int inNumPoints) + { + if (_colours == null || _colours.length != inNumPoints) + { + // Array needs to be created or resized + if (inNumPoints > 0) { + _colours = new Color[inNumPoints]; + } + else { + _colours = null; + } + } + } + + /** + * Set the colour at the given index + * @param inPointIndex point index + * @param inColour colour to use, or null + */ + protected void setColour(int inPointIndex, Color inColour) + { + if (_colours != null && _colours.length > inPointIndex && inPointIndex >= 0) + { + _colours[inPointIndex] = inColour; + } + } + + /** + * Get the colour for the given point index + * @param inPointIndex index of point in track + * @return colour object + */ + public Color getColour(int inPointIndex) + { + Color colour = null; + if (_colours != null && _colours.length > inPointIndex && inPointIndex >= 0) + { + colour = _colours[inPointIndex]; + } + if (colour == null) { + // not found, use default + colour = super.getDefaultColour(); + } + return colour; + } +} diff --git a/tim/prune/gui/colour/DateColourer.java b/tim/prune/gui/colour/DateColourer.java new file mode 100644 index 0000000..5a6306b --- /dev/null +++ b/tim/prune/gui/colour/DateColourer.java @@ -0,0 +1,111 @@ +package tim.prune.gui.colour; + +import java.awt.Color; +import java.text.DateFormat; +import java.util.Calendar; +import java.util.HashMap; +import java.util.TimeZone; + +import tim.prune.data.DataPoint; +import tim.prune.data.Timestamp; +import tim.prune.data.Track; +import tim.prune.data.TrackInfo; + +/** + * Point colourer giving a different colour to each date + * Uses the system timezone so may give funny results for + * data from other timezones (eg far-away holidays) + */ +public class DateColourer extends DiscretePointColourer +{ + // Doesn't really matter what format is used here, as long as dates are different + private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance(); + + /** + * Constructor + * @param inStartColour start colour of scale + * @param inEndColour end colour of scale + * @param inWrapLength number of unique colours before wrap + */ + public DateColourer(Color inStartColour, Color inEndColour, int inWrapLength) + { + super(inStartColour, inEndColour, inWrapLength); + } + + /** + * Calculate the colours for each of the points in the given track + * @param inTrackInfo track info object + */ + @Override + public void calculateColours(TrackInfo inTrackInfo) + { + // initialise the array to the right size + Track track = inTrackInfo == null ? null : inTrackInfo.getTrack(); + final int numPoints = track == null ? 0 : track.getNumPoints(); + init(numPoints); + // Make a hashmap of the already-used dates + HashMap usedDates = new HashMap(20); + // Also store the previous one, because they're probably consecutive + String prevDate = null; + int prevIndex = -1; + + // loop over track points + int dayIndex = -1; + for (int i=0; i 0) { + _colourIndexes = new int[inNumPoints]; + } + else { + _colourIndexes = null; + } + } + } + + /** + * Set the colour at the given index + * @param inPointIndex point index + * @param inColourIndex index of colour to use + */ + protected void setColour(int inPointIndex, int inColourIndex) + { + if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0) + { + _colourIndexes[inPointIndex] = inColourIndex; + } + } + + /** + * Get the colour for the given point index + * @param inPointIndex index of point in track + * @return colour object + */ + public Color getColour(int inPointIndex) + { + if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0 && getMaxColours() > 0) + { + int colourIndex = _colourIndexes[inPointIndex] % getMaxColours(); + if (colourIndex >= 0 && _discreteColours != null && colourIndex < _discreteColours.length) { + return _discreteColours[colourIndex]; + } + } + // not found, use default + return super.getDefaultColour(); + } + + /** + * Generate the set of discrete colours to use + * @param inNumCategories number of different categories found in the data + */ + protected void generateDiscreteColours(int inNumCategories) + { + int maxColours = getMaxColours(); + if (maxColours <= 1) {maxColours = 2;} + if (inNumCategories < 1) {inNumCategories = 1;} + else if (inNumCategories > maxColours) {inNumCategories = maxColours;} + + // Use this number of categories to generate the colours + _discreteColours = new Color[inNumCategories]; + for (int i=0; i sourceList = new ArrayList(); + for (int i=0; i 1.0) return _startColour; + + // Convert both colours to hsb, and interpolate + float[] startHSB = Color.RGBtoHSB(_startColour.getRed(), _startColour.getGreen(), _startColour.getBlue(), null); + float[] endHSB = Color.RGBtoHSB(_endColour.getRed(), _endColour.getGreen(), _endColour.getBlue(), null); + // Note that if end hue is less than start hue, hue will go backwards rather than forwards with wrap around 0 + + return Color.getHSBColor(startHSB[0] + (endHSB[0]-startHSB[0]) * inFraction, + startHSB[1] + (endHSB[1]-startHSB[1]) * inFraction, + startHSB[2] + (endHSB[2]-startHSB[2]) * inFraction); + } +} diff --git a/tim/prune/gui/colour/ProfileDataColourer.java b/tim/prune/gui/colour/ProfileDataColourer.java new file mode 100644 index 0000000..357f7f9 --- /dev/null +++ b/tim/prune/gui/colour/ProfileDataColourer.java @@ -0,0 +1,57 @@ +package tim.prune.gui.colour; + +import java.awt.Color; + +import tim.prune.config.Config; +import tim.prune.data.Track; +import tim.prune.gui.profile.ProfileData; + +/** + * Colourer based on speed values + */ +public abstract class ProfileDataColourer extends ContinuousPointColourer +{ + /** + * Constructor + * @param inStartColour start colour + * @param inEndColour end colour + */ + public ProfileDataColourer(Color inStartColour, Color inEndColour) + { + super(inStartColour, inEndColour); + } + + /** + * Calculate the colours according to the track and the profile data + */ + public void calculateColours(Track inTrack, ProfileData inData) + { + final int numPoints = inTrack == null ? 0 : inTrack.getNumPoints(); + + // Calculate values for each point + inData.init(Config.getUnitSet()); + // Figure out speed range + double minValue = inData.getMinValue(); + double maxValue = inData.getMaxValue(); + if (!inData.hasData() || (maxValue - minValue) < 0.1) + { + // not enough value range, set all to null + init(0); + } + else + { + // initialise the array to the right size + init(numPoints); + // loop over track points to calculate colours + for (int i=0; iwOXI{U49rmN+Do`b3NswPKgTu2MX&_FLx4R2N2dk_Hki%Kv5m^kh z<2nd4I%&+V01C2~c>21s-(Y5C;568J$Mh*sD8bXkF@)oKasmSrpW{I`j=9q`7?U{K zmhRAC$~<<#L|oim!C6FuL5PvtLeq(L4y(is*Vu*w5emdKI;Vst00gd1;s5{u diff --git a/tim/prune/gui/images/window_icon_128.png b/tim/prune/gui/images/window_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..9e7afc4ad0cc466c3fcea3b418edecae745819cd GIT binary patch literal 7176 zcmaKRXH-+q7wt_#l@8KDnlweKln4e;klubY=_p-#=sf`iRHO(BNL7(uM35o`f`yJz zkfOAp^p?;C|t=5JQJB0H724Uk8aep>wE{{Neg{!>xn8!=vuO zy@05wD0#mi|1ghxAzt#qaG#=GEdc-!d}O4jYZG0(QxaD%Ipnga-MbsM>*2lI9f{M5 zibQp}X8k_$M$L46y)dKql9tYciJk~ibT>2Pazfo>o`PMZMq$5jsa1-ebJ(AQI$Nz}sPk9PVW!834)In`UNBSpp5q z|G$=`pn8yGad9}H0V<|hMVg@XZBac{$EtCH9jY8ToY8dAV8||`WBL4HpDDNCFHghx zL`I^pEqOn4o{|Af7&Q#J(Z)xRk9pkv{>CssQkgvsb%zYq`I`F*tjb2tMX>5f45-X% z@@Wx;t}kJX;m0*`crwp+6J4ofNXda*h|WtsWlXl%?Lrmz>5b(DS{$Xb<7(RNt(t4wJIBm|wk|}4zk||8GZKxk61A~D z){9Yh015H^>-c$e)ctrhfL5P{pu{>teXK4Fub=T;q`$;eeTpTCrm!WMf=y`k`J4N$ z#=a1oRGsSdS2*OvV~~{ET2#F80qR%J!17WzSBK6_mY1&4po)o(V(y-_B zvYs(irldl7qAQ}4C%#GKMH~iUU*4Dxn)QHgD3b^(K7_D~)vr3r+o$Mx%8i8<-AkAz z-Tq`&T0n4Clh4*;P>u|u;z``ZeDo|Rb#yOekA{sq>pw+b1dp_S_6L&R5I<#OG_eS- zyGZOxt>)E4}!<~!PqBP0o+e?6v$Qw(C@5^>Zn#o-q4MAwROXnE|RH`K-tciuudRZ zS*Vj%zr?pKKIJvPc+P0XYe@SzGQYN*nCc?2uyrHIh!GUxI1+mG|J9ePH^K z^J=9MJ_ZY&=AMgcO3_~yq9Y!Iymb2t|5{y=bsf-R;=u0MDN>|60o((#VXz#-6-SX4 zPxf2998-mMDn(Z#xW6YRm~OVxP2)%2WqyR4zI2$J&6tU%BX*&))K%v!cy#p6rSKl; zd%7AfTA%%&Ot+xlyxqEnm0y1?C^~amQ@@+5LaK=*>ou@Vyi(jN?XTMZG>>?p8T)kP zloQR1l(zYpO!^8=AH0oD`rKV{&*3{hdhas5=Mba^Vi!RMsiUDi@u^&Jo>~8V z$U-|!wv>)9#4(g5mdS|0kX_({Ay3uy87c1PgObPtxi?o+?boG-?m>rDQOtO@j%h|w zaUVrT3X6N|dHe};*t4=I26jNC6ZFXm7}|pL;C8B>2?+5>$5xwY*!v!JUwiw#GvNrf z^8P+l#-O9o-tsU|9PVt}G@nb2L>w3f77|t-*c$HrYy{=ialVYq>dW%`0o!kd_|BfZ z{+%UeL>nKd6o|+Nhz*6&$1sw}HJO5Z{oPIBzKjUs@=bke46vLTY^sq{;7<-#1Pgs~ z#1&7(`x1fp0{Yk7iqPhc?4yZz_320IYR7@+tETY<<5K)lW>sFL`lM50!<@ z8fxx?Tq_gBFVhmNanvvYwAtWfV5yB8lIIVqocN*U9I%yAV5-)xfDJWgj_x)a0zaKo z9BM+wiPEv>NMbz-#O`W|N<-axa%fqvm#e)u)=8Bk7EhkNw=`a6rJ8%iZou1?&j~OS;>YXz!Qa4%`=k#@Tb@f$gGPn7ulo?}cz5ELV{W|f@ zK2+!B13o9TzVl2NkLU`k-1_U?fqw1eKEh$m9k0uSdvD`<=@q;P^IPGqlHDHAY;ptL zM)$=$8}DxPekr?-UJosi-+ixIYfx}_Z4=24`|*f|R8pR%dw&j$XgwbYjbU;85H?VC ztNv&rjv#Mlz7dx@Mcg*RLh|w=>(MD)=2Xto9VNv=ZM8;E{_-0evz;a9{l*#5g?a$m zm)6Ga?NCPBWyG0=j3N}?mqSnM5Yea}S#kvr?9c=Ilzkg*d)Yw)CZVs# z*r~Os6r2+>S$zCdEzn)0KwF6>J%w6AsGd5xu7y z>x7sWMWlDrzCEGO8dA1+8C+|ySpilTAzUY#JAQ`m^pxa5v&AGK2Bd4C(W3p@sncdd zGQRG#sjSh0`A2K8#xz^V;@R5wLOG9qLdI-utiJ(+C8mXRF$DjxcqRU%KftL6|0(jH za!x}!T0x*eL~+0}HSZ-23&_}l^z63+-?XEv(W7cXk?@FdQ?7DJ^Z|TJs04`KEH>>l z`p`HQ|5+O;yncZvX8_?wn<<>nRjg3Pct)(ijzvIveq)fqQf?TfJZS2ul6xiyV$6TT z%oJj+|E;4`yT*pK&w0pTH@7XpEYa;b5q-5*jQ=!+t%J*$!q8R-9L;uPz*Sj;p*?Tn zt$R?VW@(gwDofTgu`j@xSQxYC_hvw!++m7NlX;QJap8}=y9mKN=^0Z6+ZFaP%E-$t zP=}j5&7~%15eJVkDGFa(4j>L^etVK7{QQfSZBJ)udJd>vBK>LH5xj4q+~HxL+;^($ zqv9<67Ya0V*x#N6{BkK@jXzkQQwry9?kyE6f2XQNqh)QbD$^eJhpm)p2F7HmP_!?L z-Oz^)TCapL^R0foG2xdxem2*f{07wP@GJ$|a*O_8)nZj$dW?x%cUpgUm@)(rAIG9n;tONbN^~ccmJ4mq_~9YX@@p)cu&%yQ10} z5=Vd3Y`;sJ+->wk>ugrKnj!v6?Px%VdB^YsimES_f#Pj6X-H7>&kyN+-ITX=T(2jL z{_qg!eA^zd|8G|L%3Zw+SRZ7QpyG1PK6KR~1G^8BWb`dIz~35}ri?$vn;2hc9=pkP z2+DYn8_eUW?2pq+x^aIr^}xML>VY|69QTrGcy;6*?%T~aBkbWNg~H-{92K)03A>iY08i_g2p(0$!1?*(ta zrP1$?K@FTSWXAl+s}soHJ^_gq#8*|Udcb)lHSuTz@kzRUTO&RW&m}6tXCePp(ifM9 zTbqAQEec$mz0}R}rz7bX0@e5FWFkWCZdj`}a6|LnghlX$yC44)jqiqVFtit&eAw{R z51v_;o{M;>Mok|voJ4gXE8iki?@~H|(G>>yZB@|zdM-I?ff`fE`bGoyP|9j@x6xjB z((I_+yX4y=hV)y?&mf=}xJ@6aK*Y!C%z8B8Fc? zJo18$rNI9nuhVrJnN%;zA9h}U96s-Dum6r^8@SSt?Y*QgNSx$BSnV9l(h9h^JLBuv zv5ygiaIV}zrRLo+KZ+SD2t%r5oXh%vWB%xXFTE?>-cpA zKWFUD?9FzMs)TH_il(nsQX9F2Kqwb1Z($UJ? zR8nj}YjZxF=CQDPMcW33EUpqCG8|aX1~r?BY;9RtDzOcIRJe3@BgoJ)rq5MzZDFGA zBJoAp)UUj;OelGx<8dZ@V%WHjTxK_^&75^Cg-93b&-i!s`=I~b3sb*0dIKM9cbBS3 z2Go-GE7dUt)S$BYxN5=$wCyAwOet{JL$m$3m*4e8UDMCYH*7yRp1}6EAm&d!I8Co| zzo;XNn7=6O2cKQW59&fkfA3p~25&osd?STK-6E;?jTLH z)_>(~v&TnWiTs0=-u!u`T18(V?IQn&fID`Fv1W;h*V>~!6{r;44#r)TiRq3WkHf+O zVubS<1=wEFsB&MNt7#K>;V=89&*~YBivrHY!9v`$6I#k582e(ZJJVK6_vPIkYpOe- zx}?|iRPf~RBETj2(rSZ4^kuFxDZ{H!t}?-FjN@wUEW0*Au?bP@U35L^1R2zxsQo z*RXrT8{3BHbg#NIXI$juQ-{#>!R)?koTzMtY18x;0kUNxkj3UE@_fqA#4;fQ(R-!V zwKYMa%ytqJ`J%)V1fvQ0pfN4i=`Cpinyw%rx=v-0{j<&zw(+zLTO9c`=eA~DKO(@P zd<98E&w(nBqXs#2NoX?e&YbJlACdufK^rWj@ihVbxzS1s4Hcb4vmt~RbmH^Rsy=h3 zx*6fUFpr%V8RqTWGEaPk2p`{FH|zv%<0Kt4dEAOGUQV?-_r!mJrH!R%-=CKULGV$h zXH%B>v_y9FY0xR7eho{G?q-{Pb$^NMWT*bF>W- zY*OHyJ;Qr8b`npTZ?aLacFLmqSB49-dk!3B@3!aX6=$nh&)wqto!0rfj4kZ8zzGYC z(KvOQ-~=p9QFvEq&EDJS@PNvGoz5ky6A7hiSDV&tr$!MN3jG6lM4n-4kh*6(*~5W zq+z9!`sN2o(&gYD6Wd_IP4!#$dJD|mE#+9fP&L?>z8czro7mGBJVNcYRx=x0U|QW? zqqS&9s&~6A(WAivR5~4xQ4S9m!ATLhx$SH7cXoTJyJr@bXpBnY!-Al;2IqI?rlJC- zTpG%@*8Vax6226~ll$R=EO^#Vlr;V-9%`_z*`2De43MKgC z80;3g+F^#%qYmz5-w{|(cEM^|_tGmG-D1LnbTUVjZ?9U8OTm8KVLXKLzKv6?y>GwG zI@TkCk1!h#&myALp)B|l#Cu*4r_11c0>rPOo#gbQD>kj}Pr-RoeQnaG!dLUMwQNbd()Cwzgs_FD&4HkieYO6O0b#A^39>v1|38 z?Yv65oTu`)txsib$&bgTRvZycw3(O|bh1$S2&PK4J8jBfZJPAG*7-`0?Z$rgn3b9S zIYEUbE~?7Xb*rUWSA53A;Zegd-IjBGPe8*U1uaME@e5_dyCS~8M^j*QZGlsrcJ3dh zsy4aATbi;r@kZ0L;2qow1l`KSaISy5ecWAoNO5UEuuWXzj&rSRMdHYD9h$UFlQlLB zyyw#D;jd1z8UgNqWh38j$m+c-?-{n`D{RvK8xxllRcF>1d^#U~=}fA3z!GnhgkXx_ zCE9t77MM&v%@{48UZh>9tKD*Hn07=+BdsB+a!<&GBqQ|1S8#5xYd%xx>`C))i`>WM z#Z9E14GevfANDrB>)0a~w|PEAzFE+A{%%vBG*a#c^n|u(^9VnYL>B=(E20hFM~4Ry zii`Z)Ug)T|38e3$@rwnl#Lr}-N|9UOY#JDSbiX(aI=FqPJu0E|RC&wW@t7CcWF6>6 z2)302^>8vI_Xc56P5Gj_SBPPn5%Z!7^BK${?#@MJ#!kqPCZ_0Eq}%Oz4$&Z9V!87a z)0`JK7K~@LMZGtB0YTE<#R9KOj|c~@A;Dt1K7<{}!Gf?JC(Q0>SCzikhn5pP(0C6F zlIp%TXWsdS*FhCWX!5vDc{&>las+pCI5PpSr>aCBZ$AwD88aS8AhVXuy*N8FLdV5f zo8fjRq~A|Cx*g-TXzh;F(195k%I7v0bPh6I2+YcJq_^dKRuZ(5cAp>Ha~N6_Qz(yiZ-&7PPlXFA)Nj#YfB z?);O9(5RjE^UQxX@wJX4*wAnFjdb>)|4qGD7QO`GXHoqJl8gM|ir=RxiPsRt-0P6| zI4vo3DoKENzO%l(Apvpc5Yl;$bT+?_|CfKG1}VIu9zXVGuF(J6#X))m zEFB9jRXKKP?lT9PWTp?FYerGhY>o`t$+v`+(aa*=_5TR^rqA45x5N(D1JkmYA`O$L zwk_07?v+ZlaXA7v<#aC*dCp3*pEUdZD`G|2j#@TSIwg3wvOyuVzC5PBmu-S9{-(gT zWjVv22^vm{teV0>emERg-|vz7F0N0HOR_lxBPx1Xy@%SUdMyq&_)TgM=wN09|7da% zd5|d5vN*p4*|5{~C6K#a?L-|tp)9B7emBwAP(u+YPq(#1P;`Fn`AVisNHQ}*5Pe5q zwVqFNtj}?~C7Wkkan+(Qp%=;5v$O>k!4=#g!46M&5G}j~F}=Tb0d4QEwkTiCD74%p z;b!fp4<5WbVh~$fju=q%dE>$oLdaN~4zo!*U!082XU-Ow`B^m~ZCYdhMsvR@O!<$w z)W=-7Q!ut%?(G&s2<$%5Fo7U`jKWOvnucn9G#299LQd@geJ9Li$5EaLOd)b)Hhi@M#Hk;Ybk5Cp(fHgfO|K0gt z{=3~hZeZ9&`JVV*qr+UK+EXyT3DmewOZt4Di!-vVk}^N?Mn?VUhJqa>-~R9kEoB* zuJbZ-XEpM6e?~{c)wyxO*IjWSqCHx{cl!1x%)4AFj_`{r<~#F%2|Ul^!<@?3E_Hn? zDvh>x^wlo(`9(O>C9)D{>iT+*a~{+9UU(AEPPB5@aL@GEM<@8OOga2}xxip{Mu#6k zQ`LdcBuhVS!(cEFNk(UZ*FdWdSl{N+WPbZt%;PupE&Z<+mR>f_4Dr~V^FdCN>?iBu z;3>xlQ+)s}FL{7MYeT*R%!U85g_g>Nc0bmxn;YqWd)4@%|BC;dmzn*S%2oYQHdpeY zx!K20i=Gd_*QH-AGMxk7w!^VTO82Z--=oEu;N|Cxze{qJBz7Iq5tHVxSywPhOZT>46Wbs*JL^b>%OeKj$gT9!?{4y-eC8J#lM*5G;TS);EyK6I5)-V-m z+cKcodBmWSqjJ3!NMx7+_)p8Ee+uvRFL)Qhu}QMFStwQ2_# M>6_~{-f~a=KTQ=FQvd(} literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/window_icon_16.png b/tim/prune/gui/images/window_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..919f777362aca99caf4bf4de060cf8d4528f8beb GIT binary patch literal 662 zcmV;H0%`q;P)YD5JSK_% zsqk-_%q(coSPKawp}NC;kW>KJpEfYFuvb}pl~a(DGa}arfXe*W5MU;ld$eFno1xVSr~&Y~ z+9M;yDZ!DDk&-E8grgv*pctgp@LAZl?#6O$Z3{qDcygRXN=!k1^0ZVjDKSn7011QV zPHs1x3EFq+G}<0du8firGow;TLCy$=8=GWG0f;qt+tHq)$07*qoM6N<$g5$6z?*IS* literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/window_icon_20.png b/tim/prune/gui/images/window_icon_20.png new file mode 100644 index 0000000000000000000000000000000000000000..4d020f172c8f7899eb157fe3a0bf53304e5876ae GIT binary patch literal 819 zcmV-31I+x1P)66nA$qF<*tx z(dlNMnRnhZ$0Tu@;ZBtX+N8p;R?F?=6%WZf4qcd*UHyj`u z8frxX6+s@)jEWgYI+(slqgy8=o0;2mm%i9>H}77^u3YTUB_{`rL_;J1&`@J$m<$jZ z#h`Efd-*G{Y|Fj=@GbZL?G-y?J^FO4OGb`J1S*mm%nS)6k!Wa?+WnZzkHLzI?(*K7 z?%mxLTcyu{o(hS0+F_ssX*(v2*e+AfT3;Nxsl)exS+t{FdRaRHDat0kwB$cYk*o4f;@q? z07^}sPj&bpH?!Uqd*jm%{Q{tUm6{+=B+QM71pE@}HN`W@`%$WWdb>-1{nZ6Rc$Gf|;&(yiqf+J5zXSaR2+*_MPR9-I x=gKZEVWyE!{##G^M3VnE&v1KL8uPq{rtTg{uGn002ovPDHLkV1g`RdrSZT literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/window_icon_32.png b/tim/prune/gui/images/window_icon_32.png new file mode 100644 index 0000000000000000000000000000000000000000..044336d2437feb899428fafd0cdaa5692e425d76 GIT binary patch literal 1354 zcmV-Q1-1H#P)6S+3hF{Pf}kq}!HsSd1yN8}3c4v2H-auKf+DDNAxf>Z)_x@~U+?4InK>@* z$Ga~{Yh%TDhJm@0cjx~*XU?3t5mjX)&1|Up{{bNQHy0C;p)%z|La;fRamhU5x^%x( z)t^_rbEP>TBHQa`=&`ES@2E>-=ZI{XG8m&M9*4u@u`Foo!m^(~-lg|RPus6u5rBvc z4VmW46|LQNm5g3HZYH-?sG)%3)8>+<7E6r75)-qmt?oI0`s*&8dsVfwZh(diKh>0x zr+1b1%v7mkFn|Dx=JJWSCC&k2Vk{A{Ms(?L)}Q*UtL+CaI|s_r*f%VV*Jn!m_HUN) znvnp5K@cd&CthcB9xonINq?905&=!wbayD>!M_C%kxflA`o<1(-91z0`mKS41OymL zIY80#K9EOpKq;)4Y*bB#nv2qU;*tPmY1}$4lONq^_RdtLK}bkI3LpU{UD)Gs>ToHH zhy#@stLZ=!1o5TGivrZ8eqc1l0YrjG_EqRTRIdbX339$|^F#D#9Lnv65=6=g|V zM^*KQ9H3?zuT07AJ&9Z<+CSqSM~^O%_VVX!;h3lZ(jVIbRB`X;_56t*-6tWvj6`H| zt4uyrGmR1hg(WL3f~sQDMY5zyf*oVE0I?=WKlGN!b+rAls-DaN8Z_UTFw;{-Syhh{ zm%|7yEx3aKZXgOVwjds94@*3dq& zH0d>}`KLzlJICdP#;zgk@yz-&$0E89#~OdXmN-D*j6E3>r^n3W(;Se2@72PUq6L5z z`<&ULw*G9((&t^;&#G$cV&VW25F8wlvBM2%HnST}@{6`8-_L-Rdz@L1PN(H(zjZ{< zMH+o~Dd8MIRjEq-(=jt~e~HrNytB$2mN|d^SVZs5NV{*X=Uq`%M5I=wc6h{$-Cm|z zy&TldCH!;1JRCQ^+@B<&Ep;Vr)P<^l>wdtzVHn04#;(c3}PQ=>#HNt&m z>At6`r`9WaQGk3>WOqn-Czydf@z@IvcjTXVx-NkK(l-2m!wm=c3qO=d6-&=kcK`qY M07*qoM6N<$f}8nv+W-In literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/window_icon_64.png b/tim/prune/gui/images/window_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..f3b94a91fb6a6157062d8d456c5dbb0005008659 GIT binary patch literal 3073 zcmV+c4F2}o|*2R+1c5v2ghD)V`F;_R-APt zEG#=BNH!AUDlQ2~L~XJy z324pk&d$uvOy9e{KMz%1_nyn{+GB)IX}YVcs=L1b|D5mK>Kl{9@3XkW9>8JCTLc{T z01jK;BH*wGaMY(<8Ti-fuzPuoq61?wbw&R&&3pAw)70h!*V8sPbNMMOkw>ax~#aa;I5lJxU8lmIh(C?x!9 zK;zcig1b8>?M5uOC0C_;+68=(fLO~#@EHM7dOqIY(uQBx#s3%n9!Wbujm=$O;P;1w zPc@l)*R8?b?bCLXIpQsbYnV+npUroGSTSY82an+ZpX`}|NhLy)-%crwk92Iw4e6ck zs{2jg(btUtGrJ)q{QCyY_uk^}Y`x9B-5fyz++kK6tmVF&>fWXfFNU9;^LSkH%_8vp z?`3vXlodNQKhUf1HM2(~y>Q(LFta}k2)=fsyKUi&yQ47;Th3fEM3)ZVN?J;w7dP{G zzzICm12>zpQvqBVAI6d$ZEx!G=YZ=+fSH{P3BSHz?Fa60_qUg9B{p^o6406HTl!uc za@pfg%^tuTn5N)eNwxLTOFtg@$A;8Q&;Rv2tb#I@))!o$yY2YkK zAO|_{yAe?J_3>P<(>fIAd3h2k1+2pHSOP*qn$eiy`>u%qGn)$t|8b6mN6!Z5TFZ8v zfFJ>1C7?)xCF3?w)!UPTpXK1nQUnfViz-UX+KX2-Ja^RunAwSt@ZXp1`0aNG=UNS# zI0Awce2_rTS%8%YNPmnpwT;78RqG^#SqX`D0Y2T&e=gh?Q zF*}t3u7D2<(;>hS(qzu&X)s4XfPjbzV+|S7M=AImVDdWN9bhqAEbeb&RM$>c3YQgOKtU9POe<8*iwSbrRY>AoKyBxs_cLn#gmuw~R;;sPR@+4Rm z@|w&uZ*3N6vtS)Mc7&EKrVWjV2@}RdOejVHp8`=1j>bBDf6Nt7s8r8LE$A|0Aip zs|A?ZEso%&JA!vFF58JTkuApb{HN@g;b>Zu22Db164JoS(_@DLJ%;KtOx}y-Yza&X z$X7Pi950hmCw;mT;91zk37T2k5xjiH-EriYohqiXNnW21Yvm$mY8e2Yh#`H3^cgc^ zs6Ksm=xK)u?zh)0ZU06?S(6GluD;A!JR8Sp4iLWPvf zQlc75$rDm9VpD595q)ubq(Xq1opQLZ-|p^d1vF~Flj?d_ArsF-7&Btb2$NOL7qwf| ze3XKWs^{f%pK}(Fvy@q~qiuknNqS`_0RiE^oUqk}MR&9|70t7mPe?V5y%rZSabn{! zjAGuFIkvLqQUa7GWRj7f()6gYlv$>)So__G=nMJd1p>_M;RbW(ZgjUa_G&DYEB7f8 zUSOt@g-i*m#_4JguWAiZ0sN$B896K3E#=+R+SsG-OL}o80U_a&Z*ynbb;V{q@S;&e zQo*U(lqjdBS-49KIn|=C5HJgTq$qWyyP5ugKHJ*t3w^SDE+xRs9tdgNxnd_PfHMHI zpereERPq$rce8>wPeJAj%1*3)IvgCl$Ft_o_k=zp>B39`0>ZD{;BIbN>dmLG(?RF7 zC`)^FP;60^onw`}X^<+0tvc=J&AVt3Ilr(Ac!10L%|Pg1B|TSnJ_Incdqcu|SL~#v zz$Ikmd2Sg9fivL?{khpq&RokQNn*-bUpXw6$f!(*y}|G3itnp?S?KXu*MtBL_u*wb zF_-VmSHqg+W_nV>C;?w+a~HvU>xDMp|Cpo~_5*%OKnNBT)0qr> z!R)8qTFcM*x}+)7eb%td48P717Q&;ip zURATN!Y_x=dLNM0@Wq^9zp|lAqit=j3;l_tpB)5zO2E`Sy=A~5doWRF_Eqz*_ULD& zKq(0|`M`e6eR_vWI^P?p|Gz@-mvrf%;HLzn3r(DohmgrFvIa;#(Sh0KU)6mztg7Gu z4+73Xz|Z#nsfE?}fP(-R zVpD75ZEX&O9+UKqs|7xl5cuwxQ7~adNF&vFn&qWVkCGLX2bV!l!Kyd=GqV@5X zHu}PUUeZ5ZCDvLoNy$^6#y3~pnNEERteEH5B|Wp||J94w(Uvx}))RbBM1LXawQKmn zT1i)lISZd}Tl?W-c6z>YQ9x01D2u3j+GazSb|U;Qg#T+vFTLLPEUXhWvv(NozvJbq%X_s2uZv6=v!g5H zZCx1&y(;_{Bt8E|+#9Galr*!mhWkc?=9v@jbStEJ-EZpg^t8>kHn+x%WY|A={NI+B zvAz*fyHeE5oWngK^yNicZnSK%F-OHU97skC7-~R|?)Ff}KF`hx-(8e8D=dMQ1)pmcqm (getWidth()-PAN_DISTANCE)) { panX = AUTOPAN_DISTANCE + px - getWidth(); } - if (py < PAN_DISTANCE) { + if (py < (2*PAN_DISTANCE)) { panY = py - AUTOPAN_DISTANCE; } if (py > (getHeight()-PAN_DISTANCE)) { @@ -580,24 +584,58 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe } } - // Paint the track points on top - int pointsPainted = 1; - try + // Work out track opacity according to slider + final float[] opacities = {1.0f, 0.75f, 0.5f, 0.3f, 0.15f, 0.0f}; + float trackOpacity = 1.0f; + if (_transparencySlider.getValue() < 0) { + trackOpacity = opacities[-1 - _transparencySlider.getValue()]; + } + + if (trackOpacity > 0.0f) { - pointsPainted = paintPoints(g); + // Paint the track points on top + int pointsPainted = 1; + try + { + if (trackOpacity > 0.9f) + { + // Track is fully opaque, just draw it directly + pointsPainted = paintPoints(g); + _trackImage = null; + } + else + { + // Track is partly transparent, so use a separate BufferedImage + if (_trackImage == null || _trackImage.getWidth() != getWidth() || _trackImage.getHeight() != getHeight()) + { + _trackImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); + } + // Clear to transparent + Graphics2D gTrack = _trackImage.createGraphics(); + gTrack.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f)); + gTrack.fillRect(0, 0, getWidth(), getHeight()); + gTrack.setPaintMode(); + // Draw the track onto this separate image + pointsPainted = paintPoints(gTrack); + ((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, trackOpacity)); + g.drawImage(_trackImage, 0, 0, null); + } + } + catch (NullPointerException npe) {} // ignore, probably due to data being changed during drawing + catch (ArrayIndexOutOfBoundsException obe) {} // also ignore + + // Zoom to fit if no points found + if (pointsPainted <= 0 && _checkBounds) + { + zoomToFit(); + _recalculate = true; + repaint(); + } } - catch (NullPointerException npe) {} // ignore, probably due to data being changed during drawing - catch (ArrayIndexOutOfBoundsException obe) {} // also ignore // free g g.dispose(); - // Zoom to fit if no points found - if (pointsPainted <= 0 && _checkBounds) { - zoomToFit(); - _recalculate = true; - repaint(); - } _checkBounds = false; // enable / disable transparency slider _transparencySlider.setEnabled(showMap); @@ -613,15 +651,12 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe { // Set up colours final ColourScheme cs = Config.getColourScheme(); - final int[] opacities = {255, 190, 130, 80, 40, 0}; - int opacity = 255; - if (_transparencySlider.getValue() < 0) - opacity = opacities[-1 - _transparencySlider.getValue()]; - final Color pointColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_POINT), opacity); - final Color rangeColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SELECTION), opacity); - final Color currentColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_PRIMARY), opacity); - final Color secondColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SECONDARY), opacity); - final Color textColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_TEXT), opacity); + final Color pointColour = cs.getColour(ColourScheme.IDX_POINT); + final Color rangeColour = cs.getColour(ColourScheme.IDX_SELECTION); + final Color currentColour = cs.getColour(ColourScheme.IDX_PRIMARY); + final Color secondColour = cs.getColour(ColourScheme.IDX_SECONDARY); + final Color textColour = cs.getColour(ColourScheme.IDX_TEXT); + final PointColourer pointColourer = _app.getPointColourer(); final int winWidth = getWidth(); final int winHeight = getHeight(); @@ -659,26 +694,34 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe currPointVisible = px >= 0 && px < winWidth && py >= 0 && py < winHeight; isWaypoint = _track.getPoint(i).isWaypoint(); anyWaypoints = anyWaypoints || isWaypoint; - if (currPointVisible) + if (!isWaypoint) { - if (!isWaypoint) + if (currPointVisible || prevPointVisible) { - // Draw rectangle for track point + // For track points, work out which colour to use if (_track.getPoint(i).getDeleteFlag()) { inG.setColor(currentColour); } - else { + else if (pointColourer != null) + { // use the point colourer if there is one + Color trackColour = pointColourer.getColour(i); + inG.setColor(trackColour); + } + else + { inG.setColor(pointColour); } - inG.drawRect(px-2, py-2, 3, 3); - pointsPainted++; + + // Draw rectangle for track point if it's visible + if (currPointVisible) + { + inG.drawRect(px-2, py-2, 3, 3); + pointsPainted++; + } } - } - if (!isWaypoint) - { + // Connect track points if either of them are visible - if (connectPoints && (currPointVisible || prevPointVisible) - && !(prevX == -1 && prevY == -1) + if (connectPoints && !(prevX == -1 && prevY == -1) && !_track.getPoint(i).getSegmentStart()) { inG.drawLine(prevX, prevY, px, py); @@ -859,18 +902,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe } } - /** - * Make a semi-transparent colour for drawing with - * @param inColour base colour (fully opaque) - * @param inOpacity opacity where 0=invisible and 255=full - * @return new colour object - */ - private static Color makeTransparentColour(Color inColour, int inOpacity) - { - if (inOpacity > 240) return inColour; - return new Color(inColour.getRed(), inColour.getGreen(), inColour.getBlue(), inOpacity); - } - /** * Inform that tiles have been updated and the map can be repainted * @param inIsOk true if data loaded ok, false for error @@ -1050,8 +1081,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe else if (_drawMode == MODE_DRAW_POINTS_CONT) { DataPoint point = createPointFromClick(inE.getX(), inE.getY()); - _app.createPoint(point); - point.setSegmentStart(false); + _app.createPoint(point, false); // not a new segment } } else if (inE.getClickCount() == 2) diff --git a/tim/prune/gui/map/MapSource.java b/tim/prune/gui/map/MapSource.java index d838ef3..88ab85a 100644 --- a/tim/prune/gui/map/MapSource.java +++ b/tim/prune/gui/map/MapSource.java @@ -87,6 +87,8 @@ public abstract class MapSource // check prefix try { new URL(urlstr.replace('[', 'w').replace(']', 'w')); + // There's a warning that this URL object isn't used, but it's enough to know the parse + // was successful without an exception being thrown. } catch (MalformedURLException e) { diff --git a/tim/prune/gui/profile/GradientData.java b/tim/prune/gui/profile/GradientData.java new file mode 100644 index 0000000..78bf497 --- /dev/null +++ b/tim/prune/gui/profile/GradientData.java @@ -0,0 +1,66 @@ +package tim.prune.gui.profile; + +import tim.prune.I18nManager; +import tim.prune.data.GradientCalculator; +import tim.prune.data.SpeedValue; +import tim.prune.data.Track; +import tim.prune.data.UnitSet; + +/** + * Class to provide a source of gradient data for the profile chart + * or for the point colourer + */ +public class GradientData extends ProfileData +{ + /** + * Constructor + * @param inTrack track object + */ + public GradientData(Track inTrack) { + super(inTrack); + } + + /** + * Get the data and populate the instance arrays + */ + public void init(UnitSet inUnitSet) + { + setUnitSet(inUnitSet); + initArrays(); + _hasData = false; + _minValue = _maxValue = 0.0; + SpeedValue speed = new SpeedValue(); + if (_track != null) + { + for (int i=0; i<_track.getNumPoints(); i++) + { + // Get the gradient either from the speed values or from the distances and altitudes + GradientCalculator.calculateGradient(_track, i, speed); + if (speed.isValid()) + { + double speedValue = speed.getValue(); + _pointValues[i] = speedValue; + if (speedValue < _minValue || !_hasData) {_minValue = speedValue;} + if (speedValue > _maxValue || !_hasData) {_maxValue = speedValue;} + _hasData = true; + } + _pointHasData[i] = speed.isValid(); + } + } + } + + /** + * @return text description + */ + public String getLabel() + { + return I18nManager.getText("fieldname.gradient"); + } + + /** + * @return key for message when no altitudes present + */ + public String getNoDataKey() { + return "display.noaltitudes"; + } +} diff --git a/tim/prune/gui/profile/SpeedData.java b/tim/prune/gui/profile/SpeedData.java index cd4d49d..4b0f350 100644 --- a/tim/prune/gui/profile/SpeedData.java +++ b/tim/prune/gui/profile/SpeedData.java @@ -39,8 +39,8 @@ public class SpeedData extends ProfileData { double speedValue = speed.getValue(); _pointValues[i] = speedValue; - if (speedValue < _minValue || _minValue == 0.0) {_minValue = speedValue;} - if (speedValue > _maxValue) {_maxValue = speedValue;} + if (speedValue < _minValue || !_hasData) {_minValue = speedValue;} + if (speedValue > _maxValue || !_hasData) {_maxValue = speedValue;} _hasData = true; } _pointHasData[i] = speed.isValid(); diff --git a/tim/prune/gui/profile/VerticalSpeedData.java b/tim/prune/gui/profile/VerticalSpeedData.java index 510de88..6fe9bf7 100644 --- a/tim/prune/gui/profile/VerticalSpeedData.java +++ b/tim/prune/gui/profile/VerticalSpeedData.java @@ -40,8 +40,8 @@ public class VerticalSpeedData extends ProfileData // Store the value and maintain max and min values double speedValue = speed.getValue(); _pointValues[i] = speedValue; - if (speedValue < _minValue || _minValue == 0.0) {_minValue = speedValue;} - if (speedValue > _maxValue) {_maxValue = speedValue;} + if (speedValue < _minValue || !_hasData) {_minValue = speedValue;} + if (speedValue > _maxValue || !_hasData) {_maxValue = speedValue;} _hasData = true; } _pointHasData[i] = speed.isValid(); diff --git a/tim/prune/lang/prune-texts_af.properties b/tim/prune/lang/prune-texts_af.properties index f208cf0..50d9c80 100644 --- a/tim/prune/lang/prune-texts_af.properties +++ b/tim/prune/lang/prune-texts_af.properties @@ -10,10 +10,7 @@ menu.track=Spoor menu.track.undo=Herroep menu.track.clearundo=Herroep Lys Skoonmaak menu.track.deletemarked=Gemerkde Punt Uitvee -menu.track.rearrange=Herrangskik bakens -menu.track.rearrange.start=Bakens na begin van l\u00eaer -menu.track.rearrange.end=Bakens na einde van l\u00eaer -menu.track.rearrange.nearest=Beweeg elk na naaste spoor punt +function.rearrangewaypoints=Herrangskik bakens menu.range=Reeks menu.range.all=Selekteer Alles menu.range.none=Selekteer Niks @@ -275,11 +272,13 @@ dialog.correlate.correltimes=Vir korrelasie gebruik: dialog.correlate.timestamp.beginning=Begin dialog.correlate.timestamp.middle=Middel dialog.correlate.timestamp.end=Einde -dialog.rearrangephotos.tostart=Beweeg na begin -dialog.rearrangephotos.toend=Beweeg na einde -dialog.rearrangephotos.nosort=Nie sorteer -dialog.rearrangephotos.sortbyfilename=Sorteer volgens leernaam -dialog.rearrangephotos.sortbytime=Sorteer volgens tyd +dialog.rearrange.tostart=Beweeg na begin +dialog.rearrange.toend=Beweeg na einde +dialog.rearrange.tonearest=Beweeg elk na naaste spoor punt +dialog.rearrange.nosort=Nie sorteer +dialog.rearrange.sortbyfilename=Sorteer volgens leernaam +dialog.rearrange.sortbyname=Sorteer volgens naam +dialog.rearrange.sortbytime=Sorteer volgens tyd dialog.compress.closepoints.title=Naby punt verwydering dialog.compress.closepoints.paramdesc=Span faktor dialog.compress.wackypoints.title=Gekkige punt verwydering diff --git a/tim/prune/lang/prune-texts_cz.properties b/tim/prune/lang/prune-texts_cz.properties index cab34c7..2ac7ca6 100644 --- a/tim/prune/lang/prune-texts_cz.properties +++ b/tim/prune/lang/prune-texts_cz.properties @@ -13,10 +13,7 @@ menu.track.undo=Undo menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo menu.track.markrectangle=Ozna\u010dit body v obd\u00e9ln\u00edku menu.track.deletemarked=Smazat ozna\u010den\u00e9 body -menu.track.rearrange=P\u0159euspo\u0159\u00e1dat z\u00e1jmov\u00e9 body -menu.track.rearrange.start=V\u0161e na po\u010d\u00e1tek -menu.track.rearrange.end=V\u0161e na konec -menu.track.rearrange.nearest=Zarovnat body na stopu +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 @@ -413,11 +410,13 @@ dialog.correlate.audioselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fd dialog.correlate.select.audioname=N\u00e1zev audionahr\u00e1vky dialog.correlate.select.audiolater=Audio pozd\u011bj\u0161\u00ed dialog.rearrangephotos.desc=Vyberte um\u00edst\u011bn\u00ed a uspo\u0159\u00e1d\u00e1n\u00ed bod\u016f fotografi\u00ed -dialog.rearrangephotos.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek -dialog.rearrangephotos.toend=P\u0159en\u00e9st na konec -dialog.rearrangephotos.nosort=Neuspo\u0159\u00e1d\u00e1vat -dialog.rearrangephotos.sortbyfilename=Uspo\u0159\u00e1dat dle n\u00e1zvu souboru -dialog.rearrangephotos.sortbytime=Uspo\u0159\u00e1dat dle \u010dasu +dialog.rearrange.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek +dialog.rearrange.toend=P\u0159en\u00e9st na konec +dialog.rearrange.tonearest=Zarovnat body na stopu +dialog.rearrange.nosort=Neuspo\u0159\u00e1d\u00e1vat +dialog.rearrange.sortbyfilename=Uspo\u0159\u00e1dat dle n\u00e1zvu souboru +dialog.rearrange.sortbyname=Uspo\u0159\u00e1dat dle n\u00e1zvu +dialog.rearrange.sortbytime=Uspo\u0159\u00e1dat dle \u010dasu dialog.compress.closepoints.title=Odstran\u011bn\u00ed bl\u00edzk\u00fdch bod\u016f dialog.compress.closepoints.paramdesc=Koeficient bl\u00edzkosti dialog.compress.wackypoints.title=Odstran\u011bn\u00ed ust\u0159elen\u00fdch bod\u016f @@ -849,7 +848,6 @@ 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. error.cache.empty=Adres\u00e1\u0159 s cache map je pr\u00e1zdn\u00fd. error.cache.cannotdelete=Nelze smazat soubory map. -error.interpolate.invalidparameter=Po\u010det bod\u016f mus\u00ed b\u00fdt mezi 1 a 1000 error.learnestimationparams.failed=Na z\u00e1klad\u011b t\u00e9to stopy nelze vypo\u010d\u00edtat parametr.\nZkuste jin\u00e9 stopy. error.tracksplit.nosplit=Stopu nen\u00ed mo\u017en\u00e9 rozd\u011blit error.downloadsrtm.nocache=Nepoda\u0159ilo se ulo\u017eit soubory.\nPros\u00edm ov\u011b\u0159te diskovou cache. diff --git a/tim/prune/lang/prune-texts_da.properties b/tim/prune/lang/prune-texts_da.properties index ea394b9..8bcf9ca 100644 --- a/tim/prune/lang/prune-texts_da.properties +++ b/tim/prune/lang/prune-texts_da.properties @@ -10,8 +10,8 @@ menu.file.exit=Afslut menu.track.undo=Fortryd menu.track.clearundo=Nulstil fortrydelsesliste menu.track.deletemarked=Slet markerede punkter -menu.track.rearrange=Omorganis\u00e9r waypoints -menu.track.rearrange.nearest=Hvert waypoint til n\u00e6rmeste nabo +function.rearrangewaypoints=Omorganis\u00e9r waypoints +dialog.rearrange.tonearest=Hvert waypoint til n\u00e6rmeste nabo menu.range=Omr\u00e5de menu.range.all=V\u00e6lg alle menu.range.none=Ingen valgt diff --git a/tim/prune/lang/prune-texts_de.properties b/tim/prune/lang/prune-texts_de.properties index 19b6b02..a85515e 100644 --- a/tim/prune/lang/prune-texts_de.properties +++ b/tim/prune/lang/prune-texts_de.properties @@ -13,10 +13,7 @@ menu.track.undo=R\u00fcckg\u00e4ngig menu.track.clearundo=Liste der letzten \u00c4nderungen l\u00f6schen menu.track.markrectangle=Punkte im Viereck markieren menu.track.deletemarked=Markierte Punkte l\u00f6schen -menu.track.rearrange=Wegpunkte neu anordnen -menu.track.rearrange.start=Alle Wegpunkte zum Anfang -menu.track.rearrange.end=Alle Wegpunkte ans Ende -menu.track.rearrange.nearest=Jeden Wegpunkt zum n\u00e4chsten Trackpunkt verschieben +function.rearrangewaypoints=Wegpunkte neu anordnen menu.range=Bereich menu.range.all=Alles markieren menu.range.none=Nichts markieren @@ -87,11 +84,12 @@ function.exportgpx=GPX exportieren function.exportpov=POV exportieren function.exportsvg=SVG exportieren function.exportimage=Bild exportieren -function.editwaypointname=Name des Punkts bearbeiten +function.editwaypointname=Namen des Punkts bearbeiten function.compress=Track komprimieren function.deleterange=Bereich l\u00f6schen function.croptrack=Track zuschneiden function.interpolate=Punkte interpolieren +function.deletebydate=Punkte nach Datum l\u00f6schen function.addtimeoffset=Zeitverschiebung aufrechnen function.addaltitudeoffset=H\u00f6henverschiebung aufrechnen function.convertnamestotimes=Namen der Wegpunkte in Zeitstempel umwandeln @@ -106,6 +104,7 @@ function.estimatetime=Zeit absch\u00e4tzen function.learnestimationparams=Zeitparameter erlernen function.setmapbg=Karte Hintergrund setzen function.setpaths=Programmpfade setzen +function.selectsegment=Aktuellen Abschnitt markieren function.splitsegments=In Trackabschnitte schneiden function.sewsegments=Trackabschnitte zusammenf\u00fcgen function.getgpsies=Tracks bei GPSies.com herunterladen @@ -141,6 +140,7 @@ function.saveconfig=Einstellungen speichern function.diskcache=Karten auf Festplatte speichern function.managetilecache=Kartenkacheln verwalten function.getweatherforecast=Wettervorhersage herunterladen +function.setaltitudetolerance=Altitudtoleranz einstellen # Dialogs dialog.exit.confirm.title=GpsPrune beenden @@ -379,6 +379,7 @@ dialog.gpsies.activity.sailing=Segeln dialog.gpsies.activity.skating=Inline-Skating dialog.wikipedia.column.name=Artikelname dialog.wikipedia.column.distance=Entfernung +dialog.wikipedia.nonefound=Keine Punkte gefunden dialog.correlate.notimestamps=Die Punkte enthalten keine Zeitangaben, deshalb k\u00f6nnen die Fotos nicht zugeordnet werden. dialog.correlate.nouncorrelatedphotos=Alle Fotos sind schon zugeordnet.\nWollen Sie trotzdem fortfahren? dialog.correlate.nouncorrelatedaudios=Alle Audiodateien sind schon zugeordnet.\nWollen Sie trotzdem fortfahren? @@ -412,12 +413,15 @@ dialog.correlate.timestamp.end=Ende dialog.correlate.audioselect.intro=W\u00e4hlen Sie eines dieser Audiodateien aus, um die Zeitdifferenz zu berechnen dialog.correlate.select.audioname=Audio Name dialog.correlate.select.audiolater=Audio sp\u00e4ter +dialog.rearrangewaypoints.desc=Setzen Sie das Ziel und die Reihenfolge der Wegpunkte dialog.rearrangephotos.desc=Setzen Sie das Ziel und die Reihenfolge der Fotopunkte -dialog.rearrangephotos.tostart=Am Anfang -dialog.rearrangephotos.toend=Am Ende -dialog.rearrangephotos.nosort=Nicht sortieren -dialog.rearrangephotos.sortbyfilename=per Dateiname sortieren -dialog.rearrangephotos.sortbytime=per Zeitstempel sortieren +dialog.rearrange.tostart=Zum Anfang +dialog.rearrange.toend=Zum Ende +dialog.rearrange.tonearest=Zum n\u00e4chsten Trackpunkt +dialog.rearrange.nosort=Nicht sortieren +dialog.rearrange.sortbyfilename=nach Dateinamen sortieren +dialog.rearrange.sortbyname=nach Namen sortieren +dialog.rearrange.sortbytime=nach Zeitstempel sortieren dialog.compress.closepoints.title=Nahegelegene Punkte entfernen dialog.compress.closepoints.paramdesc=Span-Faktor dialog.compress.wackypoints.title=Ungew\u00f6hnliche Punkte entfernen @@ -475,7 +479,7 @@ dialog.checkversion.releasedate1=Diese neue Version ist am dialog.checkversion.releasedate2=ver\u00f6ffentlicht worden. dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://gpsprune.activityworkshop.net/download.html. dialog.keys.intro=Anstelle der Maus k\u00f6nnen Sie folgende Tastenkombinationen nutzen -dialog.keys.keylist=
Pfeil TastenKarte verschieben
Strg + Links-, Rechts-PfeilVorherigen oder n\u00e4chsten Punkt markieren
Strg + Auf-, Abw\u00e4rts-PfeilEin- oder Auszoomen
Strg + Bild auf, abVorheriges oder n\u00e4chstes Segment markieren
Strg + Pos1, EndeErsten oder letzten Punkt markieren
EntfAktuellen Punkt entfernen
+dialog.keys.keylist=
Pfeil TastenKarte verschieben
Strg + Links-, Rechts-PfeilVorherigen oder n\u00e4chsten Punkt markieren
Strg + Auf-, Abw\u00e4rts-PfeilEin- oder Auszoomen
Strg + Bild auf, abZum vorherigen oder n\u00e4chsten Abschnitt
Strg + Pos1, EndeErsten oder letzten Punkt markieren
EntfAktuellen Punkt entfernen
dialog.keys.normalmodifier=Strg dialog.keys.macmodifier=Kommando dialog.saveconfig.desc=Die folgende Einstellungen k\u00f6nnen gespeichert werden: @@ -515,6 +519,19 @@ dialog.colourchooser.title=Farbe ausw\u00e4hlen dialog.colourchooser.red=Rot dialog.colourchooser.green=Gr\u00fcn dialog.colourchooser.blue=Blau +dialog.colourer.intro=F\u00e4rb die Trackpunkte unterschiedlich ein +dialog.colourer.type=Unterschiedliche Farben +dialog.colourer.type.none=Keine +dialog.colourer.type.byfile=Nach Datei +dialog.colourer.type.bysegment=Nach Abschnitt +dialog.colourer.type.byaltitude=Nach H\u00f6henangaben +dialog.colourer.type.byspeed=Nach Geschwindigkeit +dialog.colourer.type.byvertspeed=Nach vertikalen Geschwindigkeit +dialog.colourer.type.bygradient=Nach Gef\u00e4lle +dialog.colourer.type.bydate=Nach Datum +dialog.colourer.start=Anfangsfarbe +dialog.colourer.end=Zielfarbe +dialog.colourer.maxcolours=Maximal Anzahl Farben dialog.setlanguage.firstintro=Sie k\u00f6nnen entweder eine von den mitgelieferten Sprachen

oder eine Text-Datei ausw\u00e4hlen. dialog.setlanguage.secondintro=Sie m\u00fcssen Ihre Einstellungen speichern und dann

GpsPrune neu starten, um die Sprache zu \u00e4ndern. dialog.setlanguage.language=Sprache @@ -564,6 +581,13 @@ dialog.weather.wind=Wind dialog.weather.temp=Temp dialog.weather.humidity=R.L. dialog.weather.creditnotice=Diese Daten wurden von openweathermap.org zur Verf\u00fcgung gestellt. Die Webseite hat mehr Information. +dialog.deletebydate.onlyonedate=Die Punkte fallen alle am gleichen Tag. +dialog.deletebydate.intro=F\u00fcr jeden gefundenen Datum, k\u00f6nnen Sie die Punkte behalten oder l\u00f6schen. +dialog.deletebydate.nodate=Ohne Zeitangabe +dialog.deletebydate.column.keep=Behalten +dialog.deletebydate.column.delete=L\u00f6schen +dialog.setaltitudetolerance.text.metres=Mindestabweichung (Meter) f\u00fcr H\u00f6henunterschiede +dialog.setaltitudetolerance.text.feet=Mindestabweichung (Feet) f\u00fcr H\u00f6henunterschiede # 3d window dialog.3d.title=GpsPrune-3D-Ansicht @@ -718,9 +742,10 @@ fieldname.longitude=L\u00e4ngengrad fieldname.altitude=H\u00f6he fieldname.timestamp=Zeitstempel fieldname.time=Zeit +fieldname.date=Datum fieldname.waypointname=Name fieldname.waypointtype=Typ -fieldname.newsegment=Segment +fieldname.newsegment=Abschnitt fieldname.custom=Custom fieldname.prefix=Feld fieldname.distance=L\u00e4nge @@ -846,7 +871,6 @@ error.playaudiofailed=Das Abspielen der Audiodatei ist fehlgeschlagen error.cache.notthere=Der Ordner wurde nicht gefunden error.cache.empty=Der Ordner ist leer error.cache.cannotdelete=Es konnte keine Kacheln gel\u00f6scht werden -error.interpolate.invalidparameter=Die Anzahl der Punkte muss zwischen 1 und 1000 liegen error.learnestimationparams.failed=Mit diesem Track k\u00f6nnen die Parameter nicht berechnet werden.\nVersuchen Sie mit mehreren Tracks. error.tracksplit.nosplit=Der Track konnte nicht aufgesplittet werden. error.downloadsrtm.nocache=Die Dateien konnten nicht gespeichert werden.\nBitte pr\u00fcfen Sie den Kartenordner nach. diff --git a/tim/prune/lang/prune-texts_de_CH.properties b/tim/prune/lang/prune-texts_de_CH.properties index 7e9b304..a0bad3e 100644 --- a/tim/prune/lang/prune-texts_de_CH.properties +++ b/tim/prune/lang/prune-texts_de_CH.properties @@ -13,10 +13,7 @@ menu.track.undo=Undo menu.track.clearundo=Undo-Liste l\u00f6sche menu.track.markrectangle=P\u00fcnkte inem Viereck markiere menu.track.deletemarked=Markierte P\u00fcnkte l\u00f6sche -menu.track.rearrange=Waypoints reorganisiere -menu.track.rearrange.start=Alli zum Aafang -menu.track.rearrange.end=Alli zum \u00c4nde -menu.track.rearrange.nearest=Jede zum n\u00f6chsti Trackpunkt +function.rearrangewaypoints=Waypoints reorganisiere menu.range=Beriich menu.range.all=Alles selektiere menu.range.none=N\u00fc\u00fct selektiere @@ -86,10 +83,11 @@ function.exportgpx=GPX exportier\u00e4 function.exportpov=POV exportier\u00e4 function.exportsvg=SVG exportier\u00e4 function.exportimage=Bild exportier\u00e4 -function.editwaypointname=Waypoint Name editiere +function.editwaypointname=Waypoint Namen editiere function.compress=Track komprimier\u00e4 function.deleterange=Beriich l\u00f6sche function.croptrack=Track zuschniide +function.deletebydate=P\u00fcnkte na Datum l\u00f6sche function.addtimeoffset=Ziitverschiebig zutue function.addaltitudeoffset=H\u00f6chiverschiebig zutue function.findwaypoint=Waypoint suech\u00e4 @@ -103,6 +101,7 @@ function.fullrangedetails=Zues\u00e4tzlichi Beriichinfos function.estimatetime=Ziit absch\u00e4tze function.learnestimationparams=Ziitparameter erlerne function.setmapbg=Karte Hintegrund setz\u00e4 +function.selectsegment=Aktuelli Segm\u00e4nt selektiere function.splitsegments=In Tracksegm\u00e4nte schniide function.sewsegments=Tracksegm\u00e4nte z\u00e4mef\u00fcge function.getgpsies=Gpsies Tracks hol\u00e4 @@ -136,6 +135,7 @@ function.saveconfig=Iistellige speichere function.diskcache=Karten uufem Disk speichere function.managetilecache=Kartebildli verwolte function.getweatherforecast=W\u00e4tterprognose abalade +function.setaltitudetolerance=H\u00f6chitoleranz iistelle # Dialogs dialog.exit.confirm.title=GpsPrune be\u00e4nde @@ -162,8 +162,8 @@ dialog.openoptions.deliminfo.records=Rekords, mit dialog.openoptions.deliminfo.fields=F\u00e4ldere dialog.openoptions.deliminfo.norecords=Kei Rekords dialog.openoptions.altitudeunits=H\u00f6chi Masseiheite -dialog.openoptions.speedunits=Masseiheite f\u00fcr Geschwindigkeite -dialog.openoptions.vertspeedunits=Masseiheite f\u00fcr vertikale Geschwindigkeite +dialog.openoptions.speedunits=Masseiheite f\u00fcr Gschwindikeite +dialog.openoptions.vertspeedunits=Masseiheite f\u00fcr vertikale Gschwindikeite dialog.openoptions.vspeed.positiveup=Positive bed\u00fc\u00fctet uufe dialog.openoptions.vspeed.positivedown=Positive bed\u00fc\u00fctet abe dialog.open.contentsdoubled=Dieses File h\u00e4t zwei Kopien von j\u00e4dem Punkt,\neimol als Waypoint und eimol als Trackpunkt. @@ -374,6 +374,7 @@ dialog.gpsies.activity.sailing=Segle dialog.gpsies.activity.skating=Inline-Skate dialog.wikipedia.column.name=Artikelname dialog.wikipedia.column.distance=Entf\u00e4rnig +dialog.wikipedia.nonefound=Kei Wiki-Iitr\u00e4ge gfunde dialog.correlate.notimestamps=Es h\u00e4t kei Ziitst\u00e4mpel inem Track inn\u00e4, so s'isch n\u00f6d m\u00f6glech die F\u00f6telis zu korrelier\u00e4. dialog.correlate.nouncorrelatedphotos=Alle F\u00f6telis sin scho korreliert.\nWend Sie trotzdem fortsetz\u00e4? dialog.correlate.nouncorrelatedaudios=Alle Audios sin scho korreliert.\nWend Sie trotzdem fortsetz\u00e4? @@ -407,12 +408,15 @@ dialog.correlate.timestamp.end=\u00c4nde dialog.correlate.audioselect.intro=W\u00e4hlet Sie eini vo deren Audios uus, um die Ziitdiffer\u00e4nz zu ber\u00e4chn\u00e4 dialog.correlate.select.audioname=Audio Name dialog.correlate.select.audiolater=Audio sp\u00f6ter -dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von d P\u00fcnkte setze -dialog.rearrangephotos.tostart=zum Aafang -dialog.rearrangephotos.toend=zum \u00c4nde -dialog.rearrangephotos.nosort=N\u00f6d sortiere -dialog.rearrangephotos.sortbyfilename=per Filename sortiere -dialog.rearrangephotos.sortbytime=per Ziit sortiere +dialog.rearrangewaypoints.desc=Bitte Ziel und Reihefolge von d Waypoints setze +dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von d F\u00f6telip\u00fcnkte setze +dialog.rearrange.tostart=zum Aafang +dialog.rearrange.toend=zum \u00c4nde +dialog.rearrange.tonearest=zum n\u00f6chsti Trackpunkt +dialog.rearrange.nosort=N\u00f6d sortiere +dialog.rearrange.sortbyfilename=nachem Filename sortiere +dialog.rearrange.sortbyname=nachem Name sortiere +dialog.rearrange.sortbytime=nachdr Ziit sortiere dialog.compress.duplicates.title=Duplikate entf\u00e4rn\u00e4 dialog.compress.closepoints.title=N\u00f6chigl\u00e4geni P\u00fcnkte entf\u00e4rn\u00e4 dialog.compress.closepoints.paramdesc=Span Faktor @@ -510,6 +514,19 @@ dialog.colourchooser.title=Farbe uusw\u00e4hle dialog.colourchooser.red=Rot dialog.colourchooser.green=Gr\u00fcen dialog.colourchooser.blue=Blau +dialog.colourer.intro=F\u00e4rb die Trackp\u00fcnkte unterschiedlich ii +dialog.colourer.type=Unterschiedliche Farbe +dialog.colourer.type.none=Kei +dialog.colourer.type.byfile=Nach Datei +dialog.colourer.type.bysegment=Nach Segm\u00e4nt +dialog.colourer.type.byaltitude=Nach H\u00f6chi +dialog.colourer.type.byspeed=Nach Gschwindikeit +dialog.colourer.type.byvertspeed=Nach uf/ab Gschwindikeit +dialog.colourer.type.bygradient=Nach Gef\u00e4lle +dialog.colourer.type.bydate=Nach Datum +dialog.colourer.start=Aafangsfarb +dialog.colourer.end=Zielfarb +dialog.colourer.maxcolours=Maximal Anzahl Farbe dialog.setlanguage.firstintro=Sie k\u00f6nnet entweder eini vo den iigebouti Sproche

oder ne Text-Datei uusw\u00e4hle. dialog.setlanguage.secondintro=Sie m\u00fcnt Ihri Iistellige speichere und dann

GpsPrune wieder neustarte um die Sproch z'\u00e4ndere. dialog.setlanguage.language=Sproch @@ -559,6 +576,13 @@ dialog.weather.wind=Wind dialog.weather.temp=Temp dialog.weather.humidity=R.L. dialog.weather.creditnotice=Diese Date sin vo openweathermap.org zur Verf\u00fcegig gestellt worde. Uf d Websiite h\u00e4ts no meh Infos. +dialog.deletebydate.onlyonedate=Die P\u00fcnkte sin alli vom gliichen Tag. +dialog.deletebydate.intro=F\u00fcr jeden Tag, k\u00f6nnet Sie d P\u00fcnkte behalte oder l\u00f6sche. +dialog.deletebydate.nodate=Ohni Ziitaagab +dialog.deletebydate.column.keep=Behalte +dialog.deletebydate.column.delete=L\u00f6sche +dialog.setaltitudetolerance.text.metres=Mindeschtabweichig (Meter) f\u00fcr H\u00f6chiunterschied +dialog.setaltitudetolerance.text.feet=Mindeschtabweichig (Feet) f\u00fcr H\u00f6chinunterschied # 3d window dialog.3d.title=GpsPrune Dr\u00fc\u00fc-d Aasicht @@ -612,7 +636,7 @@ tip.useamapcache=Mit lokali Kartekachle (Iistellige -> Karten uufem Disk speiche tip.learntimeparams=Wenn Sie Track -> Ziitparameter erlerne mit Ihren Tracks benutze\ndann werdet d ber\u00e4chneti Werte gnauer. tip.downloadsrtm=Sie k\u00f6nnet d Funktion beschleunige indem Sie\nOnline -> SRTM Files abalade uufrufe\num d Date lokal z'speichere. tip.usesrtmfor3d=Dere Track h\u00e4t kei H\u00f6chiinformation.\nSie k\u00f6nnet d SRTM Funktione verw\u00e4nde, um\nH\u00f6chiwerte abz'sch\u00e4tze. -tip.manuallycorrelateone=Mit mindeschtens einem verbundenen Elem\u00e4nt kann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4. +tip.manuallycorrelateone=Mit mindeschtens einem verbundenen Elem\u00e4nt chann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4. # Buttons button.ok=OK @@ -713,6 +737,7 @@ fieldname.longitude=L\u00e4ngegrad fieldname.altitude=H\u00f6chi fieldname.timestamp=Ziitst\u00e4mpel fieldname.time=Ziit +fieldname.date=Tag fieldname.waypointname=Name fieldname.waypointtype=Typ fieldname.newsegment=Segm\u00e4nt @@ -841,7 +866,6 @@ error.playaudiofailed=S Abschpiele vonem File isch fehlgschlage error.cache.notthere=D Ordner isch n\u00f6d gfunde worde error.cache.empty=D Ordner h\u00e4t n\u00fc\u00fct drinne error.cache.cannotdelete=Es sin kei Kachle gl\u00f6scht worde -error.interpolate.invalidparameter=D'Aazahl P\u00fcnkt muess zw\u00fcschet 1 und 1000 sii error.learnestimationparams.failed=Mit dere Track k\u00f6nnet die Parameter n\u00f6d br\u00e4chnet werde.\nVersuechet Sie mit mehreri Tracks. error.tracksplit.nosplit=Es isch n\u00f6d m\u00f6glech gsi, den Track uufz'schniide. error.downloadsrtm.nocache=Die Files k\u00f6nnet n\u00f6d gspeicheret werde.\nBitte pr\u00fcefet Sie den Kartenordner na. diff --git a/tim/prune/lang/prune-texts_en.properties b/tim/prune/lang/prune-texts_en.properties index bfef0df..447ce4d 100644 --- a/tim/prune/lang/prune-texts_en.properties +++ b/tim/prune/lang/prune-texts_en.properties @@ -13,10 +13,6 @@ menu.track.undo=Undo menu.track.clearundo=Clear undo list menu.track.markrectangle=Mark points in rectangle menu.track.deletemarked=Delete marked points -menu.track.rearrange=Rearrange waypoints -menu.track.rearrange.start=All to start of file -menu.track.rearrange.end=All to end of file -menu.track.rearrange.nearest=Each to nearest track point menu.range=Range menu.range.all=Select all menu.range.none=Select none @@ -92,9 +88,11 @@ function.compress=Compress track function.deleterange=Delete range function.croptrack=Crop track function.interpolate=Interpolate points +function.deletebydate=Delete points by date function.addtimeoffset=Add time offset function.addaltitudeoffset=Add altitude offset 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 @@ -104,6 +102,7 @@ function.distances=Distances function.fullrangedetails=Full range details function.estimatetime=Estimate time function.learnestimationparams=Learn time estimation parameters +function.selectsegment=Select current segment function.splitsegments=Split track into segments function.sewsegments=Sew track segments together function.getgpsies=Get Gpsies tracks @@ -141,6 +140,7 @@ function.saveconfig=Save settings function.diskcache=Save maps to disk function.managetilecache=Manage tile cache function.getweatherforecast=Get weather forecast +function.setaltitudetolerance=Set altitude tolerance # Dialogs dialog.exit.confirm.title=Exit GpsPrune @@ -218,7 +218,7 @@ dialog.save.table.field=Field dialog.save.table.hasdata=Has data dialog.save.table.save=Save dialog.save.headerrow=Output header row -dialog.save.coordinateunits=Coordinate units +dialog.save.coordinateunits=Coordinate format dialog.save.altitudeunits=Altitude units dialog.save.timestampformat=Timestamp format dialog.save.overwrite.title=File already exists @@ -379,6 +379,7 @@ dialog.gpsies.activity.sailing=Sailing dialog.gpsies.activity.skating=Skating dialog.wikipedia.column.name=Article name dialog.wikipedia.column.distance=Distance +dialog.wikipedia.nonefound=No wikipedia entries found dialog.correlate.notimestamps=There are no timestamps in the data points, so there is nothing to correlate with the photos. dialog.correlate.nouncorrelatedphotos=There are no uncorrelated photos.\nAre you sure you want to continue? dialog.correlate.nouncorrelatedaudios=There are no uncorrelated audios.\nAre you sure you want to continue? @@ -412,12 +413,15 @@ dialog.correlate.timestamp.end=End dialog.correlate.audioselect.intro=Select one of these correlated audios to use as the time offset dialog.correlate.select.audioname=Audio name dialog.correlate.select.audiolater=Audio later +dialog.rearrangewaypoints.desc=Select the destination and sort order of the waypoints dialog.rearrangephotos.desc=Select the destination and sort order of the photo points -dialog.rearrangephotos.tostart=Move to start -dialog.rearrangephotos.toend=Move to end -dialog.rearrangephotos.nosort=Don't sort -dialog.rearrangephotos.sortbyfilename=Sort by filename -dialog.rearrangephotos.sortbytime=Sort by time +dialog.rearrange.tostart=Move to start +dialog.rearrange.toend=Move to end +dialog.rearrange.tonearest=Each to nearest track point +dialog.rearrange.nosort=Don't sort +dialog.rearrange.sortbyfilename=Sort by filename +dialog.rearrange.sortbyname=Sort by name +dialog.rearrange.sortbytime=Sort by time dialog.compress.duplicates.title=Duplicate removal dialog.compress.closepoints.title=Nearby point removal dialog.compress.closepoints.paramdesc=Span factor @@ -515,6 +519,19 @@ dialog.colourchooser.title=Choose colour dialog.colourchooser.red=Red dialog.colourchooser.green=Green dialog.colourchooser.blue=Blue +dialog.colourer.intro=A point colourer can give track points different colours +dialog.colourer.type=Colourer type +dialog.colourer.type.none=None +dialog.colourer.type.byfile=By file +dialog.colourer.type.bysegment=By segment +dialog.colourer.type.byaltitude=By altitude +dialog.colourer.type.byspeed=By speed +dialog.colourer.type.byvertspeed=By vertical speed +dialog.colourer.type.bygradient=By gradient +dialog.colourer.type.bydate=By date +dialog.colourer.start=Start colour +dialog.colourer.end=End colour +dialog.colourer.maxcolours=Maximum number of colours dialog.setlanguage.firstintro=You can either select one of the included languages,

or select a text file to use instead. dialog.setlanguage.secondintro=You need to save your settings and then

restart GpsPrune to change the language. dialog.setlanguage.language=Language @@ -564,6 +581,13 @@ dialog.weather.wind=Wind dialog.weather.temp=Temp dialog.weather.humidity=Humidity dialog.weather.creditnotice=This data is made available by openweathermap.org. Their website has more details. +dialog.deletebydate.onlyonedate=The points were all recorded on the same date. +dialog.deletebydate.intro=For each date in the track, you can choose to delete or keep the points +dialog.deletebydate.nodate=No timestamp +dialog.deletebydate.column.keep=Keep +dialog.deletebydate.column.delete=Delete +dialog.setaltitudetolerance.text.metres=Limit (in metres) below which small climbs and descents will be ignored +dialog.setaltitudetolerance.text.feet=Limit (in feet) below which small climbs and descents will be ignored # 3d window dialog.3d.title=GpsPrune Three-d view @@ -718,6 +742,7 @@ fieldname.longitude=Longitude fieldname.altitude=Altitude fieldname.timestamp=Time fieldname.time=Time +fieldname.date=Date fieldname.waypointname=Name fieldname.waypointtype=Type fieldname.newsegment=Segment @@ -852,7 +877,6 @@ error.playaudiofailed=Failed to play audio clip error.cache.notthere=The tile cache directory was not found error.cache.empty=The tile cache directory is empty error.cache.cannotdelete=No tiles could be deleted -error.interpolate.invalidparameter=The number of points must be between 1 and 1000 error.learnestimationparams.failed=Cannot learn the parameters from this track.\nTry loading more tracks. error.tracksplit.nosplit=The track could not be split error.downloadsrtm.nocache=The files could not be saved.\nPlease check the disk cache. diff --git a/tim/prune/lang/prune-texts_en_US.properties b/tim/prune/lang/prune-texts_en_US.properties index a28dd50..d6724ab 100644 --- a/tim/prune/lang/prune-texts_en_US.properties +++ b/tim/prune/lang/prune-texts_en_US.properties @@ -7,8 +7,16 @@ 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 +dialog.colourer.type=Colorer type +dialog.colourer.start=Start color +dialog.colourer.end=End color +dialog.colourer.maxcolours=Maximum number of colors +dialog.setaltitudetolerance.text.metres=Limit (in meters) below which small climbs and descents will be ignored # Measurement units units.metres=Meters diff --git a/tim/prune/lang/prune-texts_es.properties b/tim/prune/lang/prune-texts_es.properties index b752c7c..63901b2 100644 --- a/tim/prune/lang/prune-texts_es.properties +++ b/tim/prune/lang/prune-texts_es.properties @@ -13,10 +13,7 @@ menu.track.undo=Deshacer menu.track.clearundo=Despejar la lista de deshacer menu.track.markrectangle=Marcar puntos dentro de un rect\u00e1ngulo menu.track.deletemarked=Eliminar puntos marcados -menu.track.rearrange=Reorganizar waypoints -menu.track.rearrange.start=Volver al comienzo -menu.track.rearrange.end=Ir al final -menu.track.rearrange.nearest=Ir al m\u00e1s pr\u00f3ximo +function.rearrangewaypoints=Reorganizar waypoints menu.range=Rango menu.range.all=Seleccionar todo menu.range.none=No seleccionar nada @@ -92,6 +89,7 @@ function.compress=Comprimir track function.deleterange=Eliminar rango function.croptrack=Truncar track function.interpolate=Interpolar puntos +function.deletebydate=Eliminar puntos de acuerdo a la fecha function.addtimeoffset=A\u00f1adir compensar tiempo function.addaltitudeoffset=A\u00f1adir compensar altitud function.convertnamestotimes=Convertir los nombres de los "waypoints" a tiempo @@ -105,6 +103,7 @@ function.fullrangedetails=Detalles adicionales de rango function.estimatetime=Estimar duraci\u00f3n function.setmapbg=Configurar fondo de mapa 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 @@ -363,6 +362,7 @@ dialog.gpsies.activity.sailing=Vela dialog.gpsies.activity.skating=Patinaje dialog.wikipedia.column.name=Nombre del art\u00edculo dialog.wikipedia.column.distance=Distancia +dialog.wikipedia.nonefound=No se encontraron puntos dialog.correlate.notimestamps=No hay informaci\u00f3n de tiempo para los puntos, as\u00ed que no hay nada que correlacionar con las fotos. dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar? dialog.correlate.nouncorrelatedaudios=No hay audios no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar? @@ -397,11 +397,13 @@ dialog.correlate.audioselect.intro=Seleccione uno de estos audios correlacionado dialog.correlate.select.audioname=Nombre del audio dialog.correlate.select.audiolater=Audio m\u00e1s adelante dialog.rearrangephotos.desc=Seleccionar el destino y sortear el orden de los puntos de las fotos -dialog.rearrangephotos.tostart=Mover al comienzo -dialog.rearrangephotos.toend=Mover al final -dialog.rearrangephotos.nosort=No sortear -dialog.rearrangephotos.sortbyfilename=Sortear por nombre del archivo -dialog.rearrangephotos.sortbytime=Sortear por tiempo +dialog.rearrange.tostart=Mover al comienzo +dialog.rearrange.toend=Mover al final +dialog.rearrange.tonearest=Mover al punto m\u00e1s pr\u00f3ximo +dialog.rearrange.nosort=No sortear +dialog.rearrange.sortbyfilename=Sortear por nombre del archivo +dialog.rearrange.sortbyname=Sortear por nombre +dialog.rearrange.sortbytime=Sortear por tiempo dialog.compress.closepoints.title=remover puntos cercanos dialog.compress.closepoints.paramdesc=Factor de extensi\u00f3n dialog.compress.wackypoints.title=Eliminar puntos an\u00f3malos @@ -499,6 +501,8 @@ dialog.colourchooser.title=Elegir color dialog.colourchooser.red=Rojo dialog.colourchooser.green=Verde dialog.colourchooser.blue=Azul +dialog.colourer.start=Color de inicio +dialog.colourer.end=Color final dialog.setlanguage.firstintro=Puede usted seleccionar algunos de los lenguajes incluidos,

o puede en lugar de esto seleccionar un archivo de texto dialog.setlanguage.secondintro=Usted necesita guardar sus preferencias y luego

reiniciar GpsPrune para cambiar el lenguaje dialog.setlanguage.language=Lenguaje @@ -546,6 +550,9 @@ dialog.weather.day.sunday=Domingo dialog.weather.wind=Viento dialog.weather.temp=Temp dialog.weather.humidity=Humedad +dialog.deletebydate.nodate=Sin marcas de tiempo +dialog.deletebydate.column.keep=Mantener +dialog.deletebydate.column.delete=Eliminar # 3d window dialog.3d.title=GpsPrune vista 3-D @@ -693,6 +700,7 @@ fieldname.longitude=Longitud fieldname.altitude=Altitud fieldname.timestamp=Informaci\u00f3n de tiempo fieldname.time=Tiempo +fieldname.date=Data fieldname.waypointname=Nombre fieldname.waypointtype=Tipo fieldname.newsegment=Segmento @@ -827,7 +835,6 @@ 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.interpolate.invalidparameter=El n\u00famero de puntos necesita ser entre 1 y 1000 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. diff --git a/tim/prune/lang/prune-texts_fa.properties b/tim/prune/lang/prune-texts_fa.properties index 960330d..a70545b 100644 --- a/tim/prune/lang/prune-texts_fa.properties +++ b/tim/prune/lang/prune-texts_fa.properties @@ -10,10 +10,10 @@ menu.track=\u0645\u0633\u064a\u0631 menu.track.undo=\u0628\u0627\u0637\u0644 \u06a9\u0631\u062f\u0646 \u06a9\u0627\u0631 \u0642\u0628\u0644\u064a menu.track.clearundo=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0644\u064a\u0633\u062a \u06a9\u0627\u0631\u0647\u0627\u06cc \u0627\u0646\u062c\u0627\u0645 \u0634\u062f\u0647 menu.track.deletemarked=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0646\u0642\u0627\u0637 \u0627\u0646\u062a\u062e\u0627\u0628 \u0634\u062f\u0647 -menu.track.rearrange=\u0628\u0627\u0632 \u0686\u064a\u0646\u06cc \u0646\u0642\u0627\u0637 \u0645\u0633\u064a\u0631 -menu.track.rearrange.start=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0628\u062a\u062f\u0627\u06cc \u0645\u0633\u064a\u0631 -menu.track.rearrange.end=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0646\u062a\u0647\u0627\u06cc \u0645\u0633\u064a\u0631 -menu.track.rearrange.nearest=\u0647\u0631 \u064a\u06a9 \u0628\u0647 \u0646\u0632\u062f\u064a\u06a9 \u0646\u0642\u0637\u0647 \u0645\u0633\u064a\u0631 +function.rearrangewaypoints=\u0628\u0627\u0632 \u0686\u064a\u0646\u06cc \u0646\u0642\u0627\u0637 \u0645\u0633\u064a\u0631 +dialog.rearrange.tostart=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0628\u062a\u062f\u0627\u06cc \u0645\u0633\u064a\u0631 +dialog.rearrange.toend=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0646\u062a\u0647\u0627\u06cc \u0645\u0633\u064a\u0631 +dialog.rearrange.tonearest=\u0647\u0631 \u064a\u06a9 \u0628\u0647 \u0646\u0632\u062f\u064a\u06a9 \u0646\u0642\u0637\u0647 \u0645\u0633\u064a\u0631 menu.range=\u0686\u064a\u0646\u0634 menu.range.all=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u0645\u0647 \u0646\u0642\u0627\u0637 menu.range.none=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u064a\u0686 \u064a\u06a9 \u0627\u0632 \u0646\u0642\u0627\u0637 diff --git a/tim/prune/lang/prune-texts_fr.properties b/tim/prune/lang/prune-texts_fr.properties index d271c27..0e25497 100644 --- a/tim/prune/lang/prune-texts_fr.properties +++ b/tim/prune/lang/prune-texts_fr.properties @@ -13,10 +13,7 @@ menu.track.undo=Annuler menu.track.clearundo=Purger la liste d'annulation menu.track.markrectangle=S\u00e9lectionner les points dans un rectangle menu.track.deletemarked=Supprimer les points marqu\u00e9s -menu.track.rearrange=R\u00e9arranger les waypoints -menu.track.rearrange.start=Tous au d\u00e9but du fichier -menu.track.rearrange.end=Tous \u00e0 la fin du fichier -menu.track.rearrange.nearest=Chacun au point de trace le plus proche +function.rearrangewaypoints=R\u00e9arranger les waypoints menu.range=\u00c9tendue menu.range.all=Tout s\u00e9lectionner menu.range.none=Rien s\u00e9lectionner @@ -103,6 +100,7 @@ function.show3d=Montrer en 3D function.distances=Distances function.fullrangedetails=Montrer tous les d\u00e9tails function.estimatetime=Temps estim\u00e9 +function.learnestimationparams=Apprendre les param\u00e8tres pour l'estimation function.setmapbg=D\u00e9finir le fond de carte function.setpaths=D\u00e9finir les chemins des programmes function.splitsegments=S\u00e9pare les segments @@ -360,6 +358,7 @@ dialog.gpsies.activity.sailing=Volle dialog.gpsies.activity.skating=Skating dialog.wikipedia.column.name=Nom de l'article dialog.wikipedia.column.distance=Distance +dialog.wikipedia.nonefound=Aucune points trouv\u00e9e dialog.correlate.notimestamps=Les points n'ont pas d'indication de temps, il n'est pas possible de les corr\u00e9ler. dialog.correlate.nouncorrelatedphotos=Il n'y a pas de photos non-corr\u00e9l\u00e9es.\nVoulez-vous continuer ? dialog.correlate.nouncorrelatedaudios=Il n'y a pas d'audios non-corr\u00e9l\u00e9s.\nVoulez-vous continuer ? @@ -394,11 +393,13 @@ dialog.correlate.audioselect.intro=Choisir un de ces fichiers audio corr\u00e9l\ dialog.correlate.select.audioname=Nom du fichier audio dialog.correlate.select.audiolater=Audio apr\u00e8s dialog.rearrangephotos.desc=Choisissez la destination et l\u2019ordre des points des photos -dialog.rearrangephotos.tostart=Aller au d\u00e9but -dialog.rearrangephotos.toend=Aller \u00e0 la fin -dialog.rearrangephotos.nosort=Ne pas trier -dialog.rearrangephotos.sortbyfilename=Trier par nom de fichier -dialog.rearrangephotos.sortbytime=Trier par horodatage +dialog.rearrange.tostart=Au d\u00e9but +dialog.rearrange.toend=\u00e0 la fin +dialog.rearrange.tonearest=Au point de trace le plus proche +dialog.rearrange.nosort=Ne pas trier +dialog.rearrange.sortbyfilename=Trier par nom de fichier +dialog.rearrange.sortbyname=Trier par nom +dialog.rearrange.sortbytime=Trier par horodatage dialog.compress.closepoints.title=Suppression des points voisins dialog.compress.closepoints.paramdesc=Taille du voisinage dialog.compress.wackypoints.title=Suppression des points anormaux @@ -545,6 +546,8 @@ dialog.weather.wind=Vent dialog.weather.temp=Temp dialog.weather.humidity=Humidit\u00e9 dialog.weather.creditnotice=Ces donn\u00e9es sont fournies par openweathermap.org. Consultez la page pour plus de d\u00e9tails. +dialog.deletebydate.column.keep=Conserver +dialog.deletebydate.column.delete=Supprimer # 3d window dialog.3d.title=Vue 3D de GpsPrune @@ -831,5 +834,4 @@ 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.interpolate.invalidparameter=Le nombre de points doit \u00eatre compris entre 1 et 1000 error.tracksplit.nosplit=Impossible de s\u00e9parer les segments diff --git a/tim/prune/lang/prune-texts_hu.properties b/tim/prune/lang/prune-texts_hu.properties index a1bf2ec..b4a9faf 100644 --- a/tim/prune/lang/prune-texts_hu.properties +++ b/tim/prune/lang/prune-texts_hu.properties @@ -13,10 +13,8 @@ menu.track.undo=Visszavon\u00e1s menu.track.clearundo=Visszavon\u00e1si lista t\u00f6rl\u00e9se menu.track.markrectangle=N\u00e9gyzeten bel\u00fcli pontok megjel\u00f6l\u00e9se menu.track.deletemarked=Jel\u00f6lt pontok t\u00f6rl\u00e9se -menu.track.rearrange=\u00datpontok \u00fajrarendez\u00e9se -menu.track.rearrange.start=\u00d6sszes a f\u00e1jl elej\u00e9re -menu.track.rearrange.end=\u00d6sszes a f\u00e1jl v\u00e9g\u00e9re -menu.track.rearrange.nearest=Egyenk\u00e9nt a legk\u00f6zelebbi nyomponthoz +function.rearrangewaypoints=\u00datpontok \u00fajrarendez\u00e9se +dialog.rearrange.tonearest=Egyenk\u00e9nt a legk\u00f6zelebbi nyomponthoz menu.range=Tartom\u00e1ny menu.range.all=Mindet kijel\u00f6l menu.range.none=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se @@ -413,11 +411,11 @@ dialog.correlate.audioselect.intro=V\u00e1lasszon egyet ezek k\u00f6z\u00fcl a m dialog.correlate.select.audioname=Hang neve dialog.correlate.select.audiolater=Hang k\u00e9s\u0151bb dialog.rearrangephotos.desc=V\u00e1lassza ki a f\u00e9nyk\u00e9ppontok c\u00e9lj\u00e1t \u00e9s rendez\u00e9si sorrendj\u00e9t -dialog.rearrangephotos.tostart=Mozgat\u00e1s a kezdet\u00e9hez -dialog.rearrangephotos.toend=Mozgat\u00e1s a v\u00e9g\u00e9hez -dialog.rearrangephotos.nosort=Ne rendezze -dialog.rearrangephotos.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint -dialog.rearrangephotos.sortbytime=Rendez\u00e9s id\u0151 szerint +dialog.rearrange.tostart=Mozgat\u00e1s a kezdet\u00e9hez +dialog.rearrange.toend=Mozgat\u00e1s a v\u00e9g\u00e9hez +dialog.rearrange.nosort=Ne rendezze +dialog.rearrange.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint +dialog.rearrange.sortbytime=Rendez\u00e9s id\u0151 szerint dialog.compress.closepoints.title=K\u00f6zeli pontok elt\u00e1vol\u00edt\u00e1sa dialog.compress.closepoints.paramdesc=Hat\u00f3t\u00e1vols\u00e1g dialog.compress.wackypoints.title=Kisz\u00e1m\u00edthatatlan pontok elt\u00e1vol\u00edt\u00e1sa @@ -848,7 +846,6 @@ error.playaudiofailed=A hangf\u00e1jl lej\u00e1tsz\u00e1sa nem siker\u00fclt error.cache.notthere=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem tal\u00e1lhat\u00f3 error.cache.empty=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra \u00fcres error.cache.cannotdelete=Nincs t\u00f6r\u00f6lhet\u0151 csempe -error.interpolate.invalidparameter=A pontok sz\u00e1ma 1 \u00e9s 1000 k\u00f6z\u00f6tt kell legyen error.learnestimationparams.failed=Nem lehet param\u00e9tereket tanulni ebb\u0151l a nyomvonalb\u00f3l.\nT\u00f6lts be t\u00f6bb nyomvonalat. error.tracksplit.nosplit=A nyomvonal nem elv\u00e1ghat\u00f3 error.downloadsrtm.nocache=A f\u00e1jlokat nem siker\u00fclt meneni.\nK\u00e9rlek, ellen\u0151rizd a gyors\u00edt\u00f3t\u00e1rat. diff --git a/tim/prune/lang/prune-texts_it.properties b/tim/prune/lang/prune-texts_it.properties index 14df816..c437011 100644 --- a/tim/prune/lang/prune-texts_it.properties +++ b/tim/prune/lang/prune-texts_it.properties @@ -13,10 +13,7 @@ menu.track.undo=Annulla menu.track.clearundo=Cancella lista annulla menu.track.markrectangle=Segnare i punti nel rettangolo menu.track.deletemarked=Cancella punti marcati -menu.track.rearrange=Riorganizza waypoint -menu.track.rearrange.start=Tutti all'inizio del file -menu.track.rearrange.end=Tutti alla fine del file -menu.track.rearrange.nearest=Sul punto pi\u00f9 vicino +function.rearrangewaypoints=Riorganizza waypoint menu.range=Serie menu.range.all=Seleziona tutto menu.range.none=Deseleziona tutto @@ -92,6 +89,7 @@ function.compress=Comprimi la traccia function.deleterange=Cancella la serie function.croptrack=Cima la traccia function.interpolate=Interpola i punti +function.deletebydate=Cancella punti secondo la data function.addtimeoffset=Aggiungi uno scarto temporale function.addaltitudeoffset=Aggiungi uno scarto di altitudine function.convertnamestotimes=Converti nomi dei waypoint in orari @@ -106,6 +104,7 @@ function.estimatetime=Stima durata function.learnestimationparams=Apprendi parametri di stima function.setmapbg=Configura sfondo mappa function.setpaths=Configura percorsi programmi +function.selectsegment=Seleziona segmento corrente function.splitsegments=Dividi traccia in segmenti function.sewsegments=Riorganizza segmenti insieme function.getgpsies=Ottieni tracce da Gpsies @@ -141,6 +140,7 @@ function.saveconfig=Salva configurazione function.diskcache=Salva mappe su disco function.managetilecache=Gestione del cache di tasselli function.getweatherforecast=Ottieni previsioni del tempo +function.setaltitudetolerance=Configura tolleranza di altitudini # Dialogs dialog.exit.confirm.title=Esci da GpsPrune @@ -379,6 +379,7 @@ dialog.gpsies.activity.sailing=Navigazione dialog.gpsies.activity.skating=Pattinaggio dialog.wikipedia.column.name=Titolo articolo dialog.wikipedia.column.distance=Distanza +dialog.wikipedia.nonefound=Nessuna punti trovata dialog.correlate.notimestamps=Non ci sono informazioni temporali tra i dati dei punti, non c'\u00e8 niente per collegarli con le foto. dialog.correlate.nouncorrelatedphotos=Non ci sono foto non correlate.\nSei sicuro di voler continuare? dialog.correlate.nouncorrelatedaudios=Non ci sono audio non correlati. \nSei sicuro di voler continuare? @@ -413,11 +414,14 @@ dialog.correlate.audioselect.intro=Seleziona una di queste riprese audio correla dialog.correlate.select.audioname=Nome ripresa audio dialog.correlate.select.audiolater=Ripresa audio successiva dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei punti foto -dialog.rearrangephotos.tostart=Sposta all'inizio -dialog.rearrangephotos.toend=Sposta alla fine -dialog.rearrangephotos.nosort=Non mettere in ordine -dialog.rearrangephotos.sortbyfilename=Metti in ordine di nome del file -dialog.rearrangephotos.sortbytime=Metti in ordine di tempo +dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei waypoint +dialog.rearrange.tostart=Sposta all'inizio +dialog.rearrange.toend=Sposta alla fine +dialog.rearrange.tonearest=Sul punto pi\u00f9 vicino +dialog.rearrange.nosort=Non mettere in ordine +dialog.rearrange.sortbyfilename=Metti in ordine di nome del file +dialog.rearrange.sortbyname=Metti in ordine di nome +dialog.rearrange.sortbytime=Metti in ordine di tempo dialog.compress.closepoints.title=Cancella punti vicini dialog.compress.closepoints.paramdesc=Fattore vicinanza dialog.compress.wackypoints.title=Cancella punti strani @@ -475,7 +479,7 @@ dialog.checkversion.releasedate1=Questa nuova versione \u00e8 stata rilasciata i dialog.checkversion.releasedate2=. dialog.checkversion.download=Per scaricare la nuova versione vai a http://gpsprune.activityworkshop.net/download.html. dialog.keys.intro=Puoi utilizzare i seguenti tast di scelta rapida al posto del mouse -dialog.keys.keylist=
Tasti frecciaMuovi mappa destra, sinistra, su, giu'
Ctrl + freccia destra, sinistraSelezione punto successivo o precedente
Ctrl + freccia su, giu'Zoom in o out
DelCancella punto attuale
+dialog.keys.keylist=
Tasti frecciaMuovi mappa destra, sinistra, su, giu'
Ctrl + freccia destra, sinistraSelezione punto successivo o precedente
Ctrl + freccia su, giu'Zoom in o out
Ctrl + pagina su, giu'Segmento successivo o precedente
Ctrl + Home, EndPunto primo o ultimo
DelCancella punto attuale
dialog.keys.normalmodifier=Ctrl dialog.keys.macmodifier=Comando dialog.saveconfig.desc=Le configurazioni seguenti possono essere salvati in un file di configurazione: @@ -515,6 +519,16 @@ dialog.colourchooser.title=Scegli colore dialog.colourchooser.red=Rosso dialog.colourchooser.green=Verde dialog.colourchooser.blue=Blu +dialog.colourer.type.byfile=Per file +dialog.colourer.type.bysegment=Per segmento +dialog.colourer.type.byaltitude=Per altitud +dialog.colourer.type.byspeed=Per velocit\u00e0 +dialog.colourer.type.byvertspeed=Per velocit\u00e0 verticale +dialog.colourer.type.bygradient=Per gradiente +dialog.colourer.type.bydate=Per data +dialog.colourer.start=Colore iniziale +dialog.colourer.end=Colore finale +dialog.colourer.maxcolours=Numero massimo di colori dialog.setlanguage.firstintro=Puoi selezionare una delle lingue incluse,

oppure selezionare un file di testo. dialog.setlanguage.secondintro=Devi salvare le impostazioni e

riavviare GpsPrune per cambiare la lingua. dialog.setlanguage.language=Lingua @@ -564,6 +578,9 @@ dialog.weather.wind=Vento dialog.weather.temp=Temp dialog.weather.humidity=Umidit\u00e0 dialog.weather.creditnotice=Queste informazioni sono rese disponibili da openweathermap.org. Il loro sito web contiene ulteriori dettagli. +dialog.deletebydate.nodate=Senza dati temporali +dialog.deletebydate.column.keep=Tieni +dialog.deletebydate.column.delete=Cancella # 3d window dialog.3d.title=Visione GpsPrune in 3D @@ -718,6 +735,7 @@ fieldname.longitude=Longitudine fieldname.altitude=Altitudine fieldname.timestamp=Dati temporali fieldname.time=Tempo +fieldname.date=Data fieldname.waypointname=Nome fieldname.waypointtype=Tipo fieldname.newsegment=Segmento @@ -852,7 +870,6 @@ error.playaudiofailed=Ripresa audio non riprodotta error.cache.notthere=Directory del cache di tasselli non trovato error.cache.empty=Directory del cache di tasselli \u00e8 vuoto error.cache.cannotdelete=Impossibile cancellare tasselli -error.interpolate.invalidparameter=Il numero di punti deve essere tra 1 e 1000 error.learnestimationparams.failed=Non \u00e8 possibile apprendere i parametri da questa traccia.\nProva a caricare pi\u00f9 tracce. error.tracksplit.nosplit=La traccia non pu\u00f2 essere divisa error.downloadsrtm.nocache=Non \u00e8 stato possibile salvare i file.\nControlla la cache del disco. diff --git a/tim/prune/lang/prune-texts_ja.properties b/tim/prune/lang/prune-texts_ja.properties index e581c18..371eec5 100644 --- a/tim/prune/lang/prune-texts_ja.properties +++ b/tim/prune/lang/prune-texts_ja.properties @@ -12,10 +12,7 @@ menu.track.undo=\u30a2\u30f3\u30c9\u30a5 menu.track.clearundo=\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3059\u308b menu.track.markrectangle=\u56db\u89d2\u306e\u4e2d\u306b\u5370\u3092\u3064\u3051\u308b menu.track.deletemarked=\u5370\u306e\u4ed8\u3044\u305f\u70b9\u3092\u524a\u9664 -menu.track.rearrange=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u4e26\u3079\u66ff\u3048 -menu.track.rearrange.start=\u5168\u3066\u3092\u30d5\u30a1\u30a4\u30eb\u306e\u59cb\u70b9\u306b -menu.track.rearrange.end=\u5168\u3066\u3092\u30d5\u30a1\u30a4\u30eb\u306e\u7d42\u70b9\u306b -menu.track.rearrange.nearest=\u305d\u308c\u305e\u308c\u3092\u6700\u8fd1\u306e\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306b +function.rearrangewaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u4e26\u3079\u66ff\u3048 menu.range=\u7bc4\u56f2(R) menu.range.all=\u5168\u3066\u9078\u629e menu.range.none=\u9078\u629e\u89e3\u9664 @@ -317,11 +314,12 @@ dialog.correlate.timestamp.middle=\u4e2d\u9593\u70b9 dialog.correlate.timestamp.end=\u7d42\u70b9 dialog.correlate.select.audioname=\u30aa\u30fc\u30c7\u30a3\u30aa\u540d dialog.rearrangephotos.desc=\u5411\u304b\u3046\u5148\u3092\u9078\u629e\u3057\u3066\u3001\u5199\u771f\u306e\u70b9\u3092\u4e26\u3079\u76f4\u3059\u3002 -dialog.rearrangephotos.tostart=\u79fb\u52d5\u958b\u59cb -dialog.rearrangephotos.toend=\u79fb\u52d5\u7d42\u4e86 -dialog.rearrangephotos.nosort=\u4e26\u3079\u66ff\u3048\u306a\u3044 -dialog.rearrangephotos.sortbyfilename=\u30d5\u30a1\u30a4\u30eb\u540d\u3067\u4e26\u3079\u66ff\u3048 -dialog.rearrangephotos.sortbytime=\u6642\u9593\u3067\u4e26\u3079\u66ff\u3048 +dialog.rearrange.tostart=\u79fb\u52d5\u958b\u59cb +dialog.rearrange.toend=\u79fb\u52d5\u7d42\u4e86 +dialog.rearrange.tonearest=\u305d\u308c\u305e\u308c\u3092\u6700\u8fd1\u306e\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306b +dialog.rearrange.nosort=\u4e26\u3079\u66ff\u3048\u306a\u3044 +dialog.rearrange.sortbyfilename=\u30d5\u30a1\u30a4\u30eb\u540d\u3067\u4e26\u3079\u66ff\u3048 +dialog.rearrange.sortbytime=\u6642\u9593\u3067\u4e26\u3079\u66ff\u3048 dialog.compress.closepoints.title=\u8fd1\u508d\u70b9\u3092\u524a\u9664 dialog.compress.closepoints.paramdesc=\u8fd1\u508d\u4fc2\u6570 dialog.compress.wackypoints.title=\u304a\u304b\u3057\u306a\u70b9\u306e\u524a\u9664 diff --git a/tim/prune/lang/prune-texts_ko.properties b/tim/prune/lang/prune-texts_ko.properties index b14b0a5..69722c9 100644 --- a/tim/prune/lang/prune-texts_ko.properties +++ b/tim/prune/lang/prune-texts_ko.properties @@ -10,10 +10,7 @@ menu.track=\ud2b8\ub799 menu.track.undo=\uc2e4\ud589\ucde8\uc18c menu.track.clearundo=\uc2e4\ud589\ucde8\uc18c \ubaa9\ub85d \uc0ad\uc81c menu.track.deletemarked=\ud45c\uc2dc\ub41c \uc9c0\uc810 \uc9c0\uc6b0\uae30 -menu.track.rearrange=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c -menu.track.rearrange.start=\uc2dc\uc791 \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9 -menu.track.rearrange.end=\ub05d \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9 -menu.track.rearrange.nearest=\uac00\uae4c\uc6b4 \ud2b8\ub799\uc9c0\uc810\uc73c\ub85c \uc774\ub3d9 +function.rearrangewaypoints=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c menu.range=\uc5f0\uacb0\uc120 menu.range.all=\ubaa8\ub450 \uc120\ud0dd menu.range.none=\uc120\ud0dd\ud558\uc9c0 \uc54a\uae30 @@ -320,11 +317,12 @@ dialog.correlate.audioselect.intro=\ud0c0\uc784\uc624\ud504\uc14b\uc73c\ub85c \u dialog.correlate.select.audioname=\uc18c\ub9ac \uc774\ub984 dialog.correlate.select.audiolater=\uc18c\ub9ac \ud6c4\uc5d0 dialog.rearrangephotos.desc=\uc0ac\uc9c4 \uc9c0\uc810\ub4e4\uc744 \uc5b4\ub5bb\uac8c \uc815\ub82c\ud560 \uac74\uc9c0 \uc120\ud0dd\ud574\uc8fc\uc138\uc694. -dialog.rearrangephotos.tostart=\uc2dc\uc791\uc73c\ub85c -dialog.rearrangephotos.toend=\ub05d\uc73c\ub85c -dialog.rearrangephotos.nosort=\uc815\ub82c\ud558\uc9c0 \uc54a\uae30 -dialog.rearrangephotos.sortbyfilename=\ud30c\uc77c \uc774\ub984\uc73c\ub85c \uc815\ub82c -dialog.rearrangephotos.sortbytime=\uc2dc\uac04\uc73c\ub85c \uc815\ub82c +dialog.rearrange.tostart=\uc2dc\uc791\uc73c\ub85c +dialog.rearrange.toend=\ub05d\uc73c\ub85c +dialog.rearrange.tonearest=\uac00\uae4c\uc6b4 \ud2b8\ub799\uc9c0\uc810\uc73c\ub85c \uc774\ub3d9 +dialog.rearrange.nosort=\uc815\ub82c\ud558\uc9c0 \uc54a\uae30 +dialog.rearrange.sortbyfilename=\ud30c\uc77c \uc774\ub984\uc73c\ub85c \uc815\ub82c +dialog.rearrange.sortbytime=\uc2dc\uac04\uc73c\ub85c \uc815\ub82c dialog.deletemarked.nonefound=\uc9c0\uc810 \ub370\uc774\ud130\uac00 \uc81c\uac70\ub420 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. dialog.compress.closepoints.title=\uc8fc\ubcc0 \ud3ec\uc778\ud2b8 \uc81c\uac70 dialog.compress.closepoints.paramdesc=\ud655\uc7a5 \uacc4\uc218 diff --git a/tim/prune/lang/prune-texts_nl.properties b/tim/prune/lang/prune-texts_nl.properties index a077bc9..bcfa3f1 100644 --- a/tim/prune/lang/prune-texts_nl.properties +++ b/tim/prune/lang/prune-texts_nl.properties @@ -13,10 +13,6 @@ menu.track.undo=Ongedaan maken menu.track.clearundo=Ongedaan-maken lijst wissen menu.track.markrectangle=Makeer alle punten in een vierkant menu.track.deletemarked=Verwijderen gemarkeerde punten -menu.track.rearrange=Rangschikken waypoints -menu.track.rearrange.start=Alles naar begin route -menu.track.rearrange.end=Alles naar einde route -menu.track.rearrange.nearest=Alles naar dichtstbijzijnde routepunt menu.range=Reeks menu.range.all=Selecteer alles menu.range.none=Selecteer geen @@ -92,8 +88,10 @@ function.compress=Route comprimeren function.deleterange=Verwijder reeks function.croptrack=Route bijknippen function.interpolate=Interpoleer punten +function.deletebydate=Verwijder punten op datum function.addtimeoffset=Tijdsverschil toevoegen function.addaltitudeoffset=Hoogteverschil toevoegen +function.rearrangewaypoints=Rangschikken waypoints function.convertnamestotimes=Converteer waypointnamen naar tijden function.deletefieldvalues=Verwijder veldwaarden function.findwaypoint=Zoek waypoint @@ -106,6 +104,7 @@ function.estimatetime=Geschatte tijd function.learnestimationparams=Parameters voor geschatte tijd function.setmapbg=Instellen kaart achtergrond function.setpaths=Instellen programmapaden +function.selectsegment=Selecteer huidige segment function.splitsegments=Splits route in segmenten function.sewsegments=Voeg segmenten samen function.getgpsies=Routes van Gpsies ophalen @@ -141,6 +140,7 @@ function.saveconfig=Instellingen opslaan function.diskcache=Kaart opslaan op schijf function.managetilecache=Beheer tegelcache function.getweatherforecast=Ophalen weersvoorspelling +function.setaltitudetolerance=Instellen hoogtetolerantie # Dialogs dialog.exit.confirm.title=GpsPrune afsluiten @@ -379,6 +379,7 @@ dialog.gpsies.activity.sailing=Zeilen dialog.gpsies.activity.skating=Skating dialog.wikipedia.column.name=Artikelnaam dialog.wikipedia.column.distance=Afstand +dialog.wikipedia.nonefound=Geen punten gevonden dialog.correlate.notimestamps=Er zit geen tijdinformatie in de punten, dus kunnen ze niet aan foto's gekoppeld worden. dialog.correlate.nouncorrelatedphotos=Er zijn geen ongekoppelde foto's.\nWeet u zeker dat u wilt doorgaan? dialog.correlate.nouncorrelatedaudios=Er zijn geen ongekoppelde geluidsbestanden.\nWeet u zeker dat u wilt doorgaan? @@ -410,14 +411,17 @@ dialog.correlate.timestamp.beginning=Begin dialog.correlate.timestamp.middle=Midden dialog.correlate.timestamp.end=Einde dialog.correlate.audioselect.intro=Gebruk \u00e9\u00e9n van deze gecorreleerde audiobestanden als tijdsveschil -dialog.correlate.select.audioname=Naam audiobestsnd +dialog.correlate.select.audioname=Naam audiobestand dialog.correlate.select.audiolater=Audio later +dialog.rearrangewaypoints.desc=Selecteer doel en sorteervolgorde van de waypoints dialog.rearrangephotos.desc=Selecteer doel en sorteervolgorde van de foto punten -dialog.rearrangephotos.tostart=Naar begin -dialog.rearrangephotos.toend=Naar einde -dialog.rearrangephotos.nosort=Niet sorteren -dialog.rearrangephotos.sortbyfilename=Sorteren op bestandsnaam -dialog.rearrangephotos.sortbytime=Sorteren op tijd +dialog.rearrange.tostart=Naar begin +dialog.rearrange.toend=Naar einde +dialog.rearrange.tonearest=Naar dichtstbijzijnde routepunt +dialog.rearrange.nosort=Niet sorteren +dialog.rearrange.sortbyfilename=Sorteren op bestandsnaam +dialog.rearrange.sortbyname=Sorteren op naam +dialog.rearrange.sortbytime=Sorteren op tijd dialog.compress.closepoints.title=Verwijder nabijliggende punten dialog.compress.closepoints.paramdesc=Bereik dialog.compress.wackypoints.title=Vreemde punten verwijderen @@ -515,6 +519,19 @@ dialog.colourchooser.title=Selecteer een kleur dialog.colourchooser.red=Rood dialog.colourchooser.green=Groen dialog.colourchooser.blue=Blauw +dialog.colourer.intro=Een puntinkleuring geeft routepunten verschillende kleuren +dialog.colourer.type=Type inkleuring +dialog.colourer.type.none=Geen +dialog.colourer.type.byfile=Op bestand +dialog.colourer.type.bysegment=Op segment +dialog.colourer.type.byaltitude=Op hoogte +dialog.colourer.type.byspeed=Op snelheid +dialog.colourer.type.byvertspeed=Op verticale snelheid +dialog.colourer.type.bygradient=Op stijgingspercentage +dialog.colourer.type.bydate=Op datum +dialog.colourer.start=Beginkleur +dialog.colourer.end=Eindkleur +dialog.colourer.maxcolours=Maximum aantal kleuren dialog.setlanguage.firstintro=U kunt kiezen uit \u00e9\u00e9n van de meegeleverde talen,

of selecteer een tekstbestand. dialog.setlanguage.secondintro=U dient uw instellingen op te slaan en

GpsPrune te herstarten om de taal te kunnen wijzigen dialog.setlanguage.language=Taal @@ -560,8 +577,17 @@ dialog.weather.day.thursday=Donderdag dialog.weather.day.friday=Vrijdag dialog.weather.day.saturday=Zaterdag dialog.weather.day.sunday=Zondag +dialog.weather.wind=Wind +dialog.weather.temp=Temp dialog.weather.humidity=Luchtvocht. dialog.weather.creditnotice=Deze gegevens worden beschikbaar gesteld door openweathermap.org. Hun website heeft meer details. +dialog.deletebydate.onlyonedate=Alle punten werden op dezelfde datum opgenomen. +dialog.deletebydate.intro=Je kan voor iedere datum in de route kiezen of je de punten wilt verwijderen of behouden +dialog.deletebydate.nodate=Geen tijden +dialog.deletebydate.column.keep=Behouden +dialog.deletebydate.column.delete=Verwijderen +dialog.setaltitudetolerance.text.metres=Grens (in meters) waaronder kleine klimmen en afdalingen worden genegeerd +dialog.setaltitudetolerance.text.feet=Grens (in feet) waaronder kleine klimmen en afdalingen worden genegeerd # 3d window dialog.3d.title=GpsPrune in 3D @@ -716,6 +742,7 @@ fieldname.longitude=Lengtegraad fieldname.altitude=Hoogte fieldname.timestamp=Tijd fieldname.time=Tijd +fieldname.date=Datum fieldname.waypointname=Naam fieldname.waypointtype=Type fieldname.newsegment=Segment @@ -757,7 +784,9 @@ units.degmin=Grd-min units.deg=Graden units.iso8601=ISO 8601 units.degreescelsius=Celsius +units.degreescelsius.short=\u00baC units.degreesfahrenheit=Fahrenheit +units.degreesfahrenheit.short=\u00baF # How to combine conditions, such as filters logic.and=en @@ -848,7 +877,6 @@ error.playaudiofailed=Kon audiobestand niet afspelen error.cache.notthere=De tegelcache map niet gevonden error.cache.empty=De tegelcache map is leeg error.cache.cannotdelete=Er konden geen tegels verwijderd worden -error.interpolate.invalidparameter=Aantal punten moet tussen 1 en 1000 liggen error.learnestimationparams.failed=Kan geen parameters bepalen van deze route.\nProbeer meer routes te laden. error.tracksplit.nosplit=Deze route kon niet opgedeeld worden error.downloadsrtm.nocache=De bestanden konden niet worden opgeslagen.\nControleer de schijfcache. diff --git a/tim/prune/lang/prune-texts_pl.properties b/tim/prune/lang/prune-texts_pl.properties index 80a67c1..e730fb1 100644 --- a/tim/prune/lang/prune-texts_pl.properties +++ b/tim/prune/lang/prune-texts_pl.properties @@ -7,15 +7,12 @@ menu.file.addphotos=Dodaj zdj\u0119cia menu.file.recentfiles=Ostatnio u\u017cywane menu.file.save=Zapisz menu.file.exit=Zako\u0144cz +menu.online=Online menu.track=\u015acie\u017cka menu.track.undo=Cofnij menu.track.clearundo=Wyczy\u015b\u0107 list\u0119 zmian menu.track.markrectangle=Zaznaczenie prostok\u0105tne menu.track.deletemarked=Usu\u0144 zaznaczone punkty -menu.track.rearrange=Zmie\u0144 kolejno\u015b\u0107 punkt\u00f3w po\u015brednich -menu.track.rearrange.start=Wszystkie na pocz\u0105tek \u015bcie\u017cki -menu.track.rearrange.end=Wszystkie na koniec \u015bcie\u017cki -menu.track.rearrange.nearest=Do najbli\u017cszego punktu menu.range=Zakres menu.range.all=Zaznacz wszystko menu.range.none=Usu\u0144 zaznaczenie @@ -23,7 +20,7 @@ menu.range.start=Zaznacz pocz\u0105tek zakresu menu.range.end=Zaznacz koniec zakresu menu.range.average=U\u015brednij zaznaczenie menu.range.reverse=Odwr\u00f3\u0107 zakres -menu.range.mergetracksegments=Po\u0142\u0105cz fragmenty \u015bcie\u017cek +menu.range.mergetracksegments=Scal fragmenty \u015bcie\u017cek menu.range.cutandmove=Wytnij i przesu\u0144 zaznaczenie menu.point=Punkt menu.point.editpoint=Edytuj punkt @@ -57,6 +54,7 @@ menu.map.editmode=Tryb edycji # Alt keys for menus altkey.menu.file=P +altkey.menu.online=O altkey.menu.track=C altkey.menu.range=Z altkey.menu.point=U @@ -90,8 +88,10 @@ function.compress=Kompresuj \u015bcie\u017ck\u0119 function.deleterange=Usu\u0144 zakres function.croptrack=Przytnij \u015bcie\u017ck\u0119 function.interpolate=Wstaw pomi\u0119dzy punkty +function.deletebydate=Usu\u0144 punkty wed\u0142ug daty function.addtimeoffset=Dodaj przesuni\u0119cie czasu function.addaltitudeoffset=Dodaj przesuni\u0119cie wysoko\u015bci +function.rearrangewaypoints=Zmie\u0144 kolejno\u015b\u0107 punkt\u00f3w po\u015brednich function.convertnamestotimes=Zamie\u0144 nazwy punkt\u00f3w na czas function.deletefieldvalues=Usu\u0144 warto\u015bci function.findwaypoint=Znajd\u017a punkt po\u015bredni @@ -104,6 +104,9 @@ function.estimatetime=Przewidywany czas function.learnestimationparams=Skoryguj wsp\u00f3\u0142czynniki szacowania czasu function.setmapbg=Wybierz map\u0119 t\u0142a function.setpaths=Ustaw \u015bcie\u017cki do program\u00f3w +function.selectsegment=Wybierz bie\u017c\u0105cy fragment +function.splitsegments=Podziel \u015bcie\u017ck\u0119 na fragmenty +function.sewsegments=Po\u0142\u0105cz fragmenty function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies function.uploadgpsies=Wy\u015blij \u015bcie\u017cki do Gpsies function.lookupsrtm=Pobierz wysoko\u015bci z SRTM @@ -136,7 +139,8 @@ function.checkversion=Sprawd\u017a czy jest nowa wersja function.saveconfig=Zapisz ustawienia function.diskcache=Zapisz mapy na dysk function.managetilecache=Zarz\u0105dzaj keszem p\u0142ytek -function.getweatherforecast=Pobierz prognoza pogody +function.getweatherforecast=Pobierz prognoz\u0119 pogody +function.setaltitudetolerance=Ustaw tolerancj\u0119 wysoko\u015bci # Dialogs dialog.exit.confirm.title=Zako\u0144cz GpsPrune @@ -244,6 +248,8 @@ dialog.exportpov.modelstyle=Styl modelu dialog.exportpov.ballsandsticks=Kule i pa\u0142ki dialog.exportpov.tubesandwalls=Rurki i \u015bciany dialog.3d.warningtracksize=Ta \u015bcie\u017cka ma bardzo wiele punkt\u00f3w, kt\u00f3rych Java3D mo\u017ce nie wy\u015bwietli\u0107.\nCzy chcesz kontynuowa\u0107? +dialog.3d.useterrain=Poka\u017c teren +dialog.3d.terraingridsize=Rozmiar siatki dialog.exportpov.baseimage=Obraz podk\u0142adu dialog.exportpov.cannotmakebaseimage=Nie mo\u017cna zapisa\u0107 obrazu podk\u0142adu dialog.baseimage.title=Obrazu podk\u0142adu @@ -373,6 +379,7 @@ dialog.gpsies.activity.sailing=\u017beglarstwo dialog.gpsies.activity.skating=Wrotki/rolki 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.correlate.notimestamps=Punkty nie maj\u0105 znacznik\u00f3w czasu, nie mo\u017cna ich powi\u0105za\u0107 ze zdj\u0119ciami. dialog.correlate.nouncorrelatedphotos=Nie ma nie powi\u0105zanych zdj\u0119\u0107.\nCzy na pewno chcesz kontynuowa\u0107? dialog.correlate.nouncorrelatedaudios=Nie ma nie powi\u0105zanych plik\u00f3w audio.\nCzy na pewno chcesz kontynuowa\u0107? @@ -406,12 +413,15 @@ dialog.correlate.timestamp.end=ko\u0144ca dialog.correlate.audioselect.intro=Wybierz jeden z powi\u0105zanych plik\u00f3w audio i u\u017cyj go jako wzorca do przesuni\u0119cia czasu dialog.correlate.select.audioname=nazwa pliku audio dialog.correlate.select.audiolater=p\u00f3\u017aniejszy plik audio +dialog.rearrangewaypoints.desc=Wybierz przeznaczenie i porz\u0105dek sortowania punkt\u00f3w po\u015brednich dialog.rearrangephotos.desc=Wybierz przeznaczenie i porz\u0105dek sortowania punkt\u00f3w ze zdj\u0119ciami -dialog.rearrangephotos.tostart=Przesu\u0144 na pocz\u0105tek -dialog.rearrangephotos.toend=Przesu\u0144 na koniec -dialog.rearrangephotos.nosort=Nie sortuj -dialog.rearrangephotos.sortbyfilename=Sortuj po nazwie pliku -dialog.rearrangephotos.sortbytime=Sortuj wed\u0142ug czasu +dialog.rearrange.tostart=Przesu\u0144 na pocz\u0105tek +dialog.rearrange.toend=Przesu\u0144 na koniec +dialog.rearrange.tonearest=Do najbli\u017cszego punktu +dialog.rearrange.nosort=Nie sortuj +dialog.rearrange.sortbyfilename=Sortuj po nazwie pliku +dialog.rearrange.sortbyname=Sortuj po nazwie +dialog.rearrange.sortbytime=Sortuj wed\u0142ug czasu dialog.compress.closepoints.title=Usuwanie bliskich sobie punkt\u00f3w dialog.compress.closepoints.paramdesc=Wsp\u00f3\u0142czynnik rozpi\u0119to\u015bci dialog.compress.wackypoints.title=Usuwanie dziwacznych punkt\u00f3w @@ -509,6 +519,19 @@ dialog.colourchooser.title=Wybierz kolor dialog.colourchooser.red=Czerwony dialog.colourchooser.green=Zielony dialog.colourchooser.blue=Niebieski +dialog.colourer.intro=Koloryzer punkt\u00f3w, zmienia kolor punkt\u00f3w \u015bcie\u017cki +dialog.colourer.type=Tryb koloryzera +dialog.colourer.type.none=\u017baden +dialog.colourer.type.byfile=wed\u0142ug pliku +dialog.colourer.type.bysegment=wed\u0142ug segmentu +dialog.colourer.type.byaltitude=wed\u0142ug wysoko\u015bci +dialog.colourer.type.byspeed=wed\u0142ug pr\u0119dko\u015bci +dialog.colourer.type.byvertspeed=wed\u0142ug pr\u0119dko\u015bci pionowej +dialog.colourer.type.bygradient=wed\u0142ug nachylenia +dialog.colourer.type.bydate=wed\u0142ug daty +dialog.colourer.start=Kolor pocz\u0105tkowy +dialog.colourer.end=Kolor ko\u0144cowy +dialog.colourer.maxcolours=Maksymalna liczba kolor\u00f3w dialog.setlanguage.firstintro=Mo\u017cesz wybra\u0107 jeden z do\u0142\u0105czonych j\u0119zyk\u00f3w

Albo wybra\u0107 wybrany przez siebie plik tekstowy. dialog.setlanguage.secondintro=B\u0119dziesz musia\u0142 zapisa\u0107 ustawienia

i zrestartowa\u0107 GpsPrune by zmieni\u0107 j\u0119zyk. dialog.setlanguage.language=J\u0119zyk @@ -536,16 +559,35 @@ dialog.deletefieldvalues.nofields=Brak p\u00f3l do skasowania dla tego zakresu dialog.setlinewidth.text=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM: dialog.searchwikipedianames.search=Szukaj +dialog.weather.location=Pozycja +dialog.weather.update=Prognoza zaktualizowana +dialog.weather.sunrise=Wsch\u00f3d s\u0142o\u0144ca +dialog.weather.sunset=Zach\u00f3d s\u0142o\u0144ca +dialog.weather.temperatureunits=Jednostki temperatury +dialog.weather.currentforecast=Bie\u017c\u0105ca pogoda +dialog.weather.dailyforecast=Prognoza dobowa +dialog.weather.3hourlyforecast=Prognoza na trzy godziny dialog.weather.day.now=Aktualny dialog.weather.day.today=Dzisiaj dialog.weather.day.tomorrow=Jutro dialog.weather.day.monday=Poniedzia\u0142ek dialog.weather.day.tuesday=Wtorek -dialog.weather.day.wednesday=\u015Aroda +dialog.weather.day.wednesday=\u015aroda dialog.weather.day.thursday=Czwartek dialog.weather.day.friday=Pi\u0105tek dialog.weather.day.saturday=Sobota dialog.weather.day.sunday=Niedziela +dialog.weather.wind=Wiatr +dialog.weather.temp=Temp +dialog.weather.humidity=Wilgotno\u015b\u0107 +dialog.weather.creditnotice=Dane na podstawie openweathermap.org. Wi\u0119cej informacji na ich stronie. +dialog.deletebydate.onlyonedate=Te punkty zosta\u0142y zarejestrowane w tym samym czasie. +dialog.deletebydate.intro=Dla ka\u017cdej daty w \u015bcie\u017cce, mo\u017cesz wybra\u0107 czy usun\u0105\u0107 czy zostawi\u0107 punkty +dialog.deletebydate.nodate=Brak znacznika czasu. +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 # 3d window dialog.3d.title=GpsPrune widok tr\u00f3jwymiarowy @@ -564,6 +606,8 @@ confirm.addtimeoffset=Dodano przesuni\u0119cie czasowe confirm.addaltitudeoffset=Dodano przesuni\u0119cie wysoko\u015bci confirm.rearrangewaypoints=Przestawiono punkty po\u015brednie confirm.rearrangephotos=Zmieniono kolejno\u015b\u0107 zdj\u0119\u0107 +confirm.splitsegments= +confirm.sewsegments=Po\u0142\u0105czono %d fragmenty/\u00f3w confirm.cutandmove=Przesuni\u0119to zaznaczenie confirm.interpolate=Dodano punkty confirm.convertnamestotimes=Zmieniono nazwy punkt\u00f3w po\u015brednich @@ -582,13 +626,20 @@ confirm.createpoint=stworzono punkt confirm.rotatephoto=obr\u00f3cono zdj\u0119cie 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 confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone -# Tips +# Tips, shown just once when appropriate 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.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. # Buttons @@ -691,6 +742,7 @@ fieldname.longitude=D\u0142ugo\u015b\u0107 fieldname.altitude=Wysoko\u015b\u0107 fieldname.timestamp=Czas fieldname.time=Czas +fieldname.date=Data fieldname.waypointname=Nazwa fieldname.waypointtype=Typ fieldname.newsegment=Odcinek @@ -764,7 +816,9 @@ undo.croptrack=przytnij \u015bcie\u017ck\u0119 undo.deletemarked=usu\u0144 punkty undo.insert=wstaw punkty undo.reverse=odwr\u00f3\u0107 zakres -undo.mergetracksegments=po\u0142\u0105cz fragmenty \u015bcie\u017cki +undo.mergetracksegments=scal fragmenty \u015bcie\u017cki +undo.splitsegments=podziel \u015bcie\u017ck\u0119 na fragmenty +undo.sewsegments=po\u0142\u0105cz fragmenty \u015bcie\u017cki undo.addtimeoffset=dodaj przesuni\u0119cie czasowe undo.addaltitudeoffset=dodaj przeuni\u0119cie wysoko\u015bci undo.rearrangewaypoints=przestaw punkty po\u015brednie @@ -823,5 +877,7 @@ error.playaudiofailed=Nie powiod\u0142o si\u0119 odtwarzanie pliku audio error.cache.notthere=Nie znaleziono katalogu kesza error.cache.empty=Katalog kesza jest pusty error.cache.cannotdelete=\u017badne p\u0142ytki nie mog\u0142y zosta\u0107 usuni\u0119te -error.interpolate.invalidparameter=Ilo\u015b\u0107 punkt\u00f3w musi zawiera\u0107 si\u0119 w zakresie od 1 do 1000 error.learnestimationparams.failed=Oszacowanie wsp\u00f3\u0142czynnik\u00f3w dla danej scie\u017cki nie powiod\u0142o si\u0119.\nSpr\u00f3buj za\u0142adowa\u0107 wi\u0119cej \u015bcie\u017cek. +error.tracksplit.nosplit=Nie mo\u017cna podzieli\u0107 \u015bcie\u017cki +error.downloadsrtm.nocache=Nie mo\u017cna zapisa\u0107 plik\u00f3w\nSprawd\u017a ustawienia kesza +error.sewsegments.nothingdone=Nie mo\u017cna po\u0142\u0105czy\u0107 fragment\u00f3w\nW \u015bcie\u017cce jest teraz %d fragment\u00f3w. diff --git a/tim/prune/lang/prune-texts_pt.properties b/tim/prune/lang/prune-texts_pt.properties index 85870ad..6319688 100644 --- a/tim/prune/lang/prune-texts_pt.properties +++ b/tim/prune/lang/prune-texts_pt.properties @@ -13,10 +13,7 @@ menu.track.undo=Desfazer menu.track.clearundo=Limpar lista de desfazer menu.track.markrectangle=Marcar pontos no ret\u00e2ngulo menu.track.deletemarked=Remover pontos marcados -menu.track.rearrange=Rearrumar pontos -menu.track.rearrange.start=Tudo para o in\u00edcio do arquivo -menu.track.rearrange.end=Tudo para o fim do arquivo -menu.track.rearrange.nearest=Cada um para o ponto da rota mais pr\u00f3ximo +function.rearrangewaypoints=Rearrumar pontos menu.range=Intervalo menu.range.all=Selecionar tudo menu.range.none=Desmarcar todas as sele\u00e7\u00f5es @@ -92,6 +89,7 @@ function.compress=Comprimir rota function.deleterange=Remover intervalo function.croptrack=Cortar rota function.interpolate=Interpolar pontos +function.deletebydate=Remover pontos de acordo com a data function.addtimeoffset=Adicionar diferen\u00e7a de tempo function.addaltitudeoffset=Adicionar diferen\u00e7a de altitude function.convertnamestotimes=Converter nomes dos pontos para tempos @@ -106,6 +104,7 @@ function.estimatetime=Tempo estimado function.learnestimationparams=Aprender os par\u00e2metros para estimativa de tempo function.setmapbg=Definir como fundo do mapa function.setpaths=Definir caminhos do programa +function.selectsegment=Selecionar segmento atual function.splitsegments=Dividir rota em segmentos function.sewsegments=Reunir segmentos em rota function.getgpsies=Obter rotas Gpsies @@ -413,11 +412,13 @@ dialog.correlate.audioselect.intro=Selecione um destes \u00e1udios correlacionad dialog.correlate.select.audioname=Nome do \u00e1udio dialog.correlate.select.audiolater=\u00c1udio posterior dialog.rearrangephotos.desc=Selecione o destino e a ordena\u00e7\u00e3o dos pontos das fotos -dialog.rearrangephotos.tostart=Mover para o in\u00edcio -dialog.rearrangephotos.toend=Mover para o fim -dialog.rearrangephotos.nosort=N\u00e3o ordenar -dialog.rearrangephotos.sortbyfilename=Ordenar pelo nome do arquivo -dialog.rearrangephotos.sortbytime=Ordenar pela hora +dialog.rearrange.tostart=Mover para o in\u00edcio +dialog.rearrange.toend=Mover para o fim +dialog.rearrange.tonearest=Cada um para o ponto da rota mais pr\u00f3ximo +dialog.rearrange.nosort=N\u00e3o ordenar +dialog.rearrange.sortbyfilename=Ordenar pelo nome do arquivo +dialog.rearrange.sortbyname=Ordenar pelo nome +dialog.rearrange.sortbytime=Ordenar pela hora dialog.compress.closepoints.title=Remo\u00e7\u00e3o de ponto pr\u00f3ximo dialog.compress.closepoints.paramdesc=Fator de deslocamento dialog.compress.wackypoints.title=Remo\u00e7\u00e3o de ponto exc\u00eantrica @@ -852,7 +853,6 @@ error.playaudiofailed=Falha ao reproduzir arquivo de \u00e1udio error.cache.notthere=A paste de cache de fundos n\u00e3o foi encontrada error.cache.empty=A pasta de cache de fundos est\u00e1 vazia error.cache.cannotdelete=Nenhum fundo pode ser removido -error.interpolate.invalidparameter=O n\u00famero de pontos deve estar entre 1 e 1000 error.learnestimationparams.failed=N\u00e3o foi poss\u00edvel aprender par\u00e2metros desta rota.\nTente baixar mais rotas. error.tracksplit.nosplit=A rota n\u00e3o pode ser dividida. error.downloadsrtm.nocache=Os arquivos n\u00e3o puderam ser salvos.\nPor favor, verifique a cache do disco. diff --git a/tim/prune/lang/prune-texts_ro.properties b/tim/prune/lang/prune-texts_ro.properties index b94b88c..e734fb7 100644 --- a/tim/prune/lang/prune-texts_ro.properties +++ b/tim/prune/lang/prune-texts_ro.properties @@ -6,24 +6,21 @@ menu.file=Fi\u015fier menu.file.addphotos=Adaug\u0103 foto menu.file.recentfiles=Fi\u015fiere recente menu.file.save=Salvare -menu.file.exit=Iesire +menu.file.exit=Ie\u015fire +menu.online=Internet menu.track=Traseu menu.track.undo=Anulare menu.track.clearundo=\u015etergere lista de anulari menu.track.deletemarked=\u015etergere puncte marcate -menu.track.rearrange=Rearanjare waypoint -menu.track.rearrange.start=Toate la inceputul fi\u015fierului -menu.track.rearrange.end=Toate la sfarsitul fi\u015fierului -menu.track.rearrange.nearest=Fiecare la punctul cel mai apropiat al traseului menu.range=Interval menu.range.all=Selectare toate menu.range.none=Nu selecta niciun punct menu.range.start=Seteaza inceputul selectiei menu.range.end=Seteaza sfarsitul selectiei -menu.range.average=Mediere selectie -menu.range.reverse=Inversare selectie +menu.range.average=Mediere selec\u0163ie +menu.range.reverse=Inversare selec\u0163ie menu.range.mergetracksegments=Unire segmente traseu -menu.range.cutandmove=Taiere si mutare selectie +menu.range.cutandmove=Taiere si mutare selec\u0163ie menu.point=Punct menu.point.editpoint=Editare punct menu.point.deletepoint=\u015etergere punct @@ -38,6 +35,7 @@ menu.view.browser.mapquest=Mapquest menu.view.browser.yahoo=Harti Yahoo menu.view.browser.bing=Harti Bing menu.settings=Set\u0103ri +menu.settings.onlinemode=\u00cencarc\u0103 h\u0103r\u021bi menu.help=Ajutor # Popup menu for map menu.map.zoomin=Apropie @@ -53,6 +51,7 @@ menu.map.editmode=Mod de editare # Alt keys for menus altkey.menu.file=F +altkey.menu.online=N altkey.menu.track=T altkey.menu.range=I altkey.menu.point=P @@ -87,24 +86,37 @@ function.deleterange=\u015etergere gama function.interpolate=Interpolare function.addtimeoffset=Adaug\u0103 decalaj timp function.addaltitudeoffset=Adaug\u0103 decalaj altitudine +function.rearrangewaypoints=Rearanjare waypoint function.findwaypoint=Gasire waypoint function.charts=Grafice function.show3d=Vizualizare arborescenta function.distances=Distan\u0163e function.fullrangedetails=Informa\u0163ie complet -function.loadaudio=Adaug\u0103 audio -function.setmapbg=Fundal +function.getgpsies=\u00cencarc\u0103 trassee Gpsies +function.uploadgpsies=Trimite date spre Gpsies +function.downloadsrtm=\u00cencarc\u0103 date SRTM +function.estimatetime=Estimare durat\u0103 +function.setmapbg=Seteaza harta +function.selectsegment=Selectare segment curent function.setcolours=Selectare culorile function.setlanguage=Selectare limba function.connecttopoint=Conecteaza la punct function.disconnectfrompoint=Deconecteaza de la punct function.removephoto=Elimina foto function.correlatephotos=Corelare fotografii +function.rearrangephotos=Rearanjare fotografii +function.rotatephotoleft=Roti foto la st\u00e2nga +function.rotatephotoright=Roti foto la dreapta +function.photopopup=Arat\u0103 foto +function.loadaudio=Adaug\u0103 audio +function.removeaudio=Elimina audio +function.playaudio=Redare audio function.help=Ajutor function.showkeys=Arat\u0103 tastele scurt\u0103turi function.about=Despre GpsPrune function.checkversion=Verific\u0103 pentru o versiune noua function.saveconfig=Salvare set\u0103ri +function.diskcache=Salvare harti function.getweatherforecast=Prognoz\u0103 meteo # Dialogs @@ -114,8 +126,9 @@ dialog.openappend.title=Adauga la datele existente dialog.openappend.text=Adauga la datele deja incarcate? dialog.deletepoint.title=\u015eterge Punct dialog.deletepoint.deletephoto=\u015eterg fotografiile atasate acestui punct? -dialog.deletephoto.title=\u015eterge Foto +dialog.deletephoto.title=\u015eterge foto dialog.deletephoto.deletepoint=\u015eterg punct atasat acestei fotografii? +dialog.deleteaudio.deletepoint=\u015eterg punct atasat acestei audio? dialog.openoptions.title=Optiuni deschidere dialog.load.table.field=Cimp dialog.load.table.datatype=Tip data @@ -129,30 +142,55 @@ dialog.delimiter.other=Alte dialog.openoptions.deliminfo.records=inregistrari, cu dialog.openoptions.deliminfo.fields=cimpuri dialog.openoptions.deliminfo.norecords=Nu sunt inregistrari +dialog.openoptions.altitudeunits=Unit\u0103\u0163i de altitudini +dialog.openoptions.speedunits=Unit\u0103\u0163i de viteza +dialog.openoptions.vertspeedunits=Unit\u0103\u0163i de viteza vertical\u0103 dialog.selecttracks.noname=F\u0103r\u0103 nume dialog.jpegload.subdirectories=Include subdirectori dialog.jpegload.loadjpegswithoutcoords=Include fotografii fara coordonate dialog.jpegload.loadjpegsoutsidearea=Include fotografii din afara zonei curente -dialog.jpegload.progress.title=Incarcare fotografii +dialog.jpegload.progress.title=\u00cenc\u0103rcare fotografii dialog.jpegload.progress=Va rog sa asteptati, caut fotografiile dialog.gpsload.nogpsbabel=Nu gasesc programul gpsbabel. Continui ? dialog.gpsload.device=Nume dispozitiv dialog.gpsload.format=Format -dialog.gpsload.getwaypoints=Incarcare waypoints +dialog.gpsload.getwaypoints=\u00cencarc\u0103 waypoints +dialog.gpsload.gettracks=\u00cencarc\u0103 trasee +dialog.gpsload.save=Salvare fi\u015fier +dialog.gpssend.sendwaypoints=Trimite waypoints +dialog.gpssend.sendtracks=Trimite trasee dialog.gpssend.trackname=Nume traseu dialog.gpsbabel.filters=Filtre -dialog.gpsbabel.filter.simplify=Simplifica +dialog.addfilter.title=Adaug\u0103 filtru +dialog.gpsbabel.filter.discard=Arunc\u0103 +dialog.gpsbabel.filter.simplify=Simplific\u0103 dialog.gpsbabel.filter.distance=Distan\u0163\u0103 +dialog.gpsbabel.filter.interpolate=Interpolare +dialog.gpsbabel.filter.discard.intro=Arunc\u0103 puncte dac\u0103 +dialog.gpsbabel.filter.discard.hdop=Hdop > +dialog.gpsbabel.filter.discard.vdop=Vdop > dialog.gpsbabel.filter.discard.numsats=Num\u0103r de sateli\u0163i < +dialog.gpsbabel.filter.simplify.maxpoints=Num\u0103r de puncte < +dialog.gpsbabel.filter.simplify.length=diferen\u0163\u0103 de lungime +dialog.gpsbabel.filter.distance.distance=Dac\u0103 distan\u0163\u0103 < +dialog.gpsbabel.filter.distance.time=\u0219i diferen\u0163\u0103 de timp < +dialog.gpsbabel.filter.interpolate.distance=Dac\u0103 distan\u0163\u0103 > +dialog.gpsbabel.filter.interpolate.time=sau diferen\u0163\u0103 de timp > dialog.saveoptions.title=Salvare fi\u015fier dialog.save.table.field=Cimp +dialog.save.table.hasdata=Date dialog.save.table.save=Salvare +dialog.save.coordinateunits=Format coordonate +dialog.save.altitudeunits=Unit\u0103\u0163i de altitudini +dialog.save.timestampformat=Format de timp dialog.save.overwrite.title=Fi\u015fierul exist\u0103 dialog.save.overwrite.text=Fi\u015fierul exist\u0103. \u00cel suprascriu? dialog.exportkml.text=Titlu +dialog.exportkml.imagesize=Dimensiune imaginii dialog.exportkml.trackcolour=Culoarea liniei dialog.exportgpx.name=Nume dialog.exportgpx.desc=Descriere +dialog.exportgpx.encoding=Codare dialog.exportgpx.encoding.system=Sistem dialog.exportgpx.encoding.utf8=UTF-8 dialog.exportpov.font=Fontul @@ -164,19 +202,30 @@ dialog.3d.useterrain=Arat\u0103 teren dialog.3d.terraingridsize=Dimensiune a grilei dialog.exportpov.baseimage=Imagine cartografice dialog.baseimage.title=Imagine cartografice +dialog.baseimage.zoom=Nivel de zoom +dialog.baseimage.incomplete=Imagine incomplet\u0103 dialog.baseimage.tiles=Tigla -dialog.exportsvg.phi=Azimut \u03D5 -dialog.exportsvg.theta=\u00cenclina\u0163ie \u03B8 +dialog.exportsvg.phi=Azimut \u03d5 +dialog.exportsvg.theta=\u00cenclina\u0163ie \u03b8 +dialog.pointtype.track=Puncte de traseu +dialog.pointtype.waypoint=Waypoints +dialog.pointtype.photo=Puncte foto +dialog.pointtype.audio=Puncte audio +dialog.pointtype.selection=Doar interval dialog.undo.title=Anulare +dialog.pointedit.title=Editare punct dialog.pointedit.intro=V\u0103 rog selecta\u0163i r\u00e2ndul care va fi editat dialog.pointedit.table.field=Cimp dialog.pointedit.table.value=Valoare +dialog.pointnameedit.name=Nume de waypoint dialog.pointnameedit.uppercase=Litere MARI dialog.pointnameedit.lowercase=Litere mici dialog.addtimeoffset.days=Zile dialog.addtimeoffset.hours=Ore dialog.addtimeoffset.minutes=Minute dialog.findwaypoint.search=C\u0103utare +dialog.saveexif.table.photoname=Nume +dialog.saveexif.title=Salvare Exif dialog.saveexif.table.status=Stare dialog.saveexif.table.save=Salveaza dialog.saveexif.photostatus.connected=Conectat @@ -185,21 +234,64 @@ dialog.saveexif.photostatus.modified=Modificat dialog.saveexif.overwrite=Suprascrie fi\u015fiere dialog.charts.xaxis=Axa X dialog.charts.yaxis=Axa Y +dialog.charts.output=Rezultat +dialog.charts.svgwidth=L\u0103\u021bime SVG +dialog.charts.svgheight=\u00cen\u0103l\u021bime SVG +dialog.distances.column.from=De punct +dialog.distances.column.to=Spre punct dialog.distances.currentpoint=Punct curent +dialog.estimatetime.details=Detalii +dialog.estimatetime.parameters=Parametrii +dialog.estimatetime.parameters.timefor=Durata pentru +dialog.estimatetime.results=Rezultate +dialog.estimatetime.results.estimatedtime=Durata estimat\u0103 +dialog.estimatetime.results.actualtime=Durata (measured) +dialog.learnestimationparams.averageerror=Eroare estimat +dialog.learnestimationparams.combinedresults=Rezultate combinat +dialog.learnestimationparams.weight.current=curente +dialog.learnestimationparams.weight.calculated=calculate +dialog.addmapsource.sourcename=Nume dialog.addmapsource.noname=F\u0103r\u0103 nume dialog.gpsies.column.name=Nume dialog.gpsies.column.length=Lungime dialog.gpsies.description=Descriere dialog.gpsies.nodescription=F\u0103r\u0103 descriere +dialog.gpsies.nonefound=Nu a fost g\u0103sit +dialog.gpsies.username=Gpsies username +dialog.gpsies.password=Gpsies parol\u0103 +dialog.gpsies.keepprivate=Traseu privat +dialog.gpsies.activities=Activit\u0103\u0163i dialog.wikipedia.column.name=Nume dialog.wikipedia.column.distance=Distan\u0163\u0103 +dialog.wikipedia.nonefound=Nu a fost g\u0103sit +dialog.correlate.select.photoname=Nume +dialog.correlate.select.timediff=Diferenta de timp dialog.correlate.options.offset.hours=ore, dialog.correlate.options.offset.minutes=minute, dialog.correlate.options.offset.seconds=secunde +dialog.correlate.options.correlate=Corelare +dialog.correlate.timestamp.beginning=\u00cenceptutul +dialog.correlate.timestamp.middle=Mijlocul +dialog.correlate.timestamp.end=Sf\u00e2r\u015fitul +dialog.correlate.select.audioname=Nume +dialog.rearrange.tostart=Toate la inceputul fi\u015fierului +dialog.rearrange.toend=Toate la sfarsitul fi\u015fierului +dialog.rearrange.tonearest=Fiecare la punctul cel mai apropiat al traseului +dialog.rearrange.nosort=Nu sunt sortate +dialog.rearrange.sortbyfilename=Sorta dup\u0103 nume de fi\u015fier +dialog.rearrange.sortbyname=Sorta dup\u0103 nume +dialog.rearrange.sortbytime=Sorta dup\u0103 timp dialog.pastecoordinates.coords=Coordonate dialog.about.version=Versiunea +dialog.about.build=Construi +dialog.about.languages=Limbi dialog.about.systeminfo=Informa\u0163ii a sistemului dialog.about.systeminfo.os=Sistem de operare +dialog.about.systeminfo.java3d=Java3d instalat +dialog.about.systeminfo.povray=Povray instalat +dialog.about.systeminfo.exiftool=Exiftool instalat +dialog.about.systeminfo.gpsbabel=Gpsbabel instalat +dialog.about.systeminfo.gnuplot=Gnuplot instalat dialog.about.systeminfo.exiflib=Bibliotec\u0103 Exif dialog.about.systeminfo.exiflib.internal=Intern dialog.about.systeminfo.exiflib.internal.failed=Intern (absent) @@ -210,15 +302,31 @@ dialog.about.no=Nu dialog.about.readme=Cite\u015fte-m\u0103 dialog.checkversion.releasedate1=Aceasta versiune noua a fost lansapa pe dialog.checkversion.releasedate2=. +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.setcolours.background=Fund dialog.setcolours.lines=Linii dialog.setcolours.primary=Primar dialog.setcolours.secondary=Secundar dialog.setcolours.point=Puncte +dialog.setcolours.selection=Selec\u0163ie dialog.setcolours.text=Text +dialog.colourchooser.title=Selectare culoare dialog.colourchooser.red=Ro\u0219u dialog.colourchooser.green=Verde dialog.colourchooser.blue=Albastru +dialog.colourer.type.none=Nimic +dialog.setlanguage.language=Limb\u0103 +dialog.setlanguage.languagefile=Fi\u015fier de limba +dialog.diskcache.table.tiles=Tigla +dialog.searchwikipedianames.search=C\u0103utare : +dialog.weather.location=Loca\u0163ie +dialog.weather.sunrise=R\u0103s\u0103rit +dialog.weather.sunset=Apus de soare +dialog.weather.currentforecast=Vremea curent\u0103 +dialog.weather.day.now=Vremea curent\u0103 dialog.weather.day.today=Ast\u0103zi dialog.weather.day.tomorrow=M\u00e2ine dialog.weather.day.monday=Luni @@ -233,6 +341,13 @@ dialog.weather.day.sunday=Duminic\u0103 confirm.loadfile=Date incarcate din fi\u015fier confirm.save.ok1=Salvat cu succes confirm.save.ok2=puncte \u00een +confirm.media.connect=foto/audio conectat +confirm.photo.disconnect=foto deconectat +confirm.audio.disconnect=audio deconectat +confirm.media.removed=\u0219ters +confirm.running=Executare ... +confirm.downloadsrtm=S-au desc\u0103rcat %d fi\u015fiere +confirm.downloadsrtm.1=S-au desc\u0103rcat %d fi\u015fier # Tips tip.title=Indiciu @@ -258,7 +373,7 @@ button.select=Selectare button.selectall=Selecteaza tot button.selectnone=Deselecteaza tot button.load=Descarca -button.upload=Inc\u0103rca +button.upload=Trimite button.guessfields=Ghici cimpuri button.check=Verifica button.delete=\u015etergere @@ -281,27 +396,43 @@ filetype.audio=Fi\u015fiere MP3, OGG, WAV details.trackdetails=Detalii traseul details.track.points=Puncte details.pointdetails=Detalii punctul +details.index.selected=Punct +details.index.of=de +details.photofile=Fi\u015fier details.rangedetails=Detalii intervalul details.range.selected=Selectat details.range.to=la details.altitude.to=la +details.range.climb=Urcare +details.range.descent=Cobor\u00e2re details.coordformat=Format coordonate -details.distanceunits=Unitati de distan\u0163e +details.distanceunits=Unit\u0103\u0163i de distan\u0163e display.range.time.secs=s display.range.time.mins=m display.range.time.hours=o display.range.time.days=z details.range.avespeed=Viteza medie details.range.maxspeed=Viteza maxim\u0103 +details.range.numsegments=Num\u0103r de segmente +details.range.pace=Ritm +details.range.gradient=Gradient +details.lists.waypoints=Waypoints details.lists.photos=Foto-uri details.lists.audio=Audio +details.photodetails=Detalii foto +details.photo.loading=\u00cenc\u0103rcare +details.photo.bearing=Direc\u0163ie +details.media.connected=Conectat details.audiodetails=Detalii audio +details.audio.file=Fi\u015fier # Field names fieldname.latitude=Latitudine fieldname.longitude=Longitudine fieldname.altitude=Altitudine +fieldname.timestamp=Timp fieldname.time=Timp +fieldname.date=Data fieldname.waypointname=Nume fieldname.waypointtype=Tip fieldname.newsegment=Segment @@ -322,22 +453,52 @@ units.kilometresperhour=km pe or\u0103 units.kilometresperhour.short=km/o units.miles=Mil\u0103 units.miles.short=mi +units.milesperhour=mil\u0103 pe or\u0103 +units.milesperhour.short=mpo units.nauticalmiles=Mil\u0103 marin\u0103 units.nauticalmiles.short=mm units.nauticalmilesperhour.short=kn +units.metrespersec=metri pe secund +units.metrespersec.short=m/s units.hours=ore units.minutes=minute units.seconds=secunde +units.degminsec=Grad-min-sec +units.degmin=Grad-min +units.deg=Grad +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=\ufeff\u0219i +logic.and=\u0219i logic.or=sau +# External urls +wikipedia.lang=ro +openweathermap.lang=ro + # Cardinals for 3d plots cardinal.n=N cardinal.s=S cardinal.e=E cardinal.w=V -wikipedia.lang=ro -openweathermap.lang=ro +# Undo operations +undo.load=\u00cencarc\u0103 date +undo.loadphotos=\u00cencarc\u0103 fotografii +undo.loadaudios=\u00cencarc\u0103 audio +undo.editpoint=Editare punct +undo.deletepoint=\u015eterge punct +undo.removephoto=Elimina foto +undo.removeaudio=Elimina audio +undo.deleterange=\u015eterge interval +undo.deletemarked=\u015eterge puncte +undo.connect=conecteaza +undo.disconnect=deconecteaza +undo.rotatephoto=roti foto + +# Error messages +error.function.notavailable.title=Func\u021bie indisponibil\u0103 \ No newline at end of file diff --git a/tim/prune/lang/prune-texts_ru.properties b/tim/prune/lang/prune-texts_ru.properties index 3481dba..ec30638 100644 --- a/tim/prune/lang/prune-texts_ru.properties +++ b/tim/prune/lang/prune-texts_ru.properties @@ -13,10 +13,7 @@ menu.track.undo=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 menu.track.markrectangle=\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u0442\u043e\u0447\u043a\u0438 \u0432 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0435 menu.track.deletemarked=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438 -menu.track.rearrange=\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u044b -menu.track.rearrange.start=\u0412\u0441\u0435 \u0432 \u043d\u0430\u0447\u0430\u043b\u043e \u0444\u0430\u0439\u043b\u0430 -menu.track.rearrange.end=\u0412\u0441\u0435 \u0432 \u043a\u043e\u043d\u0435\u0446 \u0444\u0430\u0439\u043b\u0430 -menu.track.rearrange.nearest=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u043a \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0439 +function.rearrangewaypoints=\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u044b 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 @@ -385,11 +382,12 @@ dialog.correlate.audioselect.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0 dialog.correlate.select.audioname=\u0418\u043c\u044f \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 dialog.correlate.select.audiolater=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u0437\u0434\u043d\u0435\u0435 dialog.rearrangephotos.desc=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438 \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u0444\u043e\u0442\u043e -dialog.rearrangephotos.tostart=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043d\u0430\u0447\u0430\u043b\u043e -dialog.rearrangephotos.toend=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0435\u0446 -dialog.rearrangephotos.nosort=\u041d\u0435 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c -dialog.rearrangephotos.sortbyfilename=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0444\u0430\u0439\u043b\u0430 -dialog.rearrangephotos.sortbytime=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 +dialog.rearrange.tostart=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043d\u0430\u0447\u0430\u043b\u043e +dialog.rearrange.toend=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0435\u0446 +dialog.rearrange.tonearest=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u043a \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0439 +dialog.rearrange.nosort=\u041d\u0435 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c +dialog.rearrange.sortbyfilename=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0444\u0430\u0439\u043b\u0430 +dialog.rearrange.sortbytime=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 dialog.compress.closepoints.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0431\u043b\u0438\u0436\u0435\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a dialog.compress.closepoints.paramdesc=\u0420\u0430\u0437\u043c\u0430\u0445 dialog.compress.wackypoints.title=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 "\u0448\u0430\u043b\u044c\u043d\u044b\u0445"(\u043d\u0435\u043e\u0431\u044b\u0447\u043d\u044b\u0445) \u0442\u043e\u0447\u0435\u043a @@ -794,7 +792,7 @@ error.load.noxmlinzip=\u0412 zip-\u0430\u0440\u0445\u0438\u0432\u0435 \u043d\u04 error.load.othererror=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438 \u0444\u0430\u0439\u043b\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=JEPG-\u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b +error.jpegload.nojpegsfound=JPEG-\u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b error.jpegload.nogpsfound=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 GPS-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f error.jpegload.exifreadfailed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c Exif-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u041d\u0435\u0442 Exif-\u200b\u200b\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u200b\u200b\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c\n\u0431\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u043a\u0438\u0445-\u043b\u0438\u0431\u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0445 \u0438\u043b\u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a. error.audioload.nofilesfound=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b @@ -820,5 +818,4 @@ error.playaudiofailed=\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u043e\u0441\u0 error.cache.notthere=\u041f\u0430\u043f\u043a\u0430 \u043a\u044d\u0448\u0430 \u0441 \u0442\u0430\u0439\u043b\u0430\u043c\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 error.cache.empty=\u041f\u0430\u043f\u043a\u0430 \u043a\u044d\u0448\u0430 \u0441 \u0442\u0430\u0439\u043b\u0430\u043c\u0438 \u043f\u0443\u0441\u0442\u0430 error.cache.cannotdelete=\u041d\u0435\u0442 \u0442\u0430\u0439\u043b\u043e\u0432, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f -error.interpolate.invalidparameter=\u041d\u043e\u043c\u0435\u0440 \u0442\u043e\u0447\u043a\u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0442 1 \u0434\u043e 1000 error.tracksplit.nosplit=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u0440\u0435\u043a diff --git a/tim/prune/lang/prune-texts_sv.properties b/tim/prune/lang/prune-texts_sv.properties index 7f421c8..ecde4b7 100644 --- a/tim/prune/lang/prune-texts_sv.properties +++ b/tim/prune/lang/prune-texts_sv.properties @@ -12,10 +12,10 @@ menu.track.undo=\u00c5ngra menu.track.clearundo=Rensa \u00e5ngra-historik menu.track.markrectangle=Markera punkter i rektangel menu.track.deletemarked=Radera markerade punkter -menu.track.rearrange=Ordna waypoints -menu.track.rearrange.start=Alla till b\u00f6rjan av fil -menu.track.rearrange.end=Alla till slut av fil -menu.track.rearrange.nearest=Varje till n\u00e4rmaste sp\u00e5rpunkt +function.rearrangewaypoints=Ordna waypoints +dialog.rearrange.tostart=Alla till b\u00f6rjan av fil +dialog.rearrange.toend=Alla till slut av fil +dialog.rearrange.tonearest=Varje till n\u00e4rmaste sp\u00e5rpunkt menu.range=Intervall menu.range.all=V\u00e4lj alla menu.range.none=V\u00e4lj ingen diff --git a/tim/prune/lang/prune-texts_tr.properties b/tim/prune/lang/prune-texts_tr.properties index 4baf0de..d236ff3 100644 --- a/tim/prune/lang/prune-texts_tr.properties +++ b/tim/prune/lang/prune-texts_tr.properties @@ -17,10 +17,10 @@ function.interpolate=\u0130nterpolasyon menu.range.average=Se\u00e7me ortala menu.range.reverse=S\u0131ra tersine \u00e7evir menu.range.mergetracksegments=\u0130z par\u00e7alar\u0131 birle\u015ftir -menu.track.rearrange=Yol noktalar\u0131 yeniden diz -menu.track.rearrange.start=Hepsini dosyan\u0131n ba\u015f\u0131na -menu.track.rearrange.end=Hepsini dosyan\u0131n sonuna -menu.track.rearrange.nearest=En yak\u0131n iz noktaya +function.rearrangewaypoints=Yol noktalar\u0131 yeniden diz +dialog.rearrange.tostart=Hepsini dosyan\u0131n ba\u015f\u0131na +dialog.rearrange.toend=Hepsini dosyan\u0131n sonuna +dialog.rearrange.tonearest=En yak\u0131n iz noktaya menu.range.cutandmove=Se\u00e7me kes ve ta\u015f\u0131 menu.range=S\u0131ra menu.point=Nokta diff --git a/tim/prune/lang/prune-texts_uk.properties b/tim/prune/lang/prune-texts_uk.properties index 6f74d55..3b47eea 100644 --- a/tim/prune/lang/prune-texts_uk.properties +++ b/tim/prune/lang/prune-texts_uk.properties @@ -12,10 +12,10 @@ menu.track.undo=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438 menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043c\u0456\u043d menu.track.markrectangle=\u041f\u043e\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u0443 \u043f\u0440\u044f\u043c\u043e\u043a\u0443\u0442\u043d\u0438\u043a\u0443 menu.track.deletemarked=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u043f\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u0456 \u0442\u043e\u0447\u043a\u0438 -menu.track.rearrange=\u041f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0438 -menu.track.rearrange.start=\u0423\u0441\u0435 \u043d\u0430 \u043f\u043e\u0447\u0430\u0442\u043e\u043a \u0444\u0430\u0439\u043b\u0443 -menu.track.rearrange.end=\u0423\u0441\u0435 \u043d\u0430 \u043a\u0456\u043d\u0435\u0446\u044c \u0444\u0430\u0439\u043b\u0443 -menu.track.rearrange.nearest=\u041f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u0434\u043e \u043d\u0430\u0439\u0431\u043b\u0438\u0436\u0447\u043e\u0457 +function.rearrangewaypoints=\u041f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0438 +dialog.rearrange.tostart=\u0423\u0441\u0435 \u043d\u0430 \u043f\u043e\u0447\u0430\u0442\u043e\u043a \u0444\u0430\u0439\u043b\u0443 +dialog.rearrange.toend=\u0423\u0441\u0435 \u043d\u0430 \u043a\u0456\u043d\u0435\u0446\u044c \u0444\u0430\u0439\u043b\u0443 +dialog.rearrange.tonearest=\u041f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u0434\u043e \u043d\u0430\u0439\u0431\u043b\u0438\u0436\u0447\u043e\u0457 menu.range=\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b menu.range.all=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u0443\u0441\u0456 menu.range.none=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438 \u0432\u0438\u0431\u0456\u0440\u043a\u0443 diff --git a/tim/prune/lang/prune-texts_zh.properties b/tim/prune/lang/prune-texts_zh.properties index 1fcca74..40319fa 100644 --- a/tim/prune/lang/prune-texts_zh.properties +++ b/tim/prune/lang/prune-texts_zh.properties @@ -13,10 +13,6 @@ menu.track.undo=\u64a4\u9500 menu.track.clearundo=\u6e05\u9664\u64a4\u9500\u6e05\u5355 menu.track.markrectangle=\u6807\u8bb0\u9009\u53d6\u533a\u57df\u5185\u7684\u70b9 menu.track.deletemarked=\u5220\u9664\u5df2\u6807\u8bb0\u8f68\u8ff9\u70b9 -menu.track.rearrange=\u91cd\u65b0\u6392\u5217\u822a\u70b9 -menu.track.rearrange.start=\u81f3\u8d77\u59cb\u4f4d\u7f6e -menu.track.rearrange.end=\u81f3\u672b\u4f4d\u7f6e -menu.track.rearrange.nearest=\u81f3\u6700\u8fd1\u8f68\u8ff9\u70b9 menu.range=\u822a\u6bb5 menu.range.all=\u5168\u9009 menu.range.none=\u64a4\u9500\u9009\u62e9 @@ -92,8 +88,10 @@ function.compress=\u538b\u7f29\u8f68\u8ff9(\u6807\u8bb0\u8981\u5220\u9664\u822a\ function.deleterange=\u5220\u9664\u8f68\u8ff9\u70b9\u6bb5 function.croptrack=\u4fee\u526a\u8f68\u8ff9 function.interpolate=\u91cd\u53e0\u8f68\u8ff9\u70b9 +function.deletebydate=\u6839\u636e\u65e5\u671f\u5220\u9664\u8f68\u8ff9\u70b9 function.addtimeoffset=\u52a0\u5165\u65f6\u95f4\u5dee function.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb +function.rearrangewaypoints=\u91cd\u65b0\u6392\u5217\u822a\u70b9 function.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4 function.deletefieldvalues=\u5220\u9664\u5b57\u6bb5\u503c function.findwaypoint=\u67e5\u627e\u822a\u70b9 @@ -106,6 +104,7 @@ function.estimatetime=\u4f30\u8ba1\u65f6\u95f4 function.learnestimationparams=\u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4 function.setmapbg=\u80cc\u666f\u5730\u56fe function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84 +function.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 @@ -141,6 +140,7 @@ function.saveconfig=\u4fdd\u5b58\u8bbe\u7f6e function.diskcache=\u4fdd\u5b58\u5730\u56fe function.managetilecache=\u7ba1\u7406\u5730\u56fe\u533a\u57df\u6570\u636e\u7f13\u5b58 function.getweatherforecast=\u83b7\u53d6\u5929\u6c14\u9884\u62a5 +function.setaltitudetolerance=\u8bbe\u7f6e\u9ad8\u5ea6\u516c\u5dee # Dialogs dialog.exit.confirm.title=\u9000\u51fa @@ -379,6 +379,7 @@ 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 dialog.correlate.notimestamps=\u6570\u636e\u70b9\u4e2d\u65e0\u65f6\u95f4\u4fe1\u606f\uff0c\u7167\u7247\u65e0\u6cd5\u94fe\u63a5 dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u7167\u7247\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f dialog.correlate.nouncorrelatedaudios=\u6240\u6709\u97f3\u9891\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f @@ -412,12 +413,15 @@ dialog.correlate.timestamp.end=\u7ed3\u675f dialog.correlate.audioselect.intro=\u9009\u62e9\u4ee5\u4e0b\u58f0\u97f3\u6587\u4ef6\u4f5c\u4e3a\u65f6\u95f4\u504f\u5dee dialog.correlate.select.audioname=\u58f0\u97f3\u6587\u4ef6\u540d\u5b57 dialog.correlate.select.audiolater=\u58f0\u97f3\u5ef6\u8fdf +dialog.rearrangewaypoints.desc=\u9009\u62e9\u76ee\u7684\u5730\u5e76\u6392\u5217\u8def\u70b9 dialog.rearrangephotos.desc=\u9009\u62e9\u76ee\u7684\u5730\u53ca\u7167\u7247\u70b9\u6392\u5217\u987a\u5e8f -dialog.rearrangephotos.tostart=\u79fb\u5230\u5f00\u59cb -dialog.rearrangephotos.toend=\u79fb\u5230\u672b\u5c3e -dialog.rearrangephotos.nosort=\u4e0d\u6392\u5e8f -dialog.rearrangephotos.sortbyfilename=\u6309\u540d\u79f0\u6392\u5e8f -dialog.rearrangephotos.sortbytime=\u6309\u65f6\u95f4\u6392\u5e8f +dialog.rearrange.tostart=\u79fb\u5230\u5f00\u59cb +dialog.rearrange.toend=\u79fb\u5230\u672b\u5c3e +dialog.rearrange.tonearest=\u81f3\u6700\u8fd1\u8f68\u8ff9\u70b9 +dialog.rearrange.nosort=\u4e0d\u6392\u5e8f +dialog.rearrange.sortbyfilename=\u6309\u6587\u4ef6\u540d\u6392\u5e8f +dialog.rearrange.sortbyname=\u6309\u8def\u70b9\u540d\u6392\u5e8f +dialog.rearrange.sortbytime=\u6309\u65f6\u95f4\u6392\u5e8f dialog.compress.closepoints.title=\u90bb\u8fd1\u70b9\u5220\u9664 dialog.compress.closepoints.paramdesc=\u8303\u56f4\u7cfb\u6570 dialog.compress.wackypoints.title=\u5f02\u5e38\u70b9\u5220\u9664 @@ -515,6 +519,19 @@ dialog.colourchooser.title=\u8bf7\u9009\u62e9\u989c\u8272 dialog.colourchooser.red=\u7ea2 dialog.colourchooser.green=\u7eff dialog.colourchooser.blue=\u84dd +dialog.colourer.intro=\u53ef\u4ee5\u8d4b\u4e88\u8f68\u8ff9\u70b9\u4e0d\u540c\u7684\u989c\u8272 +dialog.colourer.type=\u7740\u8272\u6a21\u5f0f +dialog.colourer.type.none=\u65e0 +dialog.colourer.type.byfile=\u6309\u6587\u4ef6 +dialog.colourer.type.bysegment=\u6309\u8f68\u8ff9\u6bb5 +dialog.colourer.type.byaltitude=\u6309\u9ad8\u5ea6 +dialog.colourer.type.byspeed=\u6309\u901f\u5ea6 +dialog.colourer.type.byvertspeed=\u6309\u5782\u76f4\u901f\u5ea6 +dialog.colourer.type.bygradient=\u6309\u5761\u5ea6 +dialog.colourer.type.bydate=\u6309\u65e5\u671f +dialog.colourer.start=\u8d77\u59cb\u989c\u8272 +dialog.colourer.end=\u7ed3\u675f\u989c\u8272 +dialog.colourer.maxcolours=\u6700\u5927\u989c\u8272\u6570\u91cf dialog.setlanguage.firstintro=\u4f60\u53ef\u4ee5\u9009\u62e9\u5df2\u6709\u8bed\u8a00,

\u6216\u9009\u62e9\u5916\u6302\u8bed\u8a00\u5305 dialog.setlanguage.secondintro=\u8bf7\u4fdd\u5b58\u8bbe\u7f6e

\u5e76\u91cd\u542fGpsPrune\u4f7f\u8bbe\u7f6e\u751f\u6548 dialog.setlanguage.language=\u8bed\u8a00 @@ -560,7 +577,17 @@ dialog.weather.day.thursday=\u5468\u56db dialog.weather.day.friday=\u5468\u4e94 dialog.weather.day.saturday=\u5468\u516d dialog.weather.day.sunday=\u5468\u65e5 +dialog.weather.wind=\u98ce\u529b +dialog.weather.temp=\u6e29\u5ea6 +dialog.weather.humidity=\u6e7f\u5ea6 dialog.weather.creditnotice=\u5929\u6c14\u4fe1\u606f\u83b7\u53d6\u81eaopenweathermap.org\uff0c\u83b7\u53d6\u66f4\u591a\u5929\u6c14\u8be6\u60c5\u8bf7\u8bbf\u95ee\u7f51\u7ad9\u3002 +dialog.deletebydate.onlyonedate=\u6240\u6709\u8f68\u8ff9\u70b9\u90fd\u662f\u540c\u4e00\u5929\u7684 +dialog.deletebydate.intro=\u4f60\u53ef\u4ee5\u9009\u62e9\u4fdd\u7559\u6216\u5220\u9664\u67d0\u4e00\u5929\u7684\u8f68\u8ff9\u70b9 +dialog.deletebydate.nodate=\u6ca1\u6709\u65f6\u95f4\u6233 +dialog.deletebydate.column.keep=\u4fdd\u7559 +dialog.deletebydate.column.delete=\u5220\u9664 +dialog.setaltitudetolerance.text.metres=\u4e0d\u8d85\u8fc7\u6b64\u6570\u503c(\u7c73)\u7684\u9ad8\u5ea6\u53d8\u5316\u5c06\u88ab\u5ffd\u7565 +dialog.setaltitudetolerance.text.feet= # 3d window dialog.3d.title=GpsPrune 3D \u663e\u793a @@ -715,6 +742,7 @@ fieldname.longitude=\u7ecf\u5ea6 fieldname.altitude=\u9ad8\u5ea6 fieldname.timestamp=\u65f6\u95f4 fieldname.time=\u65f6\u95f4 +fieldname.date=\u65e5\u671f fieldname.waypointname=\u540d\u79f0 fieldname.waypointtype=\u7c7b\u578b fieldname.newsegment=\u6bb5 @@ -849,7 +877,6 @@ 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 error.cache.empty=\u533a\u57df\u6570\u636e\u6587\u4ef6\u5939\u7a7a error.cache.cannotdelete=\u65e0\u53ef\u5220\u9664\u533a\u57df\u6570\u636e -error.interpolate.invalidparameter=\u8f93\u5165\u70b9\u6570\u91cf\u5fc5\u987b\u57281\u52301000\u4e4b\u95f4 error.learnestimationparams.failed=\u65e0\u6cd5\u4ece\u6b64\u8f68\u8ff9\u5f97\u5230\u53c2\u6570\n \u5c1d\u8bd5\u8f7d\u5165\u66f4\u591a\u8f68\u8ff9 error.tracksplit.nosplit=\u6b64\u8f68\u8ff9\u65e0\u6cd5\u5206\u5272 error.downloadsrtm.nocache=\u6587\u4ef6\u65e0\u6cd5\u4fdd\u5b58\n\u8bf7\u68c0\u67e5\u78c1\u76d8\u7f13\u5b58 diff --git a/tim/prune/load/JpegLoader.java b/tim/prune/load/JpegLoader.java index a177eaa..672cd02 100644 --- a/tim/prune/load/JpegLoader.java +++ b/tim/prune/load/JpegLoader.java @@ -258,7 +258,7 @@ public class JpegLoader implements Runnable, Cancellable // Apply timestamp to photo and its point (if any) photo.setTimestamp(timestamp); if (photo.getDataPoint() != null) { - photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.FORMAT_ISO_8601), false); + photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.Format.ISO8601), false); } return photo; } diff --git a/tim/prune/readme.txt b/tim/prune/readme.txt index b9dd6c0..7b1c9c6 100644 --- a/tim/prune/readme.txt +++ b/tim/prune/readme.txt @@ -1,9 +1,9 @@ -GpsPrune version 16.3 -===================== +GpsPrune version 17 +=================== GpsPrune is an application for viewing, editing and managing coordinate data from GPS systems, including format conversion, charting and photo correlation. -Full details can be found at http://activityworkshop.net/software/gpsprune/ +Full details can be found at http://gpsprune.activityworkshop.net/ GpsPrune is copyright 2006-2014 activityworkshop.net and distributed under the terms of the Gnu GPL version 2. You may freely use the software, and may help others to freely use it too. For further information @@ -17,7 +17,7 @@ Running ======= To run GpsPrune from the jar file, simply call it from a command prompt or shell: - java -jar gpsprune_16.3.jar + java -jar gpsprune_17.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,34 +25,19 @@ in a file manager window to execute it. A shortcut, menu item, alias, desktop i or other link can of course be made should you wish. To specify a language other than the default, use an additional parameter, eg: - java -jar gpsprune_16.3.jar --lang=DE - - -New with version 16.3 -===================== -The following fixes were added since version 16.2: - - Fix for gpx caching of points which failed to load - - Additional newlines / tabs in gpx export - - API key for openweathermap.org - - Improvements to 3d terrain reflections - - Additional translations - -New with version 16.2 -===================== -The following fixes were added since version 16.1: - - Fix for Gpx-slicing UTF8 files - - Conversion of sunrise/sunset times to local timezone - - Removal of Cloudmade maps - - Additional translations + java -jar gpsprune_17.jar --lang=DE -New with version 16.1 -===================== -The following fixes were added since version 16: - - Caching of terrain information for three-dimensional views - - Additional translations - - Improved void filling by interpolation - - Remembering file type of imported files +New with version 17 +=================== +The following features were added since version 16: + - Colouring the track points according to various criteria (such as altitude, + speed, segment, file) in both the regular map view and the image export + - Marking points for deletion according to their date + - Select the current segment + - Adding an altitude tolerance to the climb and descent calculations + - Sorting waypoints by name or by timestamp + New with version 16 =================== The following features were added since version 15: @@ -64,11 +49,11 @@ The following features were added since version 15: - Function to download and save SRTM tiles New with version 15 -===================== +=================== The following features were added since version 14: - Extend povray output using map image on base plane - Export an image of the map and track at a selected zoom level - - Estimation of hiking times and learining of parameter values + - Estimation of hiking times and learning of parameter values - Allow altitude / speed profile to show any arbitrary field - Accept files dragged and dropped onto the GpsPrune window - Take account of timezone if present in track timestamps @@ -78,7 +63,7 @@ The following features were added since version 14: - Allow loading of speeds and vertical speeds from text files New with version 14 -===================== +=================== The following features were added since version 13: - Dragging of existing points - Creation of new points by dragging the halfway point between two points @@ -120,7 +105,6 @@ The following features were added since version 11: New with version 11 =================== - The following features were added since version 10: - Option to select which of the named tracks to load out of a gpx file or gps - Function to delete all values of a single field (eg all altitudes, all timestamps) @@ -132,7 +116,6 @@ The following features were added since version 10: New with version 10 =================== - The following features were added since version 9: - Function to lookup altitudes using SRTM data from the Space Shuttle - Choice between altitude profile and speed profile in main view @@ -144,7 +127,6 @@ The following features were added since version 9: New with version 9 ================== - The following features were added since version 8: - Ability to paste coordinates (eg from wikipedia or geocaching sites) to create new points - Configurable colour settings @@ -158,7 +140,6 @@ The following features were added since version 8: New with version 8 ================== - The following features were added since version 7: - Loading of NMEA files (with suffix .nmea) - Loading of nearby tracks from gpsies.com @@ -172,7 +153,6 @@ The following features were added since version 7: New with version 7 ================== - The following features were added since version 6: - Loading of KMZ files and zipped GPX - Improved compression functions with four configurable algorithms @@ -184,7 +164,6 @@ The following features were added since version 6: New with version 6 ================== - The following features were added since version 5: - Map view using OpenStreetMap images is now integrated in the main window, with control for map transparency - Pov export has new option to use sphere sweeps for better appearance @@ -197,8 +176,7 @@ The following features were added since version 5: New with version 5 ================== - -The following features were added since version 4.1: +The following features were added since version 4: - New map window in the View menu, showing points overlaid on OpenStreetMap images - New function to launch a browser showing the area in either Google Maps or OpenStreetMap - Handling of track segments, including loading, saving and exporting, and preservation during edits and undos @@ -209,7 +187,6 @@ The following features were added since version 4.1: New with version 4 ================== - The following features were added since version 3: - Automatic correlation of photos with points based on timestamps - Manual disconnection of photos from points @@ -222,7 +199,6 @@ The following features were added since version 3: New with version 3 ================== - The following features were added since version 2: - Loading of GPX and KML files - Loading of jpeg photos with or without coordinate data @@ -234,7 +210,6 @@ The following features were added since version 2: New with version 2 ================== - The following features were added since version 1: - Display of data in 3d view using Java3D library - Export of 3d model to POV format for rendering by povray @@ -244,7 +219,6 @@ The following features were added since version 1: Features of version 1 ===================== - The following features were included in version 1: - Loading of text files, display in overhead and profile views - Display of track details such as distances, speeds diff --git a/tim/prune/save/FileSaver.java b/tim/prune/save/FileSaver.java index b4a73d4..ecf61d3 100644 --- a/tim/prune/save/FileSaver.java +++ b/tim/prune/save/FileSaver.java @@ -74,7 +74,7 @@ public class FileSaver private static final int[] FORMAT_COORDS = {Coordinate.FORMAT_NONE, Coordinate.FORMAT_DEG_MIN_SEC, Coordinate.FORMAT_DEG_MIN, Coordinate.FORMAT_DEG}; private static final Unit[] UNIT_ALTS = {null, UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_FEET}; - private static final int[] FORMAT_TIMES = {Timestamp.FORMAT_ORIGINAL, Timestamp.FORMAT_LOCALE, Timestamp.FORMAT_ISO_8601}; + private static final Timestamp.Format[] FORMAT_TIMES = {Timestamp.Format.ORIGINAL, Timestamp.Format.LOCALE, Timestamp.Format.ISO8601}; /** @@ -437,7 +437,7 @@ public class FileSaver } } // Get timestamp format - int timestampFormat = Timestamp.FORMAT_ORIGINAL; + Timestamp.Format timestampFormat = Timestamp.Format.ORIGINAL; for (int i=0; i<_timestampUnitsRadios.length; i++) { if (_timestampUnitsRadios[i].isSelected()) { @@ -572,7 +572,7 @@ public class FileSaver * @param inTimestampFormat timestamp format */ private void saveField(StringBuffer inBuffer, DataPoint inPoint, Field inField, - int inCoordFormat, Unit inAltitudeUnit, int inTimestampFormat) + int inCoordFormat, Unit inAltitudeUnit, Timestamp.Format inTimestampFormat) { // Output field according to type if (inField == Field.LATITUDE) @@ -595,14 +595,8 @@ public class FileSaver { if (inPoint.hasTimestamp()) { - if (inTimestampFormat == Timestamp.FORMAT_ORIGINAL) { - // output original string - inBuffer.append(inPoint.getTimestamp().getText(Timestamp.FORMAT_ORIGINAL)); - } - else { - // format value accordingly - inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat)); - } + // format value accordingly + inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat)); } } else diff --git a/tim/prune/save/GpxExporter.java b/tim/prune/save/GpxExporter.java index 1b2263b..0f038b7 100644 --- a/tim/prune/save/GpxExporter.java +++ b/tim/prune/save/GpxExporter.java @@ -535,7 +535,7 @@ public class GpxExporter extends GenericFunction implements Runnable source = replaceGpxTags(source, "lat=\"", "\"", inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); source = replaceGpxTags(source, "lon=\"", "\"", inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); source = replaceGpxTags(source, "", "", inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES)); - source = replaceGpxTags(source, "", inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601)); + source = replaceGpxTags(source, "", inPoint.getTimestamp().getText(Timestamp.Format.ISO8601)); if (inPoint.isWaypoint()) { source = replaceGpxTags(source, "", "", inPoint.getWaypointName()); @@ -685,7 +685,7 @@ public class GpxExporter extends GenericFunction implements Runnable if (inPoint.hasTimestamp() && inTimestamps) { inWriter.write("\t\t\n"); } // write waypoint name after elevation and time @@ -757,7 +757,7 @@ public class GpxExporter extends GenericFunction implements Runnable if (inPoint.hasTimestamp() && inTimestamps) { inWriter.write("\t\t\t\t\n"); } // photo, audio diff --git a/tim/prune/save/ImageExporter.java b/tim/prune/save/ImageExporter.java index 41bc0e6..f3383fe 100644 --- a/tim/prune/save/ImageExporter.java +++ b/tim/prune/save/ImageExporter.java @@ -36,6 +36,7 @@ import tim.prune.data.Track; import tim.prune.gui.BaseImageDefinitionPanel; import tim.prune.gui.GuiGridLayout; import tim.prune.gui.WholeNumberField; +import tim.prune.gui.colour.PointColourer; import tim.prune.gui.map.MapSource; import tim.prune.gui.map.MapSourceLibrary; import tim.prune.gui.map.MapUtils; @@ -289,9 +290,11 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer final int zoomFactor = 1 << _baseImagePanel.getImageDefinition().getZoom(); Graphics g = inImage.getImage().getGraphics(); // TODO: Set line width, style etc - g.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT)); + final PointColourer pointColourer = _app.getPointColourer(); + final Color defaultPointColour = Config.getColourScheme().getColour(ColourScheme.IDX_POINT); + g.setColor(defaultPointColour); - // Loop over points + // Loop to draw all track points final Track track = _app.getTrackInfo().getTrack(); final int numPoints = track.getNumPoints(); int prevX = 0, prevY = 0; @@ -300,6 +303,12 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer DataPoint point = track.getPoint(i); if (!point.isWaypoint()) { + // Determine what colour to use to draw the track point + if (pointColourer != null) + { + Color c = pointColourer.getColour(i); + g.setColor(c == null ? defaultPointColour : c); + } double x = track.getX(i) - xRange.getMinimum(); double y = track.getY(i) - yRange.getMinimum(); // use zoom level to calculate pixel coords on image @@ -318,10 +327,11 @@ public class ImageExporter extends GenericFunction implements BaseImageConsumer prevX = px; prevY = py; } } - // Draw waypoints + + // Now the waypoints final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT); g.setColor(textColour); - // Loop over points + // Loop again to draw waypoints for (int i=0; i"); if (point.hasTimestamp()) { - whenList.append(point.getTimestamp().getText(Timestamp.FORMAT_ISO_8601)); + whenList.append(point.getTimestamp().getText(Timestamp.Format.ISO8601)); } whenList.append("\n"); // Add coordinates to the list diff --git a/tim/prune/save/PovExporter.java b/tim/prune/save/PovExporter.java index 19e4485..c002747 100644 --- a/tim/prune/save/PovExporter.java +++ b/tim/prune/save/PovExporter.java @@ -321,8 +321,11 @@ public class PovExporter extends Export3dFunction { // file saved - store directory in config for later Config.setConfigString(Config.KEY_TRACK_DIR, povFile.getParentFile().getAbsolutePath()); - // also store exaggeration + // also store exaggeration and grid size Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_altFactor * 100)); + if (_terrainPanel.getUseTerrain() && _terrainPanel.getGridSize() > 20) { + Config.setConfigInt(Config.KEY_TERRAIN_GRID_SIZE, _terrainPanel.getGridSize()); + } } else { diff --git a/tim/prune/undo/UndoDeleteOperation.java b/tim/prune/undo/UndoDeleteOperation.java index d99a9f6..73d9c4f 100644 --- a/tim/prune/undo/UndoDeleteOperation.java +++ b/tim/prune/undo/UndoDeleteOperation.java @@ -8,13 +8,24 @@ import tim.prune.data.TrackInfo; */ public abstract class UndoDeleteOperation implements UndoOperation { + /** Flag to remember whether the deleted point was at the beginning or end of the selected range */ + private boolean _isAtBoundaryOfSelectedRange = false; + + /** + * @param inAtBoundary true if deleted point was at the beginning or end of the selected range + */ + public void setAtBoundaryOfSelectedRange(boolean inAtBoundary) + { + _isAtBoundaryOfSelectedRange = inAtBoundary; + } + /** * Modify the current point/range selection after the delete operation is undone * @param inTrackInfo track info object * @param inStartIndex start index of reinserted range * @param inEndIndex end index of reinserted range */ - protected static void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex) + protected void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex) { final int numPointsInserted = inEndIndex - inStartIndex + 1; // See if there is a currently selected point, if so does it need to be modified @@ -26,12 +37,19 @@ public abstract class UndoDeleteOperation implements UndoOperation // Same for currently selected range int rangeStart = inTrackInfo.getSelection().getStart(); int rangeEnd = inTrackInfo.getSelection().getEnd(); - if (rangeEnd >= inStartIndex && rangeEnd > rangeStart) + // Was the deleted point at the start or end of the selected range? + final boolean wasAtStart = numPointsInserted == 1 && inStartIndex == rangeStart && _isAtBoundaryOfSelectedRange; + final boolean wasAtEnd = numPointsInserted == 1 && inStartIndex == (rangeEnd+1) && _isAtBoundaryOfSelectedRange; + if (rangeEnd >= inStartIndex && rangeEnd > rangeStart || wasAtStart || wasAtEnd) { rangeEnd += numPointsInserted; if (rangeStart >= inStartIndex) { rangeStart += numPointsInserted; } + // Extend selection if the deleted point was at the start or end + if (wasAtStart) { + rangeStart--; + } inTrackInfo.getSelection().selectRange(rangeStart, rangeEnd); } } -- 2.43.0