Codebase list gpsprune / 39e0061
Imported Upstream version 17 Mònica Ramírez Arceda 9 years ago
112 changed file(s) with 4497 addition(s) and 1119 deletion(s). Raw diff Collapse all Expand all
00 # Build script using external exif library
11 # Version number
2 PRUNENAME=gpsprune_16.3
2 PRUNENAME=gpsprune_17
33 # remove compile directory
44 rm -rf compile
55 # remove dist directory
3434 import tim.prune.gui.SidebarController;
3535 import tim.prune.gui.UndoManager;
3636 import tim.prune.gui.Viewport;
37 import tim.prune.gui.colour.ColourerCaretaker;
38 import tim.prune.gui.colour.PointColourer;
3739 import tim.prune.load.FileLoader;
3840 import tim.prune.load.JpegLoader;
3941 import tim.prune.load.MediaLinkInfo;
6062 private JpegLoader _jpegLoader = null;
6163 private FileSaver _fileSaver = null;
6264 private UndoStack _undoStack = null;
65 private ColourerCaretaker _colCaretaker = null;
6366 private boolean _mangleTimestampsConfirmed = false;
6467 private Viewport _viewport = null;
6568 private ArrayList<File> _dataFiles = null;
8285 _track = new Track();
8386 _trackInfo = new TrackInfo(_track);
8487 FunctionLibrary.initialise(this);
88 _colCaretaker = new ColourerCaretaker(this);
89 UpdateMessageBroker.addSubscriber(_colCaretaker);
90 _colCaretaker.setColourer(Config.getPointColourer());
8591 }
8692
8793
119125 return _undoStack;
120126 }
121127
128 /**
129 * Update the system's point colourer using the one in the Config
130 */
131 public void updatePointColourer()
132 {
133 if (_colCaretaker != null) {
134 _colCaretaker.setColourer(Config.getPointColourer());
135 }
136 }
137
138 /**
139 * @return colourer object, or null
140 */
141 public PointColourer getPointColourer()
142 {
143 if (_colCaretaker == null) {return null;}
144 return _colCaretaker.getColourer();
145 }
122146
123147 /**
124148 * Show the specified tip if appropriate
318342 int audioIndex = _trackInfo.getAudioList().getAudioIndex(currentPoint.getAudio());
319343 DataPoint nextTrackPoint = _trackInfo.getTrack().getNextTrackPoint(pointIndex + 1);
320344 // Construct Undo object
321 UndoOperation undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex,
345 UndoDeletePoint undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex,
322346 audioIndex, nextTrackPoint != null && nextTrackPoint.getSegmentStart());
347 undo.setAtBoundaryOfSelectedRange(pointIndex == _trackInfo.getSelection().getStart() ||
348 pointIndex == _trackInfo.getSelection().getEnd());
323349 // call track to delete point
324350 if (_trackInfo.deletePoint())
325351 {
498524 */
499525 public void createPoint(DataPoint inPoint)
500526 {
527 createPoint(inPoint, true);
528 }
529
530 /**
531 * Create a new point at the end of the track
532 * @param inPoint point to add
533 * @param inNewSegment true for a single point, false for a continuation
534 */
535 public void createPoint(DataPoint inPoint, boolean inNewSegment)
536 {
501537 // create undo object
502538 UndoCreatePoint undo = new UndoCreatePoint();
503539 _undoStack.add(undo);
504540 // add point to track
505 inPoint.setSegmentStart(true);
541 inPoint.setSegmentStart(inNewSegment);
506542 _track.appendPoints(new DataPoint[] {inPoint});
507543 // ensure track's field list contains point's fields
508544 _track.extendFieldList(inPoint.getFieldList());
55 import tim.prune.function.charts.Charter;
66 import tim.prune.function.compress.CompressTrackFunction;
77 import tim.prune.function.compress.MarkPointsInRectangleFunction;
8 import tim.prune.function.deletebydate.DeleteByDateFunction;
89 import tim.prune.function.distance.DistanceFunction;
910 import tim.prune.function.edit.PointNameEditor;
1011 import tim.prune.function.estimate.EstimateTime;
4142 public static GenericFunction FUNCTION_IMPORTBABEL = null;
4243 public static GenericFunction FUNCTION_SAVECONFIG = null;
4344 public static GenericFunction FUNCTION_EDIT_WAYPOINT_NAME = null;
44 public static RearrangeWaypointsFunction FUNCTION_REARRANGE_WAYPOINTS = null;
45 public static GenericFunction FUNCTION_REARRANGE_WAYPOINTS = null;
46 public static GenericFunction FUNCTION_SELECT_SEGMENT = null;
4547 public static GenericFunction FUNCTION_SPLIT_SEGMENTS = null;
4648 public static GenericFunction FUNCTION_SEW_SEGMENTS = null;
4749 public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null;
4951 public static GenericFunction FUNCTION_DELETE_RANGE = null;
5052 public static GenericFunction FUNCTION_CROP_TRACK = null;
5153 public static GenericFunction FUNCTION_MARK_IN_RECTANGLE = null;
52 public static GenericFunction FUNCTION_INTERPOLATE = null;
54 public static GenericFunction FUNCTION_DELETE_BY_DATE = null;
55 public static SingleNumericParameterFunction FUNCTION_INTERPOLATE = null;
5356 public static GenericFunction FUNCTION_LOOKUP_SRTM = null;
5457 public static GenericFunction FUNCTION_DOWNLOAD_SRTM = null;
5558 public static GenericFunction FUNCTION_LOOKUP_WIKIPEDIA = null;
8992 public static GenericFunction FUNCTION_SET_DISK_CACHE = null;
9093 public static GenericFunction FUNCTION_SET_PATHS = null;
9194 public static GenericFunction FUNCTION_SET_COLOURS = null;
92 public static GenericFunction FUNCTION_SET_LINE_WIDTH = null;
95 public static SingleNumericParameterFunction FUNCTION_SET_LINE_WIDTH = null;
9396 public static GenericFunction FUNCTION_SET_LANGUAGE = null;
97 public static SingleNumericParameterFunction FUNCTION_SET_ALTITUDE_TOLERANCE = null;
9498 public static GenericFunction FUNCTION_HELP = null;
9599 public static GenericFunction FUNCTION_SHOW_KEYS = null;
96100 public static GenericFunction FUNCTION_ABOUT = null;
114118 FUNCTION_SAVECONFIG = new SaveConfig(inApp);
115119 FUNCTION_EDIT_WAYPOINT_NAME = new PointNameEditor(inApp);
116120 FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp);
121 FUNCTION_SELECT_SEGMENT = new SelectSegmentFunction(inApp);
117122 FUNCTION_SPLIT_SEGMENTS = new SplitSegmentsFunction(inApp);
118123 FUNCTION_SEW_SEGMENTS = new SewTrackSegmentsFunction(inApp);
119124 FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp);
121126 FUNCTION_DELETE_RANGE = new DeleteSelectedRangeFunction(inApp);
122127 FUNCTION_CROP_TRACK = new CropToSelection(inApp);
123128 FUNCTION_MARK_IN_RECTANGLE = new MarkPointsInRectangleFunction(inApp);
129 FUNCTION_DELETE_BY_DATE = new DeleteByDateFunction(inApp);
124130 FUNCTION_INTERPOLATE = new InterpolateFunction(inApp);
125131 FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp);
126132 FUNCTION_DOWNLOAD_SRTM = new DownloadSrtmFunction(inApp);
163169 FUNCTION_SET_COLOURS = new SetColours(inApp);
164170 FUNCTION_SET_LINE_WIDTH = new SetLineWidth(inApp);
165171 FUNCTION_SET_LANGUAGE = new SetLanguage(inApp);
172 FUNCTION_SET_ALTITUDE_TOLERANCE = new SetAltitudeTolerance(inApp);
166173 FUNCTION_HELP = new HelpScreen(inApp);
167174 FUNCTION_SHOW_KEYS = new ShowKeysScreen(inApp);
168175 FUNCTION_ABOUT = new AboutScreen(inApp);
22 import java.awt.event.WindowAdapter;
33 import java.awt.BorderLayout;
44 import java.awt.Component;
5 import java.awt.Image;
56 import java.awt.event.WindowEvent;
67 import java.io.File;
78 import java.io.FileNotFoundException;
3435 public class GpsPrune
3536 {
3637 /** Version number of application, used in about screen and for version check */
37 public static final String VERSION_NUMBER = "16.3";
38 public static final String VERSION_NUMBER = "17";
3839 /** Build number, just used for about screen */
39 public static final String BUILD_NUMBER = "303c";
40 public static final String BUILD_NUMBER = "320";
4041 /** Static reference to App object */
4142 private static App APP = null;
4243
227228 // Avoid automatically shutting down if window closed
228229 frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
229230
230 // set icon
231 try {
232 frame.setIconImage(IconManager.getImageIcon(IconManager.WINDOW_ICON).getImage());
233 }
234 catch (Exception e) {} // ignore
231 // set window icons of different resolutions (1.6+)
232 try
233 {
234 ArrayList<Image> icons = new ArrayList<Image>();
235 String[] resolutions = {"_16", "_20", "_32", "_64", "_128"};
236 for (String r : resolutions) {
237 icons.add(IconManager.getImageIcon(IconManager.WINDOW_ICON + r + ".png").getImage());
238 }
239 Class<?> d = java.awt.Window.class;
240 // This is the same as frame.setIconImages(icons) but is compilable also for java1.5 where this isn't available
241 d.getDeclaredMethod("setIconImages", new Class[]{java.util.List.class}).invoke(frame, icons);
242 }
243 catch (Exception e)
244 {
245 // setting a list of icon images didn't work, so try with just one image instead
246 try {
247 frame.setIconImage(IconManager.getImageIcon(IconManager.WINDOW_ICON + "_16.png").getImage());
248 }
249 catch (Exception e2) {}
250 }
235251
236252 // Set up drag-and-drop handler to accept dropped files
237253 frame.setTransferHandler(new FileDropHandler(APP));
55 */
66 public abstract class UpdateMessageBroker
77 {
8 private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 6;
8 private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 7;
99 /** Array of all subscribers */
1010 private static DataSubscriber[] _subscribers = new DataSubscriber[MAXIMUM_NUMBER_SUBSCRIBERS];
1111 /** Counter of the number of subscribers added so far */
66 import tim.prune.data.RecentFileList;
77 import tim.prune.data.UnitSet;
88 import tim.prune.data.UnitSetLibrary;
9 import tim.prune.gui.colour.ColourerFactory;
10 import tim.prune.gui.colour.PointColourer;
911 import tim.prune.gui.map.MapSourceLibrary;
1012
1113
2123 private static Properties _configValues = null;
2224 /** Colour scheme object is also part of config */
2325 private static ColourScheme _colourScheme = new ColourScheme();
26 /** Point colourer object, if any */
27 private static PointColourer _pointColourer = null;
2428 /** Recently-used file list */
2529 private static RecentFileList _recentFiles = new RecentFileList();
2630 /** Current unit set */
7276 public static final String KEY_EXIFTOOL_PATH = "prune.exiftoolpath";
7377 /** Key for colour scheme */
7478 public static final String KEY_COLOUR_SCHEME = "prune.colourscheme";
79 /** Key for point colourer */
80 public static final String KEY_POINT_COLOURER = "prune.pointcolourer";
7581 /** Key for line width used for drawing */
7682 public static final String KEY_LINE_WIDTH = "prune.linewidth";
7783 /** Key for kml track colour */
8490 public static final String KEY_ESTIMATION_PARAMS = "prune.estimationparams";
8591 /** Key for 3D exaggeration factor */
8692 public static final String KEY_HEIGHT_EXAGGERATION = "prune.heightexaggeration";
93 /** Key for terrain grid size */
94 public static final String KEY_TERRAIN_GRID_SIZE = "prune.terraingridsize";
95 /** Key for altitude tolerance */
96 public static final String KEY_ALTITUDE_TOLERANCE = "prune.altitudetolerance";
8797
8898
8999 /** Initialise the default properties */
144154 // Save all properties from file
145155 _configValues.putAll(props);
146156 _colourScheme.loadFromHex(_configValues.getProperty(KEY_COLOUR_SCHEME));
157 _pointColourer = ColourerFactory.createColourer(_configValues.getProperty(KEY_POINT_COLOURER));
147158 _recentFiles = new RecentFileList(_configValues.getProperty(KEY_RECENT_FILES));
148159 _unitSet = UnitSetLibrary.getUnitSet(_configValues.getProperty(KEY_UNITSET_KEY));
149160 // Adjust map source index if necessary
175186 props.put(KEY_AUTOSAVE_SETTINGS, "0"); // autosave false by default
176187 props.put(KEY_UNITSET_KEY, "unitset.kilometres"); // metric by default
177188 props.put(KEY_HEIGHT_EXAGGERATION, "100"); // 100%, no exaggeration
189 props.put(KEY_TERRAIN_GRID_SIZE, "50");
190 props.put(KEY_ALTITUDE_TOLERANCE, "0"); // 0, all exact as before
178191 return props;
179192 }
180193
237250 }
238251
239252 /**
253 * @return the current point colourer, if any
254 */
255 public static PointColourer getPointColourer()
256 {
257 return _pointColourer;
258 }
259
260 /**
240261 * @return list of recently used files
241262 */
242263 public static RecentFileList getRecentFileList()
337358 }
338359
339360 /**
361 * Update the point colourer from the given colourer
362 * @param inColourer point colourer object, or null
363 */
364 public static void updatePointColourer(PointColourer inColourer)
365 {
366 _pointColourer = inColourer;
367 setConfigString(KEY_POINT_COLOURER, ColourerFactory.PointColourerToString(_pointColourer));
368 }
369
370 /**
340371 * @return the current unit set
341372 */
342373 public static UnitSet getUnitSet() {
343374 return _unitSet;
344375 }
345376
377 /**
378 * @param inIndex index of unit set to select
379 */
346380 public static void selectUnitSet(int inIndex)
347381 {
348382 _unitSet = UnitSetLibrary.getUnitSet(inIndex);
00 package tim.prune.data;
1
2 import tim.prune.config.Config;
13
24 /**
35 * Represents a range of altitudes, taking units into account.
79 {
810 /** Range of altitudes in metres */
911 private IntegerRange _range = new IntegerRange();
10 /** Empty flag */
11 private boolean _empty;
12 /** Flag for whether previous value exists or not */
13 private boolean _gotPreviousValue;
1214 /** Previous metric value */
13 private int _prevValue;
15 private int _previousValue;
1416 /** Total climb in metres */
15 private double _climb;
17 private int _climb;
1618 /** Total descent in metres */
17 private double _descent;
19 private int _descent;
20 /** Flags for whether minimum or maximum has been found */
21 private boolean _gotPreviousMinimum = false, _gotPreviousMaximum = false;
22 /** Integer values of previous minimum and maximum, if any */
23 private int _previousExtreme = 0;
1824
1925
2026 /**
3036 public void clear()
3137 {
3238 _range.clear();
33 _climb = 0.0;
34 _descent = 0.0;
35 _empty = true;
36 _prevValue = 0;
39 _climb = _descent = 0;
40 _gotPreviousValue = false;
41 _previousValue = 0;
42 _gotPreviousMinimum = _gotPreviousMaximum = false;
43 _previousExtreme = 0;
3744 }
3845
3946
4350 */
4451 public void addValue(Altitude inAltitude)
4552 {
53 final int wiggleLimit = Config.getConfigInt(Config.KEY_ALTITUDE_TOLERANCE) / 100;
54
4655 if (inAltitude != null && inAltitude.isValid())
4756 {
4857 int altValue = (int) inAltitude.getMetricValue();
4958 _range.addValue(altValue);
5059 // Compare with previous value if any
51 if (!_empty)
60 if (_gotPreviousValue)
5261 {
53 if (altValue > _prevValue)
54 _climb += (altValue - _prevValue);
55 else
56 _descent += (_prevValue - altValue);
62 if (altValue != _previousValue)
63 {
64 // Got an altitude value which is different from the previous one
65 final boolean locallyUp = (altValue > _previousValue);
66 final boolean overallUp = _gotPreviousMinimum && _previousValue > _previousExtreme;
67 final boolean overallDn = _gotPreviousMaximum && _previousValue < _previousExtreme;
68 final boolean moreThanWiggle = Math.abs(altValue - _previousValue) > wiggleLimit;
69 // Do we know whether we're going up or down yet?
70 if (!_gotPreviousMinimum && !_gotPreviousMaximum)
71 {
72 // we don't know whether we're going up or down yet - check limit
73 if (moreThanWiggle)
74 {
75 if (locallyUp) {_gotPreviousMinimum = true;}
76 else {_gotPreviousMaximum = true;}
77 _previousExtreme = _previousValue;
78 _previousValue = altValue;
79 _gotPreviousValue = true;
80 }
81 }
82 else if (overallUp)
83 {
84 if (locallyUp) {
85 // we're still going up - do nothing
86 _previousValue = altValue;
87 }
88 else if (moreThanWiggle)
89 {
90 // we're going up but have dropped over a maximum
91 // Add the climb from _previousExtreme up to _previousValue
92 _climb += (_previousValue - _previousExtreme);
93 _previousExtreme = _previousValue;
94 _gotPreviousMinimum = false; _gotPreviousMaximum = true;
95 _previousValue = altValue;
96 _gotPreviousValue = true;
97 }
98 }
99 else if (overallDn)
100 {
101 if (locallyUp) {
102 if (moreThanWiggle)
103 {
104 // we're going down but have climbed up from a minimum
105 // Add the descent from _previousExtreme down to _previousValue
106 _descent += (_previousExtreme - _previousValue);
107 _previousExtreme = _previousValue;
108 _gotPreviousMinimum = true; _gotPreviousMaximum = false;
109 _previousValue = altValue;
110 _gotPreviousValue = true;
111 }
112 }
113 else {
114 // we're still going down - do nothing
115 _previousValue = altValue;
116 _gotPreviousValue = true;
117 }
118 }
119 // TODO: Behaviour when WIGGLE_LIMIT == 0 should be same as before, all differences cumulated
120 }
57121 }
58 _prevValue = altValue;
59 _empty = false;
122 else
123 {
124 // we haven't got a previous value at all, so it's the start of a new segment
125 _previousValue = altValue;
126 _gotPreviousValue = true;
127 }
128
129 // if (!_empty)
130 // {
131 // if (altValue > _previousValue)
132 // _climb += (altValue - _previousValue);
133 // else
134 // _descent += (_previousValue - altValue);
135 // }
136 // _previousValue = altValue;
137 // _empty = false;
60138 }
61139 }
62140
66144 */
67145 public void ignoreValue(Altitude inAltitude)
68146 {
69 // If we set the empty flag to true, that has the same effect as restarting a segment
70 _empty = true;
71 addValue(inAltitude);
147 // Process the previous value, if any, to update climb/descent as that's the end of the previous segment
148 if (_gotPreviousValue && _gotPreviousMinimum && _previousValue > _previousExtreme) {
149 _climb += (_previousValue - _previousExtreme);
150 }
151 else if (_gotPreviousValue && _gotPreviousMaximum && _previousValue < _previousExtreme) {
152 _descent += (_previousExtreme - _previousValue);
153 }
154 // Eliminate the counting values to start the new segment
155 _gotPreviousMinimum = _gotPreviousMaximum = false;
156 _gotPreviousValue = false;
157 // Now process this value if there is one
158 if (inAltitude != null && inAltitude.isValid())
159 {
160 final int altValue = (int) inAltitude.getMetricValue();
161 _range.addValue(altValue);
162 _previousValue = altValue;
163 _gotPreviousValue = true;
164 }
72165 }
73166
74167 /**
106199 */
107200 public int getClimb(Unit inUnit)
108201 {
109 return (int) (_climb * inUnit.getMultFactorFromStd());
202 // May need to add climb from last segment
203 int lastSegmentClimb = 0;
204 if (_gotPreviousValue && _gotPreviousMinimum && _previousValue > _previousExtreme) {
205 lastSegmentClimb = _previousValue - _previousExtreme;
206 }
207 return (int) ((_climb + lastSegmentClimb) * inUnit.getMultFactorFromStd());
110208 }
111209
112210 /**
115213 */
116214 public int getDescent(Unit inUnit)
117215 {
118 return (int) (_descent * inUnit.getMultFactorFromStd());
216 // May need to add descent from last segment
217 int lastSegmentDescent = 0;
218 if (_gotPreviousValue && _gotPreviousMaximum && _previousValue < _previousExtreme) {
219 lastSegmentDescent = _previousExtreme - _previousValue;
220 }
221 return (int) ((_descent + lastSegmentDescent) * inUnit.getMultFactorFromStd());
119222 }
120223
121224 /**
123226 */
124227 public double getMetricHeightDiff()
125228 {
126 return _climb - _descent;
229 return getClimb(UnitSetLibrary.UNITS_METRES) - getDescent(UnitSetLibrary.UNITS_METRES);
127230 }
128231 }
4343 {
4444 int i = inIndex + 1;
4545 DataPoint point = null;
46 while ((point=inTrack.getPoint(i)) != null && !point.getSegmentStart()) {
46 while ((point=inTrack.getPoint(i)) != null && (point.isWaypoint() || !point.getSegmentStart())) {
4747 i++;
4848 }
4949 return Math.min(i, inTrack.getNumPoints()-1);
5959 {
6060 int i = inIndex - 1;
6161 DataPoint point = null;
62 while ((point=inTrack.getPoint(i)) != null && !point.getSegmentStart()) {
62 while ((point=inTrack.getPoint(i)) != null && (point.isWaypoint() || !point.getSegmentStart())) {
6363 i--;
6464 }
65 return Math.max(i, 0);
65 // Have we gone past the beginning of the track?
66 i = Math.max(i, 0);
67 // count forwards past the waypoints if necessary
68 while ((point=inTrack.getPoint(i)) != null && point.isWaypoint()) {
69 i++;
70 }
71 return i;
72 }
73
74 /**
75 * Find the index of the last track point in the current segment
76 * @param inTrack track object
77 * @param inIndex current index
78 * @return index of next segment end
79 */
80 public static int getNextSegmentEnd(Track inTrack, int inIndex)
81 {
82 // First, go to start of following segment, or the end of the track
83 int i = getNextSegmentStart(inTrack, inIndex);
84 // If it's the next segment, subtract one
85 DataPoint point = inTrack.getPoint(i);
86 if (point == null || point.getSegmentStart())
87 {
88 i--;
89 }
90 // Now we may be on a waypoint, so count back to get the last track point
91 while ((point=inTrack.getPoint(i)) != null && point.isWaypoint()) {
92 i--;
93 }
94 return Math.min(i, inTrack.getNumPoints()-1);
95 }
96
97
98 /**
99 * @param inTrack track object
100 * @return true if there is at least one waypoint with a timestamp
101 */
102 public static boolean haveWaypointsGotTimestamps(Track inTrack)
103 {
104 if (inTrack != null)
105 {
106 for (int i=0; i<inTrack.getNumPoints(); i++)
107 {
108 DataPoint p = inTrack.getPoint(i);
109 if (p != null && p.isWaypoint() && p.hasTimestamp())
110 {
111 return true;
112 }
113 }
114 }
115 return false;
66116 }
67117 }
125125 // parse fields according to number found
126126 _degrees = (int) fields[0];
127127 _asDouble = _degrees;
128 _originalFormat = hasCardinal?FORMAT_DEG:FORMAT_DEG_WITHOUT_CARDINAL;
128 _originalFormat = hasCardinal ? FORMAT_DEG : FORMAT_DEG_WITHOUT_CARDINAL;
129129 _fracDenom = 10;
130130 if (numFields == 2)
131131 {
148148 _fracs = 0;
149149 _asDouble = 1.0 * _degrees + (_minutes / 60.0);
150150 }
151 }
152 // Check for exponential degrees like 1.3E-6
153 else if (numFields == 3 && !otherDelims[1] && otherDelims[2] && isJustNumber(inString))
154 {
155 _originalFormat = FORMAT_DEG;
156 _asDouble = Math.abs(Double.parseDouble(inString)); // must succeed if isJustNumber has given true
157 // now we can ignore the fields and just use this double
158 _degrees = (int) _asDouble;
159 double numMins = (_asDouble - _degrees) * 60.0;
160 _minutes = (int) numMins;
161 double numSecs = (numMins - _minutes) * 60.0;
162 _seconds = (int) numSecs;
163 _fracs = (int) ((numSecs - _seconds) * 10);
151164 }
152165 // Differentiate between d-m.f and d-m-s using . or ,
153166 else if (numFields == 3 && !otherDelims[2])
183196
184197 /**
185198 * Get the cardinal from the given character
186 * @param inFirstChar first character from file
187 * @param inLastChar last character from file
188 */
189 protected int getCardinal(char inFirstChar, char inLastChar)
199 * @param inFirstChar first character from string
200 * @param inLastChar last character from string
201 */
202 private int getCardinal(char inFirstChar, char inLastChar)
190203 {
191204 // Try leading character first
192205 int cardinal = getCardinal(inFirstChar);
8181 }
8282
8383 /**
84 * Get the SourceInfo object (if any) for the given point
85 * @param inPoint point object
86 * @return SourceInfo object if there is one, otherwise null
87 */
88 public SourceInfo getSourceForPoint(DataPoint inPoint)
89 {
90 for (SourceInfo source : _sources) {
91 if (source.getIndex(inPoint) >= 0) {
92 return source;
93 }
94 }
95 return null;
96 }
97
98 /**
8499 * Clone contents of file info
85100 */
86101 @SuppressWarnings("unchecked")
0 package tim.prune.data;
1
2 /**
3 * Abstract class to hold static calculation functions
4 * for gradient (like glide slope)
5 */
6 public abstract class GradientCalculator
7 {
8 /**
9 * Calculate the gradient value of the track at the specified index
10 * @param inTrack track object
11 * @param inIndex index of point to calculate gradient for
12 * @param inValue object in which to place result of calculation
13 */
14 public static void calculateGradient(Track inTrack, int inIndex, SpeedValue inValue)
15 {
16 inValue.setInvalid();
17 if (inTrack == null || inIndex < 0 || inValue == null)
18 {
19 System.err.println("Cannot calculate gradient for index " + inIndex);
20 return;
21 }
22
23 // If no altitude or it's a waypoint then no gradient either
24 DataPoint point = inTrack.getPoint(inIndex);
25 if (point == null || !point.hasAltitude() || point.isWaypoint()) {
26 return;
27 }
28
29 // If the point has horizontal and vertical speeds already then just use those
30 if (point.hasHSpeed() && point.hasVSpeed()) {
31 inValue.setValue(point.getVSpeed().getValueInMetresPerSec() / point.getHSpeed().getValueInMetresPerSec());
32 }
33 else if (!point.getSegmentStart())
34 {
35 // Use the previous track point and the next track point
36 DataPoint p = inTrack.getPreviousTrackPoint(inIndex-1);
37 DataPoint q = inTrack.getNextTrackPoint(inIndex+1);
38 if (p != null && q != null && !q.getSegmentStart()
39 && p.hasAltitude() && q.hasAltitude())
40 {
41 final double horizRads = DataPoint.calculateRadiansBetween(p, point) +
42 DataPoint.calculateRadiansBetween(point, q);
43 final double horizDist = Distance.convertRadiansToDistance(horizRads, UnitSetLibrary.UNITS_METRES);
44 final double heightDiff = q.getAltitude().getMetricValue() - p.getAltitude().getMetricValue();
45 // Get gradient in radians
46 final double gradient = Math.atan2(heightDiff, horizDist);
47 inValue.setValue(gradient);
48 }
49 }
50 // otherwise, just leave value as invalid
51 }
52 }
121121 public int getIndex(DataPoint inPoint)
122122 {
123123 int idx = -1;
124 for (int i=0; i<_points.length && (idx < 0); i++) {
125 if (_points[i] == inPoint) {idx = i;}
124 for (int i=0; i<_points.length; i++)
125 {
126 if (_points[i] == inPoint) {
127 idx = i;
128 break;
129 }
126130 }
127131 if (idx == -1) {return idx;} // point not found
128132 if (_pointIndices == null) {return idx;} // All points loaded
1515 */
1616 public static void calculateSpeed(Track inTrack, int inIndex, SpeedValue inValue)
1717 {
18 if (inTrack == null || inIndex < 0 || inValue == null) {
18 inValue.setInvalid();
19 if (inTrack == null || inIndex < 0 || inValue == null)
20 {
1921 System.err.println("Cannot calculate speed for index " + inIndex);
2022 return;
2123 }
22 inValue.setInvalid();
2324
2425 DataPoint point = inTrack.getPoint(inIndex);
2526 if (point == null) {return;}
1717 private boolean _valid = false;
1818 private long _milliseconds = 0L;
1919 private String _text = null;
20 private String _timeText = null;
21
22 private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateTimeInstance();
20
21 private static final DateFormat DEFAULT_DATETIME_FORMAT = DateFormat.getDateTimeInstance();
22 private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance();
2323 private static final DateFormat DEFAULT_TIME_FORMAT = DateFormat.getTimeInstance();
24 private static boolean MillisAddedToTimeFormat = false;
2425 private static final DateFormat ISO_8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
26 private static final DateFormat ISO_8601_FORMAT_WITH_MILLIS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
2527 private static final DateFormat ISO_8601_FORMAT_NOZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
2628 private static DateFormat[] ALL_DATE_FORMATS = null;
2729 private static Calendar CALENDAR = null;
3739 private static long TWENTY_YEARS_IN_SECS = 0L;
3840 private static final long GARTRIP_OFFSET = 631065600L;
3941
40 /** Specifies original timestamp format */
41 public static final int FORMAT_ORIGINAL = 0;
42 /** Specifies locale-dependent timestamp format */
43 public static final int FORMAT_LOCALE = 1;
44 /** Specifies ISO 8601 timestamp format */
45 public static final int FORMAT_ISO_8601 = 2;
42 /** Possible formats for parsing and displaying timestamps */
43 public enum Format
44 {
45 ORIGINAL,
46 LOCALE,
47 ISO8601
48 }
4649
4750 /** Identifier for the parsing strategy to use */
4851 private enum ParseType
5760 FIXED_FORMAT4,
5861 FIXED_FORMAT5,
5962 FIXED_FORMAT6,
63 FIXED_FORMAT7,
6064 GENERAL_STRING
6165 }
6266
6367 /** Array of parse types to loop through (first one is changed to last successful type) */
6468 private static ParseType[] ALL_PARSE_TYPES = {ParseType.NONE, ParseType.ISO8601_FRACTIONAL, ParseType.LONG,
6569 ParseType.FIXED_FORMAT0, ParseType.FIXED_FORMAT1, ParseType.FIXED_FORMAT2, ParseType.FIXED_FORMAT3,
66 ParseType.FIXED_FORMAT4, ParseType.FIXED_FORMAT5, ParseType.FIXED_FORMAT6, ParseType.GENERAL_STRING};
70 ParseType.FIXED_FORMAT4, ParseType.FIXED_FORMAT5, ParseType.FIXED_FORMAT6, ParseType.FIXED_FORMAT7,
71 ParseType.GENERAL_STRING};
6772
6873 // Static block to initialise offsets
6974 static
7984 TWENTY_YEARS_IN_SECS = (MSECS_SINCE_1970 - MSECS_SINCE_1990) / 1000L;
8085 // Set timezone for output
8186 ISO_8601_FORMAT.setTimeZone(gmtZone);
82 DEFAULT_DATE_FORMAT.setTimeZone(gmtZone);
87 ISO_8601_FORMAT_WITH_MILLIS.setTimeZone(gmtZone);
88 DEFAULT_DATETIME_FORMAT.setTimeZone(gmtZone);
8389 // Date formats
8490 ALL_DATE_FORMATS = new DateFormat[] {
85 DEFAULT_DATE_FORMAT,
91 DEFAULT_DATETIME_FORMAT,
8692 new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy"),
8793 new SimpleDateFormat("HH:mm:ss dd MMM yyyy"),
8894 new SimpleDateFormat("dd MMM yyyy HH:mm:ss"),
95 new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss"),
8996 new SimpleDateFormat("yyyy MMM dd HH:mm:ss"),
9097 ISO_8601_FORMAT, ISO_8601_FORMAT_NOZ
9198 };
99 for (DateFormat df : ALL_DATE_FORMATS) {
100 df.setLenient(false);
101 }
92102 }
93103
94104
99109 public Timestamp(String inString)
100110 {
101111 _valid = false;
112 _text = null;
102113 if (inString != null && !inString.equals(""))
103114 {
104115 // Try each of the parse types in turn
108119 {
109120 ALL_PARSE_TYPES[0] = type;
110121 _valid = true;
122 _text = inString;
111123 return;
112124 }
113125 }
166178 case FIXED_FORMAT4: return parseString(inString, ALL_DATE_FORMATS[4]);
167179 case FIXED_FORMAT5: return parseString(inString, ALL_DATE_FORMATS[5]);
168180 case FIXED_FORMAT6: return parseString(inString, ALL_DATE_FORMATS[6]);
181 case FIXED_FORMAT7: return parseString(inString, ALL_DATE_FORMATS[7]);
169182
170183 case GENERAL_STRING:
171184 if (inString.length() == 19)
200213 */
201214 private boolean parseString(String inString, DateFormat inDateFormat)
202215 {
203 inDateFormat.setLenient(false);
204216 ParsePosition pPos = new ParsePosition(0);
205217 Date date = inDateFormat.parse(inString, pPos);
206218 if (date != null && inString.length() == pPos.getIndex()) // require use of _all_ the string, not just the beginning
338350 }
339351
340352 /**
353 * @return true if the timestamp has non-zero milliseconds
354 */
355 public boolean hasMilliseconds()
356 {
357 return isValid() && (_milliseconds % 1000L) > 0;
358 }
359 /**
341360 * @param inOther other Timestamp
342361 * @return true if this one is at least a second after the other
343362 */
431450 */
432451 public String getText()
433452 {
434 return getText(FORMAT_LOCALE);
453 return getText(Format.LOCALE);
435454 }
436455
437456 /**
438457 * @param inFormat format of timestamp
439458 * @return Description of timestamp in required format
440459 */
441 public String getText(int inFormat)
460 public String getText(Format inFormat)
442461 {
443462 if (!_valid) {return "";}
444 if (inFormat == FORMAT_ISO_8601) {
445 return format(ISO_8601_FORMAT);
446 }
447 if (_text == null) {
448 _text = format(DEFAULT_DATE_FORMAT);
463 switch (inFormat)
464 {
465 case ORIGINAL:
466 if (_text != null) {return _text;}
467 // otherwise fallthrough to default
468 //$FALL-THROUGH$
469 case LOCALE:
470 return format(DEFAULT_DATETIME_FORMAT);
471 case ISO8601:
472 return format(hasMilliseconds() ? ISO_8601_FORMAT_WITH_MILLIS : ISO_8601_FORMAT);
449473 }
450474 return _text;
451475 }
452476
453477 /**
478 * @return date part of timestamp in locale-specific format
479 */
480 public String getDateText()
481 {
482 if (!_valid) return "";
483 return format(DEFAULT_DATE_FORMAT);
484 }
485
486 /**
454487 * @return Description of time part of timestamp in locale-specific format
455488 */
456489 public String getTimeText()
457490 {
458 if (_timeText == null)
459 {
460 if (_valid) {
461 _timeText = format(DEFAULT_TIME_FORMAT);
491 if (!_valid) return "";
492 // Maybe we should add milliseconds to this format?
493 if (hasMilliseconds() && !MillisAddedToTimeFormat)
494 {
495 try
496 {
497 SimpleDateFormat sdf = (SimpleDateFormat) DEFAULT_TIME_FORMAT;
498 String pattern = sdf.toPattern();
499 if (pattern.indexOf("ss") > 0 && pattern.indexOf("SS") < 0)
500 {
501 sdf.applyPattern(pattern.replaceFirst("s+", "$0.SSS"));
502 MillisAddedToTimeFormat = true;
503 }
462504 }
463 else _timeText = "";
464 }
465 return _timeText;
505 catch (ClassCastException cce) {}
506 }
507 return format(DEFAULT_TIME_FORMAT);
466508 }
467509
468510 /**
8686 _dataPoints[pointIndex] = point;
8787 pointIndex++;
8888 }
89 else
90 {
91 // TODO: Maybe report this somehow?
92 // System.out.println("point is not valid!");
93 }
8994 }
9095 _numPoints = pointIndex;
9196 // Set first track point to be start of segment
359364 // needs to be scaled again
360365 _scaled = false;
361366 return foundAlt;
362 }
363
364
365 /**
366 * Collect all waypoints to the start or end of the track
367 * @param inAtStart true to collect at start, false for end
368 * @return true if successful, false if no change
369 */
370 public boolean collectWaypoints(boolean inAtStart)
371 {
372 // Check for mixed data, numbers of waypoints & nons
373 int numWaypoints = 0, numNonWaypoints = 0;
374 boolean wayAfterNon = false, nonAfterWay = false;
375 DataPoint[] waypoints = new DataPoint[_numPoints];
376 DataPoint[] nonWaypoints = new DataPoint[_numPoints];
377 DataPoint point = null;
378 for (int i=0; i<_numPoints; i++)
379 {
380 point = _dataPoints[i];
381 if (point.isWaypoint())
382 {
383 waypoints[numWaypoints] = point;
384 numWaypoints++;
385 wayAfterNon |= (numNonWaypoints > 0);
386 }
387 else
388 {
389 nonWaypoints[numNonWaypoints] = point;
390 numNonWaypoints++;
391 nonAfterWay |= (numWaypoints > 0);
392 }
393 }
394 // Exit if the data is already in the specified order
395 if (numWaypoints == 0 || numNonWaypoints == 0
396 || (inAtStart && !wayAfterNon && nonAfterWay)
397 || (!inAtStart && wayAfterNon && !nonAfterWay))
398 {
399 return false;
400 }
401
402 // Copy the arrays back into _dataPoints in the specified order
403 if (inAtStart)
404 {
405 System.arraycopy(waypoints, 0, _dataPoints, 0, numWaypoints);
406 System.arraycopy(nonWaypoints, 0, _dataPoints, numWaypoints, numNonWaypoints);
407 }
408 else
409 {
410 System.arraycopy(nonWaypoints, 0, _dataPoints, 0, numNonWaypoints);
411 System.arraycopy(waypoints, 0, _dataPoints, numNonWaypoints, numWaypoints);
412 }
413 // needs to be scaled again
414 _scaled = false;
415 UpdateMessageBroker.informSubscribers();
416 return true;
417367 }
418368
419369
0 package tim.prune.data.sort;
1
2 import java.util.Comparator;
3
4 import tim.prune.data.DataPoint;
5
6
7 /**
8 * Class for comparing photos to sort them by name or timestamp
9 */
10 public class PhotoComparer implements Comparator<DataPoint>
11 {
12 /** Sort mode */
13 private SortMode _sortMode;
14
15
16 /**
17 * Constructor
18 * @param inMode sort mode
19 */
20 public PhotoComparer(SortMode inMode)
21 {
22 _sortMode = inMode;
23 }
24
25 /**
26 * Main compare method
27 */
28 public int compare(DataPoint inP1, DataPoint inP2)
29 {
30 if (inP2 == null || inP2.getPhoto() == null) return -1; // all nulls at end
31 if (inP1 == null || inP1.getPhoto() == null) return 1;
32 // Sort by name
33 int result = 0;
34 if (_sortMode == SortMode.SORTBY_NAME) {
35 result = compareNames(inP1, inP2);
36 }
37 if (result == 0) {
38 result = compareTimes(inP1, inP2);
39 }
40 // check names if times didn't work
41 if (result == 0 && _sortMode == SortMode.SORTBY_TIME) {
42 result = compareNames(inP1, inP2);
43 }
44 // names and times equal, try width and height
45 if (result == 0) {
46 result = compareSizes(inP1, inP2);
47 }
48 return result;
49 }
50
51 /**
52 * Compare the names of the two photo points
53 * @param inP1 first point
54 * @param inP2 second point
55 * @return compare value (-1,0,1)
56 */
57 private int compareNames(DataPoint inP1, DataPoint inP2)
58 {
59 // If the files can't be compared, use the photo names
60 if (inP1.getPhoto().getFile() == null || inP2.getPhoto().getFile() == null) {
61 return inP1.getPhoto().getName().compareTo(inP2.getPhoto().getName());
62 }
63 // both photos have files, so just compare the files
64 return inP1.getPhoto().getFile().compareTo(inP2.getPhoto().getFile());
65 }
66
67 /**
68 * Compare the timestamps of the two photo points
69 * @param inP1 first point
70 * @param inP2 second point
71 * @return compare value (-1,0,1)
72 */
73 private int compareTimes(DataPoint inP1, DataPoint inP2)
74 {
75 // Photos might not have timestamps
76 if (!inP2.hasTimestamp()) return -1;
77 if (!inP1.hasTimestamp()) return 1;
78 // Compare the timestamps
79 long secDiff = inP1.getPhoto().getTimestamp().getMillisecondsSince(inP2.getPhoto().getTimestamp());
80 return (secDiff<0?-1:(secDiff==0?0:1));
81 }
82
83 /**
84 * Compare the sizes of the two photos
85 * @param inP1 first point
86 * @param inP2 second point
87 * @return compare value (-1,0,1)
88 */
89 private int compareSizes(DataPoint inP1, DataPoint inP2)
90 {
91 // Try the widths
92 int w1 = inP1.getPhoto().getWidth();
93 int w2 = inP2.getPhoto().getWidth();
94 if (w2 <= 0) return -1;
95 if (w1 <= 0) return 1;
96 if (w1 != w2) return (w2 > w1 ? 1 : -1);
97 // Try the heights
98 int h1 = inP1.getPhoto().getHeight();
99 int h2 = inP2.getPhoto().getHeight();
100 if (h2 <= 0) return -1;
101 if (h1 <= 0) return 1;
102 if (h1 != h2) return (h2 > h1 ? 1 : -1);
103 // sizes same
104 return 0;
105 }
106 }
0 package tim.prune.data.sort;
1
2 /**
3 * Enumeration for possible sort modes
4 */
5 public enum SortMode
6 {
7 DONT_SORT,
8 SORTBY_NAME,
9 SORTBY_TIME
10 }
0 package tim.prune.data.sort;
1
2 import java.util.Comparator;
3
4 import tim.prune.data.DataPoint;
5
6
7 /**
8 * Class for comparing waypoints to sort them by name or timestamp
9 */
10 public class WaypointComparer implements Comparator<DataPoint>
11 {
12 /** Sort mode */
13 private SortMode _sortMode;
14
15
16 /**
17 * Constructor
18 * @param inMode sort mode
19 */
20 public WaypointComparer(SortMode inMode)
21 {
22 _sortMode = inMode;
23 }
24
25 /**
26 * Main compare method
27 */
28 public int compare(DataPoint inP1, DataPoint inP2)
29 {
30 if (inP2 == null || !inP2.isWaypoint()) return -1; // all nulls at end
31 if (inP1 == null || !inP1.isWaypoint()) return 1;
32
33 // Sort by time, if requested
34 int result = 0;
35 if (_sortMode == SortMode.SORTBY_TIME) {
36 result = compareTimes(inP1, inP2);
37 }
38 // check names if names requested or if times didn't work
39 if (result == 0) {
40 result = inP1.getWaypointName().compareTo(inP2.getWaypointName());
41 }
42 // names and times equal, try longitude
43 if (result == 0) {
44 result = inP1.getLongitude().getDouble() > inP2.getLongitude().getDouble() ? 1 : -1;
45 }
46 // and latitude
47 if (result == 0) {
48 result = inP1.getLatitude().getDouble() > inP2.getLatitude().getDouble() ? 1 : -1;
49 }
50 return result;
51 }
52
53 /**
54 * Compare the timestamps of the two waypoints
55 * @param inP1 first point
56 * @param inP2 second point
57 * @return compare value (-1,0,1)
58 */
59 private int compareTimes(DataPoint inP1, DataPoint inP2)
60 {
61 // Points might not have timestamps
62 if (inP1.hasTimestamp() && !inP2.hasTimestamp()) return 1;
63 if (!inP1.hasTimestamp() && inP2.hasTimestamp()) return -1;
64 if (inP1.hasTimestamp() && inP2.hasTimestamp())
65 {
66 // Compare the timestamps
67 long secDiff = inP1.getTimestamp().getMillisecondsSince(inP2.getTimestamp());
68 return (secDiff<0?-1:(secDiff==0?0:1));
69 }
70 // neither has a timestamp
71 return 0;
72 }
73 }
0 package tim.prune.function;
1
2 import java.awt.BorderLayout;
3 import java.awt.FlowLayout;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.awt.event.KeyAdapter;
7 import java.awt.event.KeyEvent;
8
9 import javax.swing.BorderFactory;
10 import javax.swing.JButton;
11 import javax.swing.JDialog;
12 import javax.swing.JLabel;
13 import javax.swing.JPanel;
14
15 import tim.prune.App;
16 import tim.prune.GenericFunction;
17 import tim.prune.I18nManager;
18 import tim.prune.gui.WholeNumberField;
19
20 /**
21 * First step of functions which just require a single numeric
22 * parameter in order to run
23 */
24 public class ChooseSingleParameter extends GenericFunction
25 {
26 /** Parent function which needs this parameter */
27 private SingleNumericParameterFunction _parent = null;
28 /** dialog */
29 private JDialog _dialog = null;
30 /** label which might need to be changed */
31 private JLabel _descLabel = null;
32 /** entry field */
33 private WholeNumberField _numberField = null;
34 /** ok button */
35 private JButton _okButton = null;
36
37
38 /** Constructor */
39 public ChooseSingleParameter(App inApp, SingleNumericParameterFunction inFunction)
40 {
41 super(inApp);
42 _parent = inFunction;
43 }
44
45 @Override
46 public String getNameKey() {
47 return _parent.getNameKey();
48 }
49
50 @Override
51 public void begin()
52 {
53 // Make dialog window
54 if (_dialog == null)
55 {
56 _dialog = new JDialog(_parentFrame, I18nManager.getText(_parent.getNameKey()), true);
57 _dialog.setLocationRelativeTo(_parentFrame);
58 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
59 _dialog.getContentPane().add(makeDialogComponents());
60 _dialog.pack();
61 }
62 // refresh and show the dialog
63 _descLabel.setText(I18nManager.getText(_parent.getDescriptionKey()));
64 int param = _parent.getCurrentParamValue();
65 if (param > 0) {
66 _numberField.setValue(param);
67 }
68 else {
69 _numberField.setText("");
70 }
71 _dialog.setVisible(true);
72 enableOkButton();
73 }
74
75 /**
76 * Create dialog components
77 * @return Panel containing all gui elements in dialog
78 */
79 private JPanel makeDialogComponents()
80 {
81 JPanel dialogPanel = new JPanel();
82 dialogPanel.setLayout(new BorderLayout());
83 // label
84 _descLabel = new JLabel(I18nManager.getText(_parent.getDescriptionKey()));
85 dialogPanel.add(_descLabel, BorderLayout.NORTH);
86 // Centre panel with number entry field
87 JPanel centrePanel = new JPanel();
88 centrePanel.setLayout(new BorderLayout(8, 8));
89 _numberField = new WholeNumberField(4);
90 centrePanel.add(_numberField, BorderLayout.NORTH);
91 dialogPanel.add(centrePanel, BorderLayout.CENTER);
92
93 // Listener to enable/disable ok button
94 _numberField.addActionListener(new ActionListener() {
95 public void actionPerformed(ActionEvent arg0) {
96 enableOkButton();
97 }
98 });
99 _numberField.addKeyListener(new KeyAdapter() {
100 public void keyReleased(KeyEvent inE)
101 {
102 int eCode = inE.getKeyCode();
103 if (eCode == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
104 else if (eCode == KeyEvent.VK_ENTER) {finish();}
105 super.keyReleased(inE);
106 }
107 });
108
109 // button panel at bottom
110 JPanel buttonPanel = new JPanel();
111 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
112 _okButton = new JButton(I18nManager.getText("button.ok"));
113 ActionListener okListener = new ActionListener() {
114 public void actionPerformed(ActionEvent e)
115 {
116 finish();
117 }
118 };
119 _okButton.addActionListener(okListener);
120 _okButton.setEnabled(false);
121 buttonPanel.add(_okButton);
122 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
123 cancelButton.addActionListener(new ActionListener() {
124 public void actionPerformed(ActionEvent e)
125 {
126 _dialog.dispose();
127 }
128 });
129 buttonPanel.add(cancelButton);
130 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
131 dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
132 return dialogPanel;
133 }
134
135
136 /**
137 * Enable or disable the OK button as appropriate
138 */
139 private void enableOkButton()
140 {
141 _okButton.setEnabled(_numberField.getValue() >= _parent.getMinAllowedValue()
142 && _numberField.getValue() <= _parent.getMaxAllowedValue());
143 }
144
145 /**
146 * The OK button (or Enter) has been pressed
147 */
148 private void finish()
149 {
150 if (_numberField.getValue() >= _parent.getMinAllowedValue()
151 && _numberField.getValue() <= _parent.getMaxAllowedValue())
152 {
153 _parent.completeFunction(_numberField.getValue());
154 _dialog.dispose();
155 }
156 }
157 }
9090
9191 // Set status label according to error or "none found", leave blank if ok
9292 if (_errorMessage == null && _trackListModel.isEmpty()) {
93 _errorMessage = I18nManager.getText("dialog.gpsies.nonefound");
93 _errorMessage = I18nManager.getText("dialog.wikipedia.nonefound");
9494 }
9595 _statusLabel.setText(_errorMessage == null ? "" : _errorMessage);
9696 }
22 import javax.swing.JOptionPane;
33
44 import tim.prune.App;
5 import tim.prune.GenericFunction;
65 import tim.prune.I18nManager;
76 import tim.prune.data.DataPoint;
87 import tim.prune.data.Track;
1110 /**
1211 * Function to interpolate between the points in a range
1312 */
14 public class InterpolateFunction extends GenericFunction
13 public class InterpolateFunction extends SingleNumericParameterFunction
1514 {
1615 /**
1716 * Constructor
1817 * @param inApp app object
1918 */
2019 public InterpolateFunction(App inApp) {
21 super(inApp);
20 super(inApp, 1, 1000);
2221 }
2322
2423 /** @return name key */
2625 return "function.interpolate";
2726 }
2827
28 /** @return description key for input parameter */
29 public String getDescriptionKey() {
30 return "dialog.interpolate.parameter.text";
31 }
32
33 /** @return current (or default) parameter value */
34 public int getCurrentParamValue() {
35 return 0;
36 }
37
2938 /**
3039 * Perform the operation
3140 */
3241 public void begin()
42 {
43 // not needed, we just use the completeFunction method instead
44 }
45
46 /**
47 * Complete the function after the input parameter has been chosen
48 */
49 public void completeFunction(int inParam)
3350 {
3451 // Firstly, work out whether the selected range only contains waypoints or not
3552 final int startIndex = _app.getTrackInfo().getSelection().getStart();
4865 betweenWaypoints = true;
4966 }
5067
51 // Get number of points to add
52 Object numPointsStr = JOptionPane.showInputDialog(_parentFrame,
53 I18nManager.getText("dialog.interpolate.parameter.text"),
54 I18nManager.getText(getNameKey()),
55 JOptionPane.QUESTION_MESSAGE, null, null, "");
56 if (numPointsStr == null) {return;}
57 int numToAdd = parseNumber(numPointsStr);
58 if (numToAdd <= 0 || numToAdd > 1000)
59 {
60 _app.showErrorMessage(getNameKey(), "error.interpolate.invalidparameter");
61 return;
62 }
63
6468 if (startIndex < 0 || endIndex < 0 || endIndex <= startIndex) {
6569 return;
6670 }
6771
6872 // construct new point array with the interpolated points
73 final int numToAdd = inParam;
6974 final Track track = _app.getTrackInfo().getTrack();
7075 final int maxToAdd = (endIndex-startIndex) * numToAdd;
7176 final int extendedSize = track.getNumPoints() + maxToAdd;
140145 }
141146 return false;
142147 }
143
144 /**
145 * Helper method to parse an Object into an integer
146 * @param inObject object, eg from dialog
147 * @return int value given
148 */
149 private static int parseNumber(Object inObject)
150 {
151 int num = 0;
152 if (inObject != null)
153 {
154 try
155 {
156 num = Integer.parseInt(inObject.toString());
157 }
158 catch (NumberFormatException nfe)
159 {}
160 }
161 return num;
162 }
163148 }
+0
-109
tim/prune/function/PhotoComparer.java less more
0 package tim.prune.function;
1
2 import java.util.Comparator;
3
4 import tim.prune.data.DataPoint;
5
6 /**
7 * Class for comparing photos to sort them by name or timestamp
8 */
9 public class PhotoComparer implements Comparator<DataPoint>
10 {
11 public enum SortMode {
12 SORTBY_NAME, SORTBY_TIME
13 };
14
15 /** Sort mode */
16 private SortMode _sortMode = SortMode.SORTBY_NAME;
17
18 /**
19 * Constructor
20 * @param inMode sort mode
21 */
22 public PhotoComparer(SortMode inMode)
23 {
24 _sortMode = inMode;
25 }
26
27 /**
28 * Main compare method
29 */
30 public int compare(DataPoint inP1, DataPoint inP2)
31 {
32 if (inP2 == null || inP2.getPhoto() == null) return -1; // all nulls at end
33 if (inP1 == null || inP1.getPhoto() == null) return 1;
34 // Sort by name
35 int result = 0;
36 if (_sortMode == SortMode.SORTBY_NAME) {
37 result = compareNames(inP1, inP2);
38 }
39 if (result == 0) {
40 result = compareTimes(inP1, inP2);
41 }
42 // check names if times didn't work
43 if (result == 0 && _sortMode == SortMode.SORTBY_TIME) {
44 result = compareNames(inP1, inP2);
45 }
46 // names and times equal, try width and height
47 if (result == 0) {
48 result = compareSizes(inP1, inP2);
49 }
50 return result;
51 }
52
53 /**
54 * Compare the names of the two photo points
55 * @param inP1 first point
56 * @param inP2 second point
57 * @return compare value (-1,0,1)
58 */
59 private int compareNames(DataPoint inP1, DataPoint inP2)
60 {
61 // If the files can't be compared, use the photo names
62 if (inP1.getPhoto().getFile() == null || inP2.getPhoto().getFile() == null) {
63 return inP1.getPhoto().getName().compareTo(inP2.getPhoto().getName());
64 }
65 // both photos have files, so just compare the files
66 return inP1.getPhoto().getFile().compareTo(inP2.getPhoto().getFile());
67 }
68
69 /**
70 * Compare the timestamps of the two photo points
71 * @param inP1 first point
72 * @param inP2 second point
73 * @return compare value (-1,0,1)
74 */
75 private int compareTimes(DataPoint inP1, DataPoint inP2)
76 {
77 // Photos might not have timestamps
78 if (!inP2.hasTimestamp()) return -1;
79 if (!inP1.hasTimestamp()) return 1;
80 // Compare the timestamps
81 long secDiff = inP1.getPhoto().getTimestamp().getSecondsSince(inP2.getPhoto().getTimestamp());
82 return (secDiff<0?-1:(secDiff==0?0:1));
83 }
84
85 /**
86 * Compare the sizes of the two photos
87 * @param inP1 first point
88 * @param inP2 second point
89 * @return compare value (-1,0,1)
90 */
91 private int compareSizes(DataPoint inP1, DataPoint inP2)
92 {
93 // Try the widths
94 int w1 = inP1.getPhoto().getWidth();
95 int w2 = inP2.getPhoto().getWidth();
96 if (w2 <= 0) return -1;
97 if (w1 <= 0) return 1;
98 if (w1 != w2) return (w2 > w1 ? 1 : -1);
99 // Try the heights
100 int h1 = inP1.getPhoto().getHeight();
101 int h2 = inP2.getPhoto().getHeight();
102 if (h2 <= 0) return -1;
103 if (h1 <= 0) return 1;
104 if (h1 != h2) return (h2 > h1 ? 1 : -1);
105 // sizes same
106 return 0;
107 }
108 }
0 package tim.prune.function;
1
2 import java.awt.BorderLayout;
3 import java.awt.FlowLayout;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6
7 import javax.swing.BorderFactory;
8 import javax.swing.BoxLayout;
9 import javax.swing.ButtonGroup;
10 import javax.swing.JButton;
11 import javax.swing.JDialog;
12 import javax.swing.JLabel;
13 import javax.swing.JPanel;
14 import javax.swing.JRadioButton;
15
16 import tim.prune.App;
17 import tim.prune.GenericFunction;
18 import tim.prune.I18nManager;
19 import tim.prune.data.sort.SortMode;
20
21 /**
22 * Abstract superclass for the functions which rearrange points,
23 * such as waypoints or photo points
24 */
25 public abstract class RearrangeFunction extends GenericFunction
26 {
27 /** Function dialog */
28 private JDialog _dialog = null;
29 /** Radio buttons for start/end/nearest */
30 private JRadioButton[] _positionRadios = null;
31 /** Radio buttons for sorting */
32 private JRadioButton[] _sortRadios = null;
33 /** Is the "nearest" option available? */
34 private boolean _nearestAvailable = false;
35
36
37 /** Enumeration for rearrange commands */
38 protected enum Rearrange
39 {
40 /** Rearrange all waypoints to start */
41 TO_START,
42 /** Rearrange all waypoints to end */
43 TO_END,
44 /** Rearrange each waypoint to nearest track point */
45 TO_NEAREST
46 }
47
48
49 /**
50 * Constructor
51 * @param inApp app object
52 * @param isNearestAvailable true if nearest option is visible
53 */
54 public RearrangeFunction(App inApp, boolean isNearestAvailable)
55 {
56 super(inApp);
57 _nearestAvailable = isNearestAvailable;
58 }
59
60 /**
61 * Begin the function by showing the dialog
62 */
63 public void begin()
64 {
65 // Make dialog window
66 if (_dialog == null)
67 {
68 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
69 _dialog.setLocationRelativeTo(_parentFrame);
70 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
71 _dialog.getContentPane().add(makeDialogComponents());
72 _dialog.pack();
73 }
74 // If sorting by time isn't available, then disable radio button
75 _sortRadios[2].setEnabled(isSortByTimeAllowed());
76 // Show dialog
77 _dialog.setVisible(true);
78 }
79
80
81 /**
82 * Create dialog components
83 * @return Panel containing all gui elements in dialog
84 */
85 private JPanel makeDialogComponents()
86 {
87 JPanel dialogPanel = new JPanel();
88 dialogPanel.setLayout(new BorderLayout());
89 JLabel descLabel = new JLabel(I18nManager.getText(getDescriptionKey()));
90 descLabel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
91 dialogPanel.add(descLabel, BorderLayout.NORTH);
92 // Radios for position (start / end / nearest)
93 _positionRadios = new JRadioButton[3];
94 final String[] posNames = {"tostart", "toend", "tonearest"};
95 ButtonGroup posGroup = new ButtonGroup();
96 JPanel posPanel = new JPanel();
97 posPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
98 for (int i=0; i<posNames.length; i++)
99 {
100 _positionRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrange." + posNames[i]));
101 posGroup.add(_positionRadios[i]);
102 posPanel.add(_positionRadios[i]);
103 }
104 _positionRadios[0].setSelected(true);
105 _positionRadios[2].setVisible(_nearestAvailable);
106
107 // Radios for sort (none / filename / time)
108 _sortRadios = new JRadioButton[3];
109 final String[] sortNames = {"nosort", getSortNameKey(), "sortbytime"};
110 ButtonGroup sortGroup = new ButtonGroup();
111 JPanel sortPanel = new JPanel();
112 sortPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
113 for (int i=0; i<3; i++)
114 {
115 _sortRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrange." + sortNames[i]));
116 sortGroup.add(_sortRadios[i]);
117 sortPanel.add(_sortRadios[i]);
118 }
119 _sortRadios[0].setSelected(true);
120 // Use listener to disable all sort options if nearest type chosen, re-enable otherwise
121 ActionListener rearrListener = new ActionListener() {
122 public void actionPerformed(ActionEvent arg0) {
123 final boolean sortAvailable = !_positionRadios[2].isSelected();
124 for (int i=0; i<_sortRadios.length; i++) {
125 _sortRadios[i].setEnabled(sortAvailable);
126 }
127 }
128 };
129 for (int i=0; i<_positionRadios.length; i++) {
130 _positionRadios[i].addActionListener(rearrListener);
131 }
132 // add to middle of dialog
133 JPanel centrePanel = new JPanel();
134 centrePanel.setLayout(new BoxLayout(centrePanel, BoxLayout.Y_AXIS));
135 centrePanel.add(posPanel);
136 centrePanel.add(sortPanel);
137 dialogPanel.add(centrePanel, BorderLayout.CENTER);
138 // button panel at bottom
139 JPanel buttonPanel = new JPanel();
140 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
141 JButton okButton = new JButton(I18nManager.getText("button.ok"));
142 okButton.addActionListener(new ActionListener() {
143 public void actionPerformed(ActionEvent e) {
144 finish();
145 _dialog.dispose();
146 }
147 });
148 buttonPanel.add(okButton);
149 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
150 cancelButton.addActionListener(new ActionListener() {
151 public void actionPerformed(ActionEvent e) {
152 _dialog.dispose();
153 }
154 });
155 buttonPanel.add(cancelButton);
156 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
157 return dialogPanel;
158 }
159
160 /**
161 * @return true if sorting by time is allowed, false otherwise
162 */
163 protected boolean isSortByTimeAllowed()
164 {
165 return true;
166 }
167
168 /**
169 * @return the selected rearrange option
170 */
171 protected Rearrange getRearrangeOption()
172 {
173 if (_positionRadios[0].isSelected()) {
174 return Rearrange.TO_START;
175 }
176 if (_positionRadios[1].isSelected()) {
177 return Rearrange.TO_END;
178 }
179 return Rearrange.TO_NEAREST;
180 }
181
182 /**
183 * @return the selected sort mode
184 */
185 protected SortMode getSortMode()
186 {
187 if (_sortRadios[0].isSelected()) {
188 return SortMode.DONT_SORT;
189 }
190 if (_sortRadios[1].isSelected()) {
191 return SortMode.SORTBY_NAME;
192 }
193 return SortMode.SORTBY_TIME;
194 }
195
196 /** @return key for description */
197 protected abstract String getDescriptionKey();
198
199 /** @return partial key for the sort by name radio */
200 protected abstract String getSortNameKey();
201
202 /**
203 * Perform the rearrange
204 */
205 protected abstract void finish();
206 }
00 package tim.prune.function;
11
2 import java.awt.BorderLayout;
3 import java.awt.FlowLayout;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
62 import java.util.Arrays;
7
8 import javax.swing.BoxLayout;
9 import javax.swing.ButtonGroup;
10 import javax.swing.JButton;
11 import javax.swing.JDialog;
12 import javax.swing.JLabel;
133 import javax.swing.JOptionPane;
14 import javax.swing.JPanel;
15 import javax.swing.JRadioButton;
16
174 import tim.prune.App;
18 import tim.prune.GenericFunction;
195 import tim.prune.I18nManager;
206 import tim.prune.data.DataPoint;
217 import tim.prune.data.Track;
8 import tim.prune.data.sort.PhotoComparer;
9 import tim.prune.data.sort.SortMode;
2210 import tim.prune.undo.UndoRearrangePhotos;
2311
2412 /**
2513 * Class to provide the function for rearranging photo points
2614 */
27 public class RearrangePhotosFunction extends GenericFunction
15 public class RearrangePhotosFunction extends RearrangeFunction
2816 {
29 /** Function dialog */
30 private JDialog _dialog = null;
31 /** Radio buttons for start/end */
32 private JRadioButton[] _positionRadios = null;
33 /** Radio buttons for sorting */
34 private JRadioButton[] _sortRadios = null;
35
36
3717 /**
3818 * Constructor
3919 * @param inApp app object
4020 */
4121 public RearrangePhotosFunction(App inApp)
4222 {
43 super(inApp);
23 super(inApp, false);
4424 }
4525
46 /** Begin the rearrange */
47 public void begin()
48 {
49 // Make dialog window
50 if (_dialog == null)
51 {
52 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
53 _dialog.setLocationRelativeTo(_parentFrame);
54 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
55 _dialog.getContentPane().add(makeDialogComponents());
56 _dialog.pack();
57 }
58 // Reset dialog and show
59 _dialog.setVisible(true);
60 }
61
62 /** Get the name key (not needed) */
26 /** Get the name key */
6327 public String getNameKey() {
6428 return "function.rearrangephotos";
6529 }
6630
31 /** Get the description key */
32 public String getDescriptionKey() {
33 return "dialog.rearrangephotos.desc";
34 }
6735
68 /**
69 * Create dialog components
70 * @return Panel containing all gui elements in dialog
71 */
72 private JPanel makeDialogComponents()
73 {
74 JPanel dialogPanel = new JPanel();
75 dialogPanel.setLayout(new BorderLayout());
76 dialogPanel.add(new JLabel(I18nManager.getText("dialog.rearrangephotos.desc")), BorderLayout.NORTH);
77 // Radios for position (start / end)
78 _positionRadios = new JRadioButton[2];
79 final String[] posNames = {"tostart", "toend"};
80 ButtonGroup posGroup = new ButtonGroup();
81 JPanel posPanel = new JPanel();
82 posPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
83 for (int i=0; i<2; i++)
84 {
85 _positionRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrangephotos." + posNames[i]));
86 posGroup.add(_positionRadios[i]);
87 posPanel.add(_positionRadios[i]);
88 }
89 _positionRadios[0].setSelected(true);
90 // Radios for sort (none / filename / time)
91 _sortRadios = new JRadioButton[3];
92 final String[] sortNames = {"nosort", "sortbyfilename", "sortbytime"};
93 ButtonGroup sortGroup = new ButtonGroup();
94 JPanel sortPanel = new JPanel();
95 sortPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
96 for (int i=0; i<3; i++)
97 {
98 _sortRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrangephotos." + sortNames[i]));
99 sortGroup.add(_sortRadios[i]);
100 sortPanel.add(_sortRadios[i]);
101 }
102 _sortRadios[0].setSelected(true);
103 // add to middle of dialog
104 JPanel centrePanel = new JPanel();
105 centrePanel.setLayout(new BoxLayout(centrePanel, BoxLayout.Y_AXIS));
106 centrePanel.add(posPanel);
107 centrePanel.add(sortPanel);
108 dialogPanel.add(centrePanel, BorderLayout.CENTER);
109 // button panel at bottom
110 JPanel buttonPanel = new JPanel();
111 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
112 JButton okButton = new JButton(I18nManager.getText("button.ok"));
113 okButton.addActionListener(new ActionListener() {
114 public void actionPerformed(ActionEvent e) {
115 finish();
116 _dialog.dispose();
117 }
118 });
119 buttonPanel.add(okButton);
120 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
121 cancelButton.addActionListener(new ActionListener() {
122 public void actionPerformed(ActionEvent e) {
123 _dialog.dispose();
124 }
125 });
126 buttonPanel.add(cancelButton);
127 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
128 return dialogPanel;
36 /** Sort by filename key */
37 protected String getSortNameKey() {
38 return "sortbyfilename";
12939 }
13040
13141 /**
13242 * Perform the rearrange
13343 */
134 private void finish()
44 protected void finish()
13545 {
13646 Track track = _app.getTrackInfo().getTrack();
13747 UndoRearrangePhotos undo = new UndoRearrangePhotos(track);
14454 for (int i=0; i<numPoints; i++)
14555 {
14656 DataPoint point = track.getPoint(i);
147 if (point.getPhoto() != null) {
57 if (point.getPhoto() != null)
58 {
14859 photos[numPhotos] = point;
14960 numPhotos++;
15061 }
151 else {
62 else
63 {
15264 nonPhotos[numNonPhotos] = point;
15365 numNonPhotos++;
15466 }
15567 }
156 // Sort photos if necessary
157 if (!_sortRadios[0].isSelected() && numPhotos > 1) {
158 sortPhotos(photos, _sortRadios[1].isSelected());
68 boolean pointsChanged = false;
69 if (numPhotos > 0)
70 {
71 Rearrange rearrangeOption = getRearrangeOption();
72 SortMode sortOption = getSortMode();
73 // Sort photos if necessary
74 if (sortOption != SortMode.DONT_SORT && numPhotos > 1) {
75 sortPhotos(photos, sortOption);
76 }
77 // Put the non-photo points and photo points together
78 DataPoint[] neworder = new DataPoint[numPoints];
79 if (rearrangeOption == Rearrange.TO_START)
80 {
81 // photos at front
82 System.arraycopy(photos, 0, neworder, 0, numPhotos);
83 System.arraycopy(nonPhotos, 0, neworder, numPhotos, numNonPhotos);
84 }
85 else
86 {
87 // photos at end
88 System.arraycopy(nonPhotos, 0, neworder, 0, numNonPhotos);
89 System.arraycopy(photos, 0, neworder, numNonPhotos, numPhotos);
90 }
91
92 // Give track the new point order
93 pointsChanged = track.replaceContents(neworder);
15994 }
160 // Put the non-photo points and photo points together
161 DataPoint[] neworder = new DataPoint[numPoints];
162 if (_positionRadios[0].isSelected()) {
163 // photos at front
164 System.arraycopy(photos, 0, neworder, 0, numPhotos);
165 System.arraycopy(nonPhotos, 0, neworder, numPhotos, numNonPhotos);
166 }
167 else {
168 // photos at end
169 System.arraycopy(nonPhotos, 0, neworder, 0, numNonPhotos);
170 System.arraycopy(photos, 0, neworder, numNonPhotos, numPhotos);
171 }
172 // Give track the new point order
173 if (track.replaceContents(neworder))
95 // did anything change?
96 if (pointsChanged)
17497 {
17598 _app.getTrackInfo().getSelection().clearAll();
17699 _app.completeFunction(undo, I18nManager.getText("confirm.rearrangephotos"));
186109 /**
187110 * Sort the given photo list either by filename or by time
188111 * @param inPhotos array of DataPoint objects to sort
189 * @param inSortByFile true to sort by filename, false to sort by timestamp
112 * @param inSortOrder sort order
190113 * @return sorted array
191114 */
192 private static void sortPhotos(DataPoint[] inPhotos, boolean inSortByFile)
115 private static void sortPhotos(DataPoint[] inPhotos, SortMode inSortMode)
193116 {
194 PhotoComparer comparer = new PhotoComparer(inSortByFile ? PhotoComparer.SortMode.SORTBY_NAME : PhotoComparer.SortMode.SORTBY_TIME);
117 PhotoComparer comparer = new PhotoComparer(inSortMode);
195118 Arrays.sort(inPhotos, comparer);
196119 }
197120 }
00 package tim.prune.function;
1
2 import java.util.Arrays;
13
24 import javax.swing.JOptionPane;
35
46 import tim.prune.App;
5 import tim.prune.GenericFunction;
67 import tim.prune.I18nManager;
8 import tim.prune.data.Checker;
9 import tim.prune.data.DataPoint;
710 import tim.prune.data.Track;
11 import tim.prune.data.sort.SortMode;
12 import tim.prune.data.sort.WaypointComparer;
813 import tim.prune.undo.UndoRearrangeWaypoints;
914
1015 /**
1116 * Class to provide the function for rearranging waypoints
1217 */
13 public class RearrangeWaypointsFunction extends GenericFunction
18 public class RearrangeWaypointsFunction extends RearrangeFunction
1419 {
15
16 /** Enumeration for rearrange commands */
17 public enum Rearrange
18 {
19 /** Rearrange all waypoints to start */
20 TO_START,
21 /** Rearrange all waypoints to end */
22 TO_END,
23 /** Rearrange each waypoint to nearest track point */
24 TO_NEAREST
25 }
2620
2721 /**
2822 * Constructor
3024 */
3125 public RearrangeWaypointsFunction(App inApp)
3226 {
33 super(inApp);
27 super(inApp, true);
3428 }
3529
36 /** Begin the rearrange (not needed) */
37 public void begin() {
30 /** Get the name key */
31 public String getNameKey() {
32 return "function.rearrangewaypoints";
3833 }
3934
40 /** Get the name key (not needed) */
41 public String getNameKey() {
42 return null;
35 /** Get whether sorting by time is allowed or not */
36 protected boolean isSortByTimeAllowed() {
37 return Checker.haveWaypointsGotTimestamps(_app.getTrackInfo().getTrack());
38 }
39
40 /** Get the description key */
41 public String getDescriptionKey() {
42 return "dialog.rearrangewaypoints.desc";
43 }
44
45 /** Sort by name key */
46 protected String getSortNameKey() {
47 return "sortbyname";
4348 }
4449
4550 /**
46 * Rearrange the waypoints into track order
47 * @param inFunction nearest point, all to end or all to start
51 * Perform the rearrange and sort according to the radio buttons
4852 */
49 public void rearrangeWaypoints(Rearrange inFunction)
53 protected void finish()
5054 {
5155 Track track = _app.getTrackInfo().getTrack();
56 // Figure out what is required from the radio buttons
57 Rearrange rearrangeOption = getRearrangeOption();
58 SortMode sortOption = getSortMode();
59
5260 UndoRearrangeWaypoints undo = new UndoRearrangeWaypoints(track);
5361 boolean success = false;
54 if (inFunction == Rearrange.TO_START || inFunction == Rearrange.TO_END)
62 if (rearrangeOption == Rearrange.TO_START || rearrangeOption == Rearrange.TO_END)
5563 {
5664 // Collect the waypoints to the start or end of the track
57 success = track.collectWaypoints(inFunction == Rearrange.TO_START);
65 success = collectWaypoints(rearrangeOption, sortOption);
5866 }
5967 else
6068 {
7381 }
7482 }
7583
84
85 /**
86 * Do the collection and sorting of the waypoints
87 * @param inRearrangeOption beginning or end
88 * @param inSortOption optional sort criterion
89 * @return true on success
90 */
91 private boolean collectWaypoints(Rearrange inRearrangeOption, SortMode inSortOption)
92 {
93 // Check for mixed data, numbers of waypoints & nons
94 int numWaypoints = 0, numNonWaypoints = 0;
95 boolean wayAfterNon = false, nonAfterWay = false;
96 Track track = _app.getTrackInfo().getTrack();
97 final int numPoints = track.getNumPoints();
98 DataPoint[] waypoints = new DataPoint[numPoints];
99 DataPoint[] nonWaypoints = new DataPoint[numPoints];
100 DataPoint point = null;
101 for (int i=0; i<numPoints; i++)
102 {
103 point = track.getPoint(i);
104 if (point.isWaypoint())
105 {
106 waypoints[numWaypoints] = point;
107 numWaypoints++;
108 wayAfterNon |= (numNonWaypoints > 0);
109 }
110 else
111 {
112 nonWaypoints[numNonWaypoints] = point;
113 numNonWaypoints++;
114 nonAfterWay |= (numWaypoints > 0);
115 }
116 }
117
118 // Exit if the data is already in the specified order
119 final boolean wpsToStart = (inRearrangeOption == Rearrange.TO_START);
120 final boolean doSort = (inSortOption != SortMode.DONT_SORT);
121 if (numWaypoints == 0 || numNonWaypoints == 0
122 || (wpsToStart && !wayAfterNon && nonAfterWay && !doSort)
123 || (!wpsToStart && wayAfterNon && !nonAfterWay && !doSort)
124 || inRearrangeOption == Rearrange.TO_NEAREST)
125 {
126 return false;
127 }
128 // Note: it could still be that the rearrange and sort has no effect, but we don't know yet
129 // Make a copy of the waypoints array first so we can compare it with after the sort
130 DataPoint[] origWaypoints = new DataPoint[numPoints];
131 System.arraycopy(waypoints, 0, origWaypoints, 0, numPoints);
132
133 if (doSort && numWaypoints > 1)
134 {
135 // Sort the waypoints array
136 WaypointComparer comparer = new WaypointComparer(inSortOption);
137 Arrays.sort(waypoints, comparer);
138 final boolean sortDidNothing = areArraysSame(origWaypoints, waypoints);
139 if (sortDidNothing && (numNonWaypoints == 0
140 || (wpsToStart && !wayAfterNon && nonAfterWay)
141 || (!wpsToStart && wayAfterNon && !nonAfterWay)))
142 {
143 return false;
144 }
145 }
146
147 // Copy the arrays into an array in the specified order
148 DataPoint[] neworder = new DataPoint[numPoints];
149 if (wpsToStart)
150 {
151 System.arraycopy(waypoints, 0, neworder, 0, numWaypoints);
152 System.arraycopy(nonWaypoints, 0, neworder, numWaypoints, numNonWaypoints);
153 }
154 else
155 {
156 System.arraycopy(nonWaypoints, 0, neworder, 0, numNonWaypoints);
157 System.arraycopy(waypoints, 0, neworder, numNonWaypoints, numWaypoints);
158 }
159 // Give track the new point order
160 return track.replaceContents(neworder);
161 }
162
163 /**
164 * Compare two arrays of DataPoints and see if they're identical or not
165 * @param inOriginal original array of points
166 * @param inSorted array of points after sorting
167 * @return true if the two arrays have the same points in the same order
168 */
169 private static boolean areArraysSame(DataPoint[] inOriginal, DataPoint[] inSorted)
170 {
171 if (inOriginal == null && inSorted == null) return true; // both null
172 if (inOriginal == null || inSorted == null) return false; // only one of them null
173 if (inOriginal.length != inSorted.length) return false;
174 // Loop over all points
175 for (int i=0; i<inOriginal.length; i++)
176 {
177 DataPoint origPoint = inOriginal[i];
178 DataPoint sortedPoint = inSorted[i];
179 if ((origPoint != null || sortedPoint != null)
180 && (origPoint != sortedPoint))
181 {
182 return false; // points different
183 }
184 }
185 // Must be all the same
186 return true;
187 }
76188 }
4747 else
4848 {
4949 // point is attached, so need to confirm point deletion
50 final int pointIndex = _app.getTrackInfo().getTrack().getPointIndex(currentAudio.getDataPoint());
5051 undoAction = new UndoDeleteAudio(currentAudio, _app.getTrackInfo().getSelection().getCurrentAudioIndex(),
51 currentAudio.getDataPoint(), _app.getTrackInfo().getTrack().getPointIndex(currentAudio.getDataPoint()));
52 currentAudio.getDataPoint(), pointIndex);
53 undoAction.setAtBoundaryOfSelectedRange(pointIndex == _app.getTrackInfo().getSelection().getStart() ||
54 pointIndex == _app.getTrackInfo().getSelection().getEnd());
5255 int response = JOptionPane.showConfirmDialog(_app.getFrame(),
5356 I18nManager.getText("dialog.deleteaudio.deletepoint"),
5457 I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION);
4747 else
4848 {
4949 // point is attached, so need to confirm point deletion
50 final int pointIndex = _app.getTrackInfo().getTrack().getPointIndex(currentPhoto.getDataPoint());
5051 undoAction = new UndoDeletePhoto(currentPhoto, _app.getTrackInfo().getSelection().getCurrentPhotoIndex(),
51 currentPhoto.getDataPoint(), _app.getTrackInfo().getTrack().getPointIndex(currentPhoto.getDataPoint()));
52 currentPhoto.getDataPoint(), pointIndex);
53 undoAction.setAtBoundaryOfSelectedRange(pointIndex == _app.getTrackInfo().getSelection().getStart() ||
54 pointIndex == _app.getTrackInfo().getSelection().getEnd());
5255 int response = JOptionPane.showConfirmDialog(_app.getFrame(),
5356 I18nManager.getText("dialog.deletephoto.deletepoint"),
5457 I18nManager.getText("dialog.deletephoto.title"),
157157 */
158158 private void saveConfig(File inSaveFile)
159159 {
160 // TODO: Check for null inSaveFile, then just call finish() ?
160161 FileOutputStream outStream = null;
161162 try
162163 {
9797
9898 // Set status label according to error or "none found", leave blank if ok
9999 if (_errorMessage == null && _trackListModel.isEmpty()) {
100 _errorMessage = I18nManager.getText("dialog.gpsies.nonefound");
100 _errorMessage = I18nManager.getText("dialog.wikipedia.nonefound");
101101 }
102102 _statusLabel.setText(_errorMessage == null ? "" : _errorMessage);
103103 }
0 package tim.prune.function;
1
2 import tim.prune.App;
3 import tim.prune.GenericFunction;
4 import tim.prune.data.Checker;
5 import tim.prune.data.DataPoint;
6
7 /**
8 * Function to allow the selection of which tracks to load from the file / stream
9 */
10 public class SelectSegmentFunction extends GenericFunction
11 {
12
13 /**
14 * Constructor
15 * @param inApp app object to use for load
16 */
17 public SelectSegmentFunction(App inApp)
18 {
19 super(inApp);
20 }
21
22 /**
23 * Start the function
24 */
25 public void begin()
26 {
27 // If no point selected, or a waypoint is selected, then do nothing
28 DataPoint currPoint = _app.getTrackInfo().getCurrentPoint();
29 if (currPoint != null && !currPoint.isWaypoint())
30 {
31 // Find indexes of segment start and end
32 final int currIndex = _app.getTrackInfo().getSelection().getCurrentPointIndex();
33 final int startIndex = Checker.getPreviousSegmentStart(_app.getTrackInfo().getTrack(), currIndex+1);
34 final int endIndex = Checker.getNextSegmentEnd(_app.getTrackInfo().getTrack(), currIndex);
35 // Select this range if there is one
36 if (endIndex > startIndex) {
37 _app.getTrackInfo().getSelection().selectRange(startIndex, endIndex);
38 }
39 }
40 }
41
42 /** @return name key */
43 public String getNameKey() {
44 return "function.selectsegment";
45 }
46 }
0 package tim.prune.function;
1
2 import tim.prune.App;
3 import tim.prune.DataSubscriber;
4 import tim.prune.UpdateMessageBroker;
5 import tim.prune.config.Config;
6 import tim.prune.data.Unit;
7
8 /**
9 * Function to set the tolerance for the altitude range calculations
10 */
11 public class SetAltitudeTolerance extends SingleNumericParameterFunction
12 {
13
14 /**
15 * Constructor
16 * @param inApp App object
17 */
18 public SetAltitudeTolerance(App inApp) {
19 super(inApp, 0, 100);
20 }
21
22 /** @return name key */
23 public String getNameKey() {
24 return "function.setaltitudetolerance";
25 }
26
27 /**
28 * @return description key
29 */
30 public String getDescriptionKey()
31 {
32 // Two different keys for feet and metres
33 final boolean isMetres = Config.getUnitSet().getAltitudeUnit().isStandard();
34 return "dialog.setaltitudetolerance.text." + (isMetres ? "metres" : "feet");
35 }
36
37 /**
38 * @return the current value to display
39 */
40 public int getCurrentParamValue()
41 {
42 int configVal = Config.getConfigInt(Config.KEY_ALTITUDE_TOLERANCE);
43 // Convert this to feet if necessary
44 Unit altUnit = Config.getUnitSet().getAltitudeUnit();
45 if (altUnit.isStandard()) {
46 return configVal / 100;
47 }
48 return (int) (configVal * altUnit.getMultFactorFromStd() / 100.0);
49 }
50
51 /**
52 * Run function
53 */
54 public void begin()
55 {
56 // Not required, because this function is started from a ChooseSingleParameter function
57 // and goes directly to the completeFunction method.
58 }
59
60 /**
61 * Complete the function using the given tolerance parameter
62 */
63 public void completeFunction(int inTolerance)
64 {
65 // Convert back from feet into metres again
66 Unit altUnit = Config.getUnitSet().getAltitudeUnit();
67 int configVal = inTolerance * 100;
68 if (!altUnit.isStandard()) {
69 configVal = (int) (inTolerance * 100.0 / altUnit.getMultFactorFromStd());
70 }
71 Config.setConfigInt(Config.KEY_ALTITUDE_TOLERANCE, configVal);
72 UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
73 }
74 }
00 package tim.prune.function;
11
22 import java.awt.BorderLayout;
3 import java.awt.Color;
43 import java.awt.Component;
54 import java.awt.Dimension;
65 import java.awt.GridLayout;
76 import java.awt.event.ActionEvent;
87 import java.awt.event.ActionListener;
9 import java.awt.event.MouseAdapter;
10 import java.awt.event.MouseEvent;
11
128 import javax.swing.BorderFactory;
139 import javax.swing.Box;
1410 import javax.swing.BoxLayout;
2319 import tim.prune.UpdateMessageBroker;
2420 import tim.prune.config.ColourScheme;
2521 import tim.prune.config.Config;
26 import tim.prune.gui.ColourChooser;
27 import tim.prune.gui.ColourPatch;
22 import tim.prune.gui.colour.ColourChooser;
23 import tim.prune.gui.colour.ColourPatch;
24 import tim.prune.gui.colour.ColourerSelectorPanel;
25 import tim.prune.gui.colour.PatchListener;
26 import tim.prune.gui.colour.PointColourer;
2827
2928 /**
3029 * Class to show the popup window for setting the colours
3534 private JButton _okButton = null;
3635 /** Array of 8 colour patches */
3736 private ColourPatch[] _patches = null;
37 /** colourer selection panel */
38 private ColourerSelectorPanel _colourerSelector = null;
3839 /** Single colour chooser */
3940 private ColourChooser _colourChooser = null;
4041
4849 ColourScheme.IDX_TEXT, ColourScheme.IDX_LINES
4950 };
5051
51 /**
52 * Inner class to react to patch clicks
53 */
54 class PatchListener extends MouseAdapter
55 {
56 /** Associated patch */
57 private ColourPatch _patch = null;
58 /** Constructor */
59 public PatchListener(ColourPatch inPatch) {
60 _patch = inPatch;
61 }
62 /** React to mouse clicks */
63 public void mouseClicked(MouseEvent e)
64 {
65 _colourChooser.showDialog(_patch.getBackground());
66 Color colour = _colourChooser.getChosenColour();
67 if (colour != null) _patch.setColour(colour);
68 }
69 }
7052
7153 /**
7254 * Constructor
9375 JPanel mainPanel = new JPanel();
9476 mainPanel.setLayout(new BorderLayout(0, 10));
9577
96 JLabel intro = new JLabel(I18nManager.getText("dialog.setcolours.intro"));
97 intro.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
98 mainPanel.add(intro, BorderLayout.NORTH);
78 JLabel introLabel = new JLabel(I18nManager.getText("dialog.setcolours.intro"));
79 introLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
80 mainPanel.add(introLabel, BorderLayout.NORTH);
81
82 // Panel in centre, to hold both the patch panel and the colourer panel (and maybe introLabel too?)
9983 JPanel centralPanel = new JPanel();
100 centralPanel.setLayout(new GridLayout());
84 centralPanel.setLayout(new BoxLayout(centralPanel, BoxLayout.Y_AXIS));
85
86 // Make panel for 8 colour patches
87 JPanel patchPanel = new JPanel();
88 patchPanel.setLayout(new GridLayout());
10189 _patches = new ColourPatch[8];
10290
10391 ColourScheme scheme = Config.getColourScheme();
11098 // Top label and patch
11199 colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2])));
112100 patch = new ColourPatch(scheme.getColour(INDICES[i*2]));
113 patch.addMouseListener(new PatchListener(patch));
101 patch.addMouseListener(new PatchListener(patch, _colourChooser));
114102 colPanel.add(patch);
115103 _patches[i*2] = patch;
116104 // separator
118106 // Bottom label and patch
119107 colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2+1])));
120108 patch = new ColourPatch(scheme.getColour(INDICES[i*2+1]));
121 patch.addMouseListener(new PatchListener(patch));
109 patch.addMouseListener(new PatchListener(patch, _colourChooser));
122110 colPanel.add(patch);
123111 _patches[i*2+1] = patch;
124112
125113 // Add column to panel
126114 colPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
127 centralPanel.add(colPanel);
128 }
115 patchPanel.add(colPanel);
116 }
117 patchPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
118 centralPanel.add(patchPanel);
119 centralPanel.add(Box.createVerticalStrut(15));
120
121 // now the colourer selector
122 _colourerSelector = new ColourerSelectorPanel(_colourChooser);
123 _colourerSelector.setAlignmentX(Component.LEFT_ALIGNMENT);
124 centralPanel.add(_colourerSelector);
125 // add the central panel to the main one
129126 mainPanel.add(centralPanel, BorderLayout.CENTER);
130127
131128 // Buttons at the bottom
175172 {
176173 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()));
177174 _dialog.setLocationRelativeTo(_parentFrame);
175 _colourChooser = new ColourChooser(_dialog);
178176 _dialog.getContentPane().add(makeContents());
179177 _dialog.pack();
180 _colourChooser = new ColourChooser(_dialog);
181178 }
182179 // Reset colours to current ones
183180 ColourScheme scheme = Config.getColourScheme();
184181 for (int i=0; i<8; i++) {
185182 _patches[i].setColour(scheme.getColour(INDICES[i]));
186183 }
184 PointColourer colourer = Config.getPointColourer();
185 _colourerSelector.init(colourer, scheme.getColour(ColourScheme.IDX_POINT));
187186 _dialog.setVisible(true);
188187 _okButton.requestFocus();
189188 }
199198 scheme.setColour(INDICES[i], _patches[i].getBackground());
200199 }
201200 Config.updateColourScheme();
201 PointColourer colourer = _colourerSelector.getSelectedColourer();
202 Config.updatePointColourer(colourer);
203 _app.updatePointColourer();
202204 UpdateMessageBroker.informSubscribers();
203205 }
204206 }
00 package tim.prune.function;
1
2 import javax.swing.JOptionPane;
31
42 import tim.prune.App;
53 import tim.prune.DataSubscriber;
6 import tim.prune.GenericFunction;
7 import tim.prune.I18nManager;
84 import tim.prune.UpdateMessageBroker;
95 import tim.prune.config.Config;
106
11 public class SetLineWidth extends GenericFunction
7 /**
8 * Function to set the width with which lines are drawn
9 */
10 public class SetLineWidth extends SingleNumericParameterFunction
1211 {
1312
1413 /**
1615 * @param inApp App object
1716 */
1817 public SetLineWidth(App inApp) {
19 super(inApp);
18 super(inApp, 1, 4);
2019 }
2120
2221 /** @return name key */
2423 return "function.setlinewidth";
2524 }
2625
26 /** @return description key */
27 public String getDescriptionKey() {
28 return "dialog.setlinewidth.text";
29 }
30
31 /** @return the current value to display */
32 public int getCurrentParamValue() {
33 return Config.getConfigInt(Config.KEY_LINE_WIDTH);
34 }
2735
2836 /**
2937 * Run function
3038 */
3139 public void begin()
3240 {
33 int currLineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH);
34 if (currLineWidth < 1 || currLineWidth > 4) {
35 currLineWidth = 2;
36 }
37 Object lineWidthStr = JOptionPane.showInputDialog(_app.getFrame(),
38 I18nManager.getText("dialog.setlinewidth.text"),
39 I18nManager.getText(getNameKey()),
40 JOptionPane.QUESTION_MESSAGE, null, null, "" + currLineWidth);
41 if (lineWidthStr != null)
41 // Not required, because this function is started from a ChooseSingleParameter function
42 // and goes directly to the completeFunction method.
43 }
44
45 /**
46 * Complete the function using the given line width parameter
47 */
48 public void completeFunction(int inLineWidth)
49 {
50 final int currLineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH);
51 if (inLineWidth >= 1 && inLineWidth <= 4 && inLineWidth != currLineWidth)
4252 {
43 int lineWidth = 2;
44 try {
45 lineWidth = Integer.parseInt(lineWidthStr.toString());
46 if (lineWidth >= 1 && lineWidth <= 4 && lineWidth != currLineWidth)
47 {
48 Config.setConfigInt(Config.KEY_LINE_WIDTH, lineWidth);
49 UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
50 }
51 }
52 catch (NumberFormatException nfe) {};
53 Config.setConfigInt(Config.KEY_LINE_WIDTH, inLineWidth);
54 UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
5355 }
5456 }
5557 }
155155 */
156156 private void finish()
157157 {
158 // Store exaggeration factor in config
158 // Store exaggeration factor and grid size in config
159159 Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_exaggField.getValue() * 100));
160 int terrainGridSize = _terrainPanel.getGridSize();
161 if (terrainGridSize < 20) {terrainGridSize = 20;}
162 Config.setConfigInt(Config.KEY_TERRAIN_GRID_SIZE, terrainGridSize);
163
160164 ThreeDWindow window = WindowFactory.getWindow(_parentFrame);
161165 if (window != null)
162166 {
0 package tim.prune.function;
1
2 import tim.prune.App;
3 import tim.prune.GenericFunction;
4
5 /**
6 * Abstract superclass of Functions which just take a
7 * single numeric parameter
8 */
9 public abstract class SingleNumericParameterFunction extends GenericFunction
10 {
11 /** Minimum and maximum allowed values */
12 protected int _minAllowedValue, _maxAllowedValue;
13
14 /** Constructor */
15 public SingleNumericParameterFunction(App inApp, int inMinValue, int inMaxValue)
16 {
17 super(inApp);
18 _minAllowedValue = inMinValue;
19 _maxAllowedValue = inMaxValue;
20 }
21
22 /** Get the current value for display in the dialog */
23 public abstract int getCurrentParamValue();
24
25 /** Get the key for the description label */
26 public abstract String getDescriptionKey();
27
28 /** Callback to trigger the rest of the function once the parameter has been chosen */
29 public abstract void completeFunction(int inParam);
30
31 /** @return minimum allowed value */
32 public int getMinAllowedValue() {return _minAllowedValue;}
33 /** @return maximum allowed value */
34 public int getMaxAllowedValue() {return _maxAllowedValue;}
35 }
283283 + getSvgValue(_svgHeightField, DEFAULT_SVG_HEIGHT) + "\n");
284284 writer.write("set out '" + svgFile.getAbsolutePath() + "'\n");
285285 }
286 else {
287 // For screen output, gnuplot should use the default terminal (windows or x11 or wxt or something)
288 }
286289 if (numCharts > 1) {
287290 writer.write("set multiplot layout " + numCharts + ",1\n");
288291 }
373376 break;
374377 }
375378 // Make a temporary data file for the output (one per subchart)
376 File tempFile = File.createTempFile("prunedata", null);
379 File tempFile = File.createTempFile("gpsprunedata", null);
377380 tempFile.deleteOnExit();
378381 // write out values for x and y to temporary file
379382 FileWriter tempFileWriter = null;
1414 import javax.swing.JPanel;
1515
1616 import tim.prune.App;
17 import tim.prune.GenericFunction;
1817 import tim.prune.I18nManager;
1918 import tim.prune.UpdateMessageBroker;
2019 import tim.prune.data.DataPoint;
2322 /**
2423 * Class to provide the function for track compression
2524 */
26 public class CompressTrackFunction extends GenericFunction
25 public class CompressTrackFunction extends MarkAndDeleteFunction
2726 {
2827 private Track _track = null;
2928 private JDialog _dialog = null;
3029 private JButton _okButton = null;
3130 private CompressionAlgorithm[] _algorithms = null;
3231 private SummaryLabel _summaryLabel = null;
33 /** flag to remember whether the automatic deletion has been set to always */
34 private boolean _automaticallyDelete = false;
3532
3633
3734 /**
186183 // Show confirmation dialog with OK button (not status bar message)
187184 if (numMarked > 0)
188185 {
189 // Allow calling of delete function with one click
190 final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
191 I18nManager.getText("button.always")};
192 int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
193 JOptionPane.showOptionDialog(_parentFrame,
194 I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked),
195 I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
196 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
197 if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
198 if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
199 {
200 new Thread(new Runnable() {
201 public void run() {
202 _app.finishCompressTrack();
203 }
204 }).start();
205 }
186 optionallyDeleteMarkedPoints(numMarked);
206187 }
207188 else
208189 {
0 package tim.prune.function.compress;
1
2 import javax.swing.JOptionPane;
3
4 import tim.prune.App;
5 import tim.prune.GenericFunction;
6 import tim.prune.I18nManager;
7
8 /**
9 * Superclass of those functions which mark points for deletion
10 * (and optionally delete them automatically)
11 */
12 public abstract class MarkAndDeleteFunction extends GenericFunction
13 {
14 /** flag to remember whether the automatic deletion has been set to always */
15 private boolean _automaticallyDelete = false;
16
17
18 /**
19 * Constructor
20 * @param inApp App object
21 */
22 public MarkAndDeleteFunction(App inApp)
23 {
24 super(inApp);
25 }
26
27 /**
28 * optionally delete the marked points
29 */
30 protected void optionallyDeleteMarkedPoints(int inNumMarked)
31 {
32 // Allow calling of delete function with one click
33 final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
34 I18nManager.getText("button.always")};
35 int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
36 JOptionPane.showOptionDialog(_parentFrame,
37 I18nManager.getTextWithNumber("dialog.compress.confirm", inNumMarked),
38 I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
39 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
40 if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
41 if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
42 {
43 new Thread(new Runnable() {
44 public void run() {
45 _app.finishCompressTrack();
46 }
47 }).start();
48 }
49 }
50 }
00 package tim.prune.function.compress;
11
2 import javax.swing.JOptionPane;
3
42 import tim.prune.App;
5 import tim.prune.GenericFunction;
6 import tim.prune.I18nManager;
73 import tim.prune.UpdateMessageBroker;
84 import tim.prune.data.DataPoint;
95
106 /**
117 * Function to mark all the points in the selected rectangle
128 */
13 public class MarkPointsInRectangleFunction extends GenericFunction
9 public class MarkPointsInRectangleFunction extends MarkAndDeleteFunction
1410 {
1511 /** Minimum and maximum latitude values of rectangle */
1612 private double _minLat = 0.0, _maxLat = 0.0;
1713 /** Minimum and maximum longitude values of rectangle */
1814 private double _minLon = 0.0, _maxLon = 0.0;
19 /** flag to remember whether the automatic deletion has been set to always */
20 private boolean _automaticallyDelete = false;
2115
2216
2317 /**
8579 final double pointLat = point.getLatitude().getDouble();
8680 final boolean insideRect = (pointLon >= _minLon && pointLon <= _maxLon
8781 && pointLat >= _minLat && pointLat <= _maxLat);
88 // If so, then mark it
82 // Mark it accordingly (also resetting points outside the rect to false)
8983 point.setMarkedForDeletion(insideRect);
9084 if (insideRect) {
9185 numMarked++;
9791 // Confirm message showing how many marked
9892 if (numMarked > 0)
9993 {
100 // Allow calling of delete function with one click
101 final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
102 I18nManager.getText("button.always")};
103 int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
104 JOptionPane.showOptionDialog(_parentFrame,
105 I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked),
106 I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
107 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
108 if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
109 if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
110 {
111 new Thread(new Runnable() {
112 public void run() {
113 _app.finishCompressTrack();
114 }
115 }).start();
116 }
94 optionallyDeleteMarkedPoints(numMarked);
11795 }
11896 }
11997 }
0 package tim.prune.function.deletebydate;
1
2 import java.text.DateFormat;
3 import java.util.Date;
4
5 /**
6 * Class to hold the information about a date,
7 * including how many points correspond to the date
8 * and whether it has been selected for deletion or not
9 */
10 public class DateInfo implements Comparable<DateInfo>
11 {
12 /** Date, or null for no date - used for earlier/later comparison */
13 private Date _date = null;
14 /** String representation of date, for equality comparison */
15 private String _dateString = null;
16 /** Number of points with this date */
17 private int _numPoints = 0;
18 /** Flag for deletion or retention */
19 private boolean _toDelete = false;
20
21 // Doesn't really matter what format is used here, as long as dates are different
22 private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance();
23
24 /**
25 * Constructor
26 * @param inDate date object from timestamp
27 */
28 public DateInfo(Date inDate)
29 {
30 _date = inDate;
31 if (_date == null) {
32 _dateString = "";
33 }
34 else {
35 _dateString = DEFAULT_DATE_FORMAT.format(_date);
36 }
37 _numPoints = 0;
38 _toDelete = false;
39 }
40
41 /**
42 * @return true if this info is for dateless points (points without timestamp)
43 */
44 public boolean isDateless() {
45 return (_date == null);
46 }
47
48 /**
49 * @return date object, or null
50 */
51 public Date getDate() {
52 return _date;
53 }
54
55 /**
56 * Compare with a given Date object to see if they represent the same date
57 * @param inDate date to compare
58 * @return true if they're the same date
59 */
60 public boolean isSameDate(Date inDate)
61 {
62 if (inDate == null) {
63 return (_date == null);
64 }
65 else if (_dateString == null) {
66 return false;
67 }
68 String otherDateString = DEFAULT_DATE_FORMAT.format(inDate);
69 return _dateString.equals(otherDateString);
70 }
71
72 /**
73 * Increment the point count
74 */
75 public void incrementCount() {
76 _numPoints++;
77 }
78
79 /**
80 * @return point count
81 */
82 public int getPointCount() {
83 return _numPoints;
84 }
85
86 /**
87 * @param inFlag true to delete, false to keep
88 */
89 public void setDeleteFlag(boolean inFlag) {
90 _toDelete = inFlag;
91 }
92
93 /**
94 * @return true to delete, false to keep
95 */
96 public boolean getDeleteFlag() {
97 return _toDelete;
98 }
99
100 /**
101 * Compare with another DateInfo object for sorting
102 */
103 public int compareTo(DateInfo inOther)
104 {
105 // Dateless goes first
106 if (_date == null || _dateString == null) {return -1;}
107 if (inOther._date == null || inOther._dateString == null) {return 1;}
108 // Just compare dates
109 return _date.compareTo(inOther._date);
110 }
111 }
0 package tim.prune.function.deletebydate;
1
2 import java.util.ArrayList;
3 import java.util.Collections;
4 import java.util.Date;
5 import java.util.List;
6
7 /**
8 * List of date info objects for use by the table model
9 */
10 public class DateInfoList
11 {
12 /** list of info about points according to date */
13 private List<DateInfo> _infoList = new ArrayList<DateInfo>();
14 /** previously used dateinfo object to reduce list searching */
15 private DateInfo _previousInfo = null;
16 /** true if the list has been sorted, false otherwise */
17 private boolean _hasBeenSorted = false;
18
19
20 /**
21 * Add a point to the corresponding dateinfo
22 * @param inDate date of current point, or null if no timestamp
23 */
24 public void addPoint(Date inDate)
25 {
26 if (_previousInfo != null && _previousInfo.isSameDate(inDate))
27 {
28 // found it
29 _previousInfo.incrementCount();
30 }
31 else
32 {
33 // loop through list, seeing if date already present
34 boolean foundDate = false;
35 for (DateInfo info : _infoList)
36 {
37 if (info.isSameDate(inDate))
38 {
39 info.incrementCount();
40 _previousInfo = info;
41 foundDate = true;
42 break;
43 }
44 }
45 // create new info if necessary
46 if (!foundDate)
47 {
48 _previousInfo = new DateInfo(inDate);
49 _previousInfo.incrementCount();
50 _infoList.add(_previousInfo);
51 _hasBeenSorted = false;
52 }
53 }
54 }
55
56 /**
57 * Clear the whole list
58 */
59 public void clearAll()
60 {
61 _infoList.clear();
62 _previousInfo = null;
63 _hasBeenSorted = true;
64 }
65
66 /**
67 * not used, can be removed
68 * @return true if any points without dates were found
69 */
70 public boolean hasDatelessPoints()
71 {
72 if (_infoList.isEmpty()) {return false;}
73 sort();
74 DateInfo firstInfo = _infoList.get(0);
75 return (firstInfo != null && firstInfo.isDateless() && firstInfo.getPointCount() > 0);
76 }
77
78 /**
79 * @return number of entries in the list, including dateless points
80 */
81 public int getNumEntries()
82 {
83 return _infoList.size();
84 }
85
86 /**
87 * @return the total number of points found, which should match the track size
88 */
89 public int getTotalNumPoints()
90 {
91 int total = 0;
92 for (DateInfo info : _infoList) {
93 total += info.getPointCount();
94 }
95 return total;
96 }
97
98 /**
99 * Sort the info list by ascending timestamps
100 */
101 private void sort()
102 {
103 if (!_hasBeenSorted)
104 {
105 Collections.sort(_infoList);
106 _hasBeenSorted = true;
107 }
108 }
109
110 /**
111 * Get the DateInfo object at the given index
112 * @param inIndex index in (sorted) list
113 * @return corresponding object (may throw exception if out of range)
114 */
115 public DateInfo getDateInfo(int inIndex)
116 {
117 sort();
118 return _infoList.get(inIndex);
119 }
120 }
0 package tim.prune.function.deletebydate;
1
2 import java.awt.BorderLayout;
3 import java.awt.Component;
4 import java.awt.Dimension;
5 import java.awt.FlowLayout;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.awt.event.KeyAdapter;
9 import java.awt.event.KeyEvent;
10 import java.util.Date;
11
12 import javax.swing.BorderFactory;
13 import javax.swing.JButton;
14 import javax.swing.JDialog;
15 import javax.swing.JLabel;
16 import javax.swing.JPanel;
17 import javax.swing.JScrollPane;
18 import javax.swing.JTable;
19
20 import tim.prune.App;
21 import tim.prune.DataSubscriber;
22 import tim.prune.I18nManager;
23 import tim.prune.UpdateMessageBroker;
24 import tim.prune.data.DataPoint;
25 import tim.prune.function.compress.MarkAndDeleteFunction;
26
27 /**
28 * Function to select a date or dates,
29 * and delete the corresponding points
30 */
31 public class DeleteByDateFunction extends MarkAndDeleteFunction
32 {
33 /** dialog for selecting dates */
34 private JDialog _dialog = null;
35 /** Ok button */
36 private JButton _okButton = null;
37 /** date info list */
38 private DateInfoList _infoList = new DateInfoList();
39
40
41 /**
42 * Constructor
43 * @param inApp App object
44 */
45 public DeleteByDateFunction(App inApp)
46 {
47 super(inApp);
48 }
49
50 @Override
51 public String getNameKey() {
52 return "function.deletebydate";
53 }
54
55 @Override
56 public void begin()
57 {
58 // Make a list of which dates are present in the track
59 _infoList.clearAll();
60 final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
61 for (int i=0; i<numPoints; i++)
62 {
63 DataPoint point = _app.getTrackInfo().getTrack().getPoint(i);
64 if (point != null)
65 {
66 if (point.hasTimestamp()) {
67 _infoList.addPoint(point.getTimestamp().getCalendar().getTime());
68 }
69 else {
70 _infoList.addPoint(null); // no timestamp available
71 }
72 }
73 }
74 // System.out.println("Debug: info list has dateless points? " + (_infoList.hasDatelessPoints() ? "yes":"no"));
75 // System.out.println("Debug: info list has " + _infoList.getNumEntries() + " different entries");
76 // System.out.println("Debug: info list has " + _infoList.getTotalNumPoints() + " total points");
77 // final boolean checkOk = (_infoList.getTotalNumPoints() == numPoints);
78 // System.out.println("Debug: which " + (checkOk?"IS":"ISN'T!") + " the same as track: " + numPoints);
79
80 // Loop over entries for debug
81 // if (!checkOk)
82 // {
83 // for (int i=0; i<_infoList.getNumEntries(); i++)
84 // {
85 // DateInfo info = _infoList.getDateInfo(i);
86 // System.out.println(" " + i + " (" + info.getPointCount() + " points) - " +
87 // (info.isDateless() ? "no date" : "date"));
88 // }
89 // }
90
91 // Complain if there is only one entry in the list - this means all points are on the same day
92 if (_infoList.getNumEntries() < 2)
93 {
94 _app.showErrorMessage(getNameKey(), "dialog.deletebydate.onlyonedate");
95 }
96 else
97 {
98 // Create and build dialog if necessary
99 if (_dialog == null)
100 {
101 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
102 _dialog.setLocationRelativeTo(_parentFrame);
103 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
104 _dialog.getContentPane().add(makeDialogComponents());
105 _dialog.pack();
106 }
107 // Show dialog
108 _dialog.setVisible(true);
109 }
110 }
111
112 /**
113 * Create dialog components
114 * @return Panel containing all gui elements in dialog
115 */
116 private Component makeDialogComponents()
117 {
118 JPanel dialogPanel = new JPanel();
119 dialogPanel.setLayout(new BorderLayout(5, 5));
120 // Label at top
121 JLabel topLabel = new JLabel(I18nManager.getText("dialog.deletebydate.intro"));
122 topLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
123 dialogPanel.add(topLabel, BorderLayout.NORTH);
124
125 // close window if escape pressed
126 KeyAdapter escListener = new KeyAdapter() {
127 public void keyReleased(KeyEvent inE) {
128 if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
129 _dialog.dispose();
130 }
131 }
132 };
133
134 JTable infoTable = new JTable(new DeletionTableModel(_infoList));
135 JScrollPane pane = new JScrollPane(infoTable);
136 pane.setPreferredSize(new Dimension(300, 80));
137 pane.setBorder(BorderFactory.createEmptyBorder(2, 50, 2, 50));
138 dialogPanel.add(pane, BorderLayout.CENTER);
139
140 // button panel at bottom
141 JPanel buttonPanel = new JPanel();
142 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
143 // OK button
144 _okButton = new JButton(I18nManager.getText("button.ok"));
145 _okButton.addActionListener(new ActionListener() {
146 public void actionPerformed(ActionEvent e) {
147 performDelete();
148 }
149 });
150 buttonPanel.add(_okButton);
151 _okButton.addKeyListener(escListener);
152 // Cancel button
153 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
154 cancelButton.addActionListener(new ActionListener() {
155 public void actionPerformed(ActionEvent e) {
156 _dialog.dispose();
157 }
158 });
159 cancelButton.addKeyListener(new KeyAdapter() {
160 public void keyPressed(KeyEvent inE) {
161 if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
162 }
163 });
164 buttonPanel.add(cancelButton);
165 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
166 return dialogPanel;
167 }
168
169 /**
170 * Do the actual point deletion according to the
171 * selected rows in the table
172 */
173 private void performDelete()
174 {
175 int numMarked = 0;
176 final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
177 final int numDates = _infoList.getNumEntries();
178 // Loop over all points to mark each one for deletion or not
179 for (int p=0; p<numPoints; p++)
180 {
181 DataPoint point = _app.getTrackInfo().getTrack().getPoint(p);
182 if (point != null)
183 {
184 Date date = (point.hasTimestamp() ? point.getTimestamp().getCalendar().getTime() : null);
185 boolean pointMarked = false;
186 // Try to match each of the date info objects in the list
187 for (int d=0; d<numDates; d++)
188 {
189 DateInfo info = _infoList.getDateInfo(d);
190 if ( (info.isDateless() && date == null) // matches dateless
191 || (!info.isDateless() && date != null && info.isSameDate(date)))
192 {
193 pointMarked = info.getDeleteFlag();
194 break;
195 }
196 }
197 point.setMarkedForDeletion(pointMarked);
198 if (pointMarked) {
199 numMarked++;
200 }
201 }
202 }
203 // Now points have been marked, we can ask user to delete them (or delete automatically)
204 if (numMarked > 0) {
205 optionallyDeleteMarkedPoints(numMarked);
206 }
207 else {
208 // Do nothing //System.out.println("Nothing selected to delete!");
209 // delete flags might have been reset, so refresh display
210 UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
211 }
212 _dialog.dispose();
213 }
214 }
0 package tim.prune.function.deletebydate;
1
2 import java.text.DateFormat;
3 import javax.swing.table.AbstractTableModel;
4 import tim.prune.I18nManager;
5
6 /**
7 * Table model for selecting which dates to delete
8 */
9 public class DeletionTableModel extends AbstractTableModel
10 {
11 /** info list, one for each row of table */
12 private DateInfoList _infoList = null;
13
14 /** Formatter, determining how dates appear in the table */
15 private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance();
16 /** Column heading for date */
17 private static final String COLUMN_HEADING_DATE = I18nManager.getText("fieldname.date");
18 /** Column heading for number of points */
19 private static final String COLUMN_HEADING_NUMPOINTS = I18nManager.getText("details.track.points");
20 /** Column heading for keep */
21 private static final String COLUMN_HEADING_KEEP = I18nManager.getText("dialog.deletebydate.column.keep");
22 /** Column heading for delete */
23 private static final String COLUMN_HEADING_DELETE = I18nManager.getText("dialog.deletebydate.column.delete");
24
25
26 /**
27 * Constructor
28 * @param inList date info list from function
29 */
30 public DeletionTableModel(DateInfoList inList)
31 {
32 _infoList = inList;
33 }
34
35 /**
36 * @return column count
37 */
38 public int getColumnCount()
39 {
40 return 4; // always fixed (date, numpoints, keep, delete)
41 }
42
43 /**
44 * @return row count
45 */
46 public int getRowCount()
47 {
48 if (_infoList == null) {return 0;} // shouldn't happen
49 return _infoList.getNumEntries();
50 }
51
52 /**
53 * Get the name of the column
54 * @param inColNum column number
55 * @return column name
56 */
57 public String getColumnName(int inColNum)
58 {
59 if (inColNum == 0) return COLUMN_HEADING_DATE;
60 else if (inColNum == 1) return COLUMN_HEADING_NUMPOINTS;
61 else if (inColNum == 2) return COLUMN_HEADING_KEEP;
62 else if (inColNum == 3) return COLUMN_HEADING_DELETE;
63 return "unknown column!";
64 }
65
66 /**
67 * Get the class of objects in the given column
68 * @see javax.swing.table.AbstractTableModel#getColumnClass(int)
69 */
70 public Class<?> getColumnClass(int inColumnIndex)
71 {
72 if (inColumnIndex == 1) {return Integer.class;}
73 if (inColumnIndex > 1) {return Boolean.class;}
74 return super.getColumnClass(inColumnIndex);
75 }
76
77 /**
78 * Get whether the given cell is editable
79 * @see javax.swing.table.AbstractTableModel#isCellEditable(int, int)
80 */
81 public boolean isCellEditable(int inRowIndex, int inColumnIndex)
82 {
83 return (inColumnIndex > 1);
84 }
85
86 /**
87 * Set the value at the given table cell
88 * @see javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object, int, int)
89 */
90 public void setValueAt(Object inValue, int inRowIndex, int inColumnIndex)
91 {
92 // can only edit the keep and delete columns
93 final boolean isKeep = (inColumnIndex == 2);
94 final boolean isDelete = (inColumnIndex == 3);
95 // ignore all events for other columns
96 if (isKeep || isDelete)
97 {
98 try {
99 boolean setFlag = ((Boolean) inValue).booleanValue();
100 if (setFlag)
101 {
102 _infoList.getDateInfo(inRowIndex).setDeleteFlag(isDelete);
103 // make sure the other cell (keep or delete) on the same row is updated too
104 fireTableCellUpdated(inRowIndex, 5 - inColumnIndex);
105 }
106 }
107 catch (ClassCastException cce) {}
108 }
109 }
110
111 /**
112 * @return cell contents at the given row, column inded
113 */
114 public Object getValueAt(int inRowIndex, int inColIndex)
115 {
116 try {
117 DateInfo info = _infoList.getDateInfo(inRowIndex);
118 if (info != null)
119 {
120 switch (inColIndex)
121 {
122 case 0: // date
123 if (info.isDateless()) {
124 return I18nManager.getText("dialog.deletebydate.nodate");
125 }
126 return DEFAULT_DATE_FORMAT.format(info.getDate());
127 case 1: // number of points
128 return info.getPointCount();
129 case 2: // keep
130 return !info.getDeleteFlag();
131 case 3: // delete
132 return info.getDeleteFlag();
133 }
134 }
135 }
136 catch (IndexOutOfBoundsException obe) {} // ignore, fallthrough
137 return null;
138 }
139 }
1616 private static final String _toColLabel = I18nManager.getText("dialog.distances.column.to");
1717 /** Column heading (depends on metric/imperial settings) */
1818 private String _distanceLabel = null;
19 /** Previous distance units */
20 private Unit _previousDistUnit = null;
1921
2022 /**
2123 * @return column count
6870 Unit distUnit = Config.getUnitSet().getDistanceUnit();
6971 _distanceLabel = I18nManager.getText("fieldname.distance") + " (" +
7072 I18nManager.getText(distUnit.getShortnameKey()) + ")";
73 final boolean distUnitsChanged = (distUnit != _previousDistUnit);
74 _previousDistUnit = distUnit;
75
7176 // Initialize array of distances
7277 int numRows = getRowCount();
7378 if (_distances == null || _distances.length != numRows) {
8388 _distances[i] = Distance.convertRadiansToDistance(rads);
8489 }
8590 }
86 // Let table know that it has to refresh data (and might as well refresh column headings too)
87 fireTableStructureChanged();
91 // Let table know that it has to refresh data, and maybe the whole table too
92 if (distUnitsChanged) {
93 fireTableStructureChanged();
94 }
95 else {
96 fireTableDataChanged();
97 }
8898 }
8999 }
+0
-151
tim/prune/gui/ColourChooser.java less more
0 package tim.prune.gui;
1
2 import java.awt.BorderLayout;
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.FlowLayout;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8
9 import javax.swing.BoxLayout;
10 import javax.swing.JButton;
11 import javax.swing.JDialog;
12 import javax.swing.JLabel;
13 import javax.swing.JPanel;
14 import javax.swing.JSlider;
15 import javax.swing.event.ChangeEvent;
16 import javax.swing.event.ChangeListener;
17
18 import tim.prune.I18nManager;
19
20 /**
21 * Class to offer a dialog to choose a colour.
22 * Normally a JColorChooser would be used, but this is too buggy
23 * in Java 1.6 and extremely prone to thread-locking, meaning
24 * that the application has to be killed (and all data lost).
25 */
26 public class ColourChooser
27 {
28 /** main dialog object */
29 private JDialog _dialog = null;
30 /** array of three slider objects for rgb */
31 private JSlider[] _rgbSliders = null;
32 /** array of labels for rgb values */
33 private JLabel[] _rgbLabels = null;
34 /** colour patch */
35 private ColourPatch _patch = null;
36 /** chosen colour */
37 private Color _chosenColour = null;
38
39
40 /**
41 * Constructor
42 * @param inParent parent dialog
43 */
44 public ColourChooser(JDialog inParent)
45 {
46 _dialog = new JDialog(inParent, I18nManager.getText("dialog.colourchooser.title"), true);
47 _dialog.setLocationRelativeTo(inParent);
48 _dialog.getContentPane().add(makeContents());
49 _dialog.pack();
50 }
51
52 /**
53 * Make the dialog contents
54 * @return JPanel containing dialog elements
55 */
56 private JPanel makeContents()
57 {
58 JPanel mainPanel = new JPanel();
59 mainPanel.setLayout(new BorderLayout());
60 _rgbSliders = new JSlider[3];
61 _rgbLabels = new JLabel[3];
62 _patch = new ColourPatch(Color.WHITE);
63 JPanel centrePanel = new JPanel();
64 centrePanel.setLayout(new BorderLayout());
65 centrePanel.add(_patch, BorderLayout.CENTER);
66
67 JPanel sliderPanel = new JPanel();
68 sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.Y_AXIS));
69 final String[] labelKeys = {"red", "green", "blue"};
70 for (int i=0; i<3; i++)
71 {
72 String key = I18nManager.getText("dialog.colourchooser." + labelKeys[i]);
73 sliderPanel.add(new JLabel(key));
74 _rgbSliders[i] = new JSlider(0, 255);
75 _rgbSliders[i].addChangeListener(new ChangeListener() {
76 public void stateChanged(ChangeEvent arg0) {
77 updatePatch();
78 }
79 });
80 _rgbSliders[i].setToolTipText(key);
81 JPanel sliderHolder = new JPanel();
82 sliderHolder.setLayout(new BorderLayout(5, 0));
83 sliderHolder.add(_rgbSliders[i], BorderLayout.CENTER);
84 _rgbLabels[i] = new JLabel("255");
85 _rgbLabels[i].setPreferredSize(new Dimension(40, 1));
86 sliderHolder.add(_rgbLabels[i], BorderLayout.EAST);
87 sliderPanel.add(sliderHolder);
88 }
89 centrePanel.add(sliderPanel, BorderLayout.SOUTH);
90 mainPanel.add(centrePanel, BorderLayout.CENTER);
91
92 // Button panel for ok, cancel
93 JPanel buttonPanel = new JPanel();
94 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
95 JButton okButton = new JButton(I18nManager.getText("button.ok"));
96 okButton.addActionListener(new ActionListener() {
97 public void actionPerformed(ActionEvent e) {
98 _chosenColour = _patch.getBackground();
99 _dialog.setVisible(false);
100 }
101 });
102 buttonPanel.add(okButton);
103 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
104 cancelButton.addActionListener(new ActionListener() {
105 public void actionPerformed(ActionEvent e) {
106 _chosenColour = null;
107 _dialog.setVisible(false);
108 }
109 });
110 buttonPanel.add(cancelButton);
111 mainPanel.add(buttonPanel, BorderLayout.SOUTH);
112 return mainPanel;
113 }
114
115 /**
116 * Show the dialog to choose a colour
117 * @param inStartColour current colour
118 */
119 public void showDialog(Color inStartColour)
120 {
121 // Initialise sliders
122 _rgbSliders[0].setValue(inStartColour.getRed());
123 _rgbSliders[1].setValue(inStartColour.getGreen());
124 _rgbSliders[2].setValue(inStartColour.getBlue());
125 updatePatch();
126 _dialog.setLocationRelativeTo(_dialog.getParent());
127 _dialog.setVisible(true);
128 }
129
130 /**
131 * Update the patch colour from the slider values
132 */
133 private void updatePatch()
134 {
135 for (int i=0; i<3; i++) {
136 _rgbLabels[i].setText("" + _rgbSliders[i].getValue());
137 }
138 final Color colour = new Color(_rgbSliders[0].getValue(),
139 _rgbSliders[1].getValue(),_rgbSliders[2].getValue());
140 _patch.setColour(colour);
141 }
142
143 /**
144 * @return the selected colour, or null if cancel pressed
145 */
146 public Color getChosenColour()
147 {
148 return _chosenColour;
149 }
150 }
+0
-34
tim/prune/gui/ColourPatch.java less more
0 package tim.prune.gui;
1
2 import java.awt.Color;
3 import java.awt.Dimension;
4 import javax.swing.JPanel;
5
6 import tim.prune.config.ColourUtils;
7
8 /**
9 * Class to act as a colour patch to illustrate a chosen colour
10 */
11 public class ColourPatch extends JPanel
12 {
13 /**
14 * Constructor
15 */
16 public ColourPatch(Color inColour)
17 {
18 Dimension size = new Dimension(80, 50);
19 setMinimumSize(size);
20 setPreferredSize(size);
21 setColour(inColour);
22 }
23
24 /**
25 * Set the colour of the patch
26 * @param inColour Color to use
27 */
28 public void setColour(Color inColour)
29 {
30 super.setBackground(inColour);
31 setToolTipText(ColourUtils.makeHexCode(inColour));
32 }
33 }
3131 import tim.prune.data.Field;
3232 import tim.prune.data.Photo;
3333 import tim.prune.data.Selection;
34 import tim.prune.data.SourceInfo;
3435 import tim.prune.data.SpeedCalculator;
3536 import tim.prune.data.SpeedValue;
3637 import tim.prune.data.TrackInfo;
4849 private JLabel _indexLabel = null;
4950 private JLabel _latLabel = null, _longLabel = null;
5051 private JLabel _altLabel = null;
51 private JLabel _timeLabel = null;
52 private JLabel _ptDateLabel = null, _ptTimeLabel = null;
5253 private JLabel _descLabel = null;
5354 private JLabel _speedLabel = null, _vSpeedLabel = null;
5455 private JLabel _nameLabel = null, _typeLabel = null;
56 private JLabel _filenameLabel = null;
5557
5658 // Range details
5759 private JLabel _rangeLabel = null;
8991 private static final String LABEL_POINT_LATITUDE = I18nManager.getText("fieldname.latitude") + ": ";
9092 private static final String LABEL_POINT_LONGITUDE = I18nManager.getText("fieldname.longitude") + ": ";
9193 private static final String LABEL_POINT_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": ";
92 private static final String LABEL_POINT_TIMESTAMP = I18nManager.getText("fieldname.timestamp") + ": ";
94 private static final String LABEL_POINT_DATE = I18nManager.getText("fieldname.date") + ": ";
95 private static final String LABEL_POINT_TIME = I18nManager.getText("fieldname.timestamp") + ": ";
9396 private static final String LABEL_POINT_WAYPOINTNAME = I18nManager.getText("fieldname.waypointname") + ": ";
9497 private static final String LABEL_POINT_WAYPOINTTYPE = I18nManager.getText("fieldname.waypointtype") + ": ";
9598 private static final String LABEL_POINT_DESCRIPTION = I18nManager.getText("fieldname.description") + ": ";
9699 private static final String LABEL_POINT_SPEED = I18nManager.getText("fieldname.speed") + ": ";
97100 private static final String LABEL_POINT_VERTSPEED = I18nManager.getText("fieldname.verticalspeed") + ": ";
101 private static final String LABEL_POINT_FILENAME = I18nManager.getText("details.track.file") + ": ";
98102 private static final String LABEL_RANGE_SELECTED = I18nManager.getText("details.range.selected") + ": ";
99103 private static final String LABEL_RANGE_DURATION = I18nManager.getText("fieldname.duration") + ": ";
100104 private static final String LABEL_RANGE_DISTANCE = I18nManager.getText("fieldname.distance") + ": ";
130134 pointDetailsPanel.add(_longLabel);
131135 _altLabel = new JLabel("");
132136 pointDetailsPanel.add(_altLabel);
133 _timeLabel = new JLabel("");
134 _timeLabel.setMinimumSize(new Dimension(120, 10));
135 pointDetailsPanel.add(_timeLabel);
137 _ptDateLabel = new JLabel("");
138 _ptDateLabel.setMinimumSize(new Dimension(120, 10));
139 pointDetailsPanel.add(_ptDateLabel);
140 _ptTimeLabel = new JLabel("");
141 _ptTimeLabel.setMinimumSize(new Dimension(120, 10));
142 pointDetailsPanel.add(_ptTimeLabel);
136143 _descLabel = new JLabel("");
137144 pointDetailsPanel.add(_descLabel);
138145 _speedLabel = new JLabel("");
143150 pointDetailsPanel.add(_nameLabel);
144151 _typeLabel = new JLabel("");
145152 pointDetailsPanel.add(_typeLabel);
153 _filenameLabel = new JLabel("");
154 pointDetailsPanel.add(_filenameLabel);
146155 pointDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
147156
148157 // range details panel
295304 _latLabel.setText("");
296305 _longLabel.setText("");
297306 _altLabel.setText("");
298 _timeLabel.setText("");
307 _ptDateLabel.setText("");
308 _ptTimeLabel.setText("");
299309 _descLabel.setText("");
300310 _nameLabel.setText("");
301311 _typeLabel.setText("");
302312 _speedLabel.setText("");
303313 _vSpeedLabel.setText("");
314 _filenameLabel.setText("");
304315 }
305316 else
306317 {
315326 I18nManager.getText(altUnit.getShortnameKey()))
316327 : "");
317328 if (currentPoint.hasTimestamp()) {
318 _timeLabel.setText(LABEL_POINT_TIMESTAMP + currentPoint.getTimestamp().getText());
319 _timeLabel.setToolTipText(currentPoint.getTimestamp().getText());
329 _ptDateLabel.setText(LABEL_POINT_DATE + currentPoint.getTimestamp().getDateText());
330 _ptTimeLabel.setText(LABEL_POINT_TIME + currentPoint.getTimestamp().getTimeText());
320331 }
321332 else {
322 _timeLabel.setText("");
323 _timeLabel.setToolTipText("");
333 _ptDateLabel.setText("");
334 _ptTimeLabel.setText("");
324335 }
325336 // Maybe the point has a description?
326337 String pointDesc = currentPoint.getFieldValue(Field.DESCRIPTION);
376387 _typeLabel.setText(LABEL_POINT_WAYPOINTTYPE + type);
377388 }
378389 else _typeLabel.setText("");
390
391 // File to which point belongs
392 final int numFiles = _trackInfo.getFileInfo().getNumFiles();
393 String filename = null;
394 if (numFiles > 1)
395 {
396 final SourceInfo info = _trackInfo.getFileInfo().getSourceForPoint(currentPoint);
397 if (info != null) {
398 filename = info.getName();
399 }
400 }
401 if (filename != null) {
402 _filenameLabel.setText(LABEL_POINT_FILENAME + filename);
403 _filenameLabel.setToolTipText(filename);
404 }
405 else {
406 _filenameLabel.setText("");
407 _filenameLabel.setToolTipText("");
408 }
379409 }
380410
381411 // Update range details
446476 String shortPath = shortenPath(fullPath);
447477 _photoPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath);
448478 _photoPathLabel.setToolTipText(currentPhoto.getFullPath());
449 _photoTimestampLabel.setText(currentPhoto.hasTimestamp()?(LABEL_POINT_TIMESTAMP + currentPhoto.getTimestamp().getText()):"");
479 _photoTimestampLabel.setText(currentPhoto.hasTimestamp()?(LABEL_POINT_TIME + currentPhoto.getTimestamp().getText()):"");
450480 _photoConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": "
451481 + (currentPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED ?
452482 I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes")));
482512 String shortPath = shortenPath(fullPath);
483513 _audioPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath);
484514 _audioPathLabel.setToolTipText(fullPath == null ? "" : fullPath);
485 _audioTimestampLabel.setText(currentAudio.hasTimestamp()?(LABEL_POINT_TIMESTAMP + currentAudio.getTimestamp().getText()):"");
515 _audioTimestampLabel.setText(currentAudio.hasTimestamp()?(LABEL_POINT_TIME + currentAudio.getTimestamp().getText()):"");
486516 int audioLength = currentAudio.getLengthInSeconds();
487517 _audioLengthLabel.setText(audioLength < 0?"":LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(audioLength));
488518 _audioConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": "
531561 {
532562 final int DECIMAL_PLACES = 7;
533563 if (inCoord == null) return "";
564 String result = inCoord;
534565 final int dotPos = Math.max(inCoord.lastIndexOf('.'), inCoord.lastIndexOf(','));
535 if (dotPos >= 0) {
566 if (dotPos >= 0)
567 {
536568 final int chopPos = dotPos + DECIMAL_PLACES;
537 if (chopPos < (inCoord.length()-1)) {
538 return inCoord.substring(0, chopPos);
539 }
540 }
541 return inCoord;
569 if (chopPos < (inCoord.length()-1))
570 {
571 result = inCoord.substring(0, chopPos);
572 // Maybe there's an exponential in there too which needs to be appended
573 int expPos = inCoord.toUpperCase().indexOf("E", chopPos);
574 if (expPos > 0 && expPos < (inCoord.length()-1))
575 {
576 result += inCoord.substring(expPos);
577 }
578 }
579 }
580 return result;
542581 }
543582
544583 /**
99 {
1010
1111 /** Icon for window */
12 public static final String WINDOW_ICON = "window_icon.png";
12 public static final String WINDOW_ICON = "window_icon";
1313
1414 /** Icon for scalebar button on main map display */
1515 public static final String SCALEBAR_BUTTON = "scalebar.gif";
2020 import tim.prune.UpdateMessageBroker;
2121 import tim.prune.config.Config;
2222 import tim.prune.data.AudioClip;
23 import tim.prune.data.DataPoint;
2324 import tim.prune.data.Field;
2425 import tim.prune.data.Photo;
2526 import tim.prune.data.RecentFile;
2728 import tim.prune.data.Selection;
2829 import tim.prune.data.Track;
2930 import tim.prune.data.TrackInfo;
30 import tim.prune.function.RearrangeWaypointsFunction.Rearrange;
31 import tim.prune.function.ChooseSingleParameter;
3132 import tim.prune.function.browser.UrlGenerator;
3233
3334 /**
5960 private JMenuItem _compressItem = null;
6061 private JMenuItem _markRectangleItem = null;
6162 private JMenuItem _deleteMarkedPointsItem = null;
63 private JMenuItem _deleteByDateItem = null;
6264 private JMenuItem _interpolateItem = null;
6365 private JMenuItem _averageItem = null;
6466 private JMenuItem _selectAllItem = null;
6567 private JMenuItem _selectNoneItem = null;
68 private JMenuItem _selectSegmentItem = null;
6669 private JMenuItem _selectStartItem = null;
6770 private JMenuItem _selectEndItem = null;
6871 private JMenuItem _findWaypointItem = null;
7174 private JMenuItem _addTimeOffsetItem = null;
7275 private JMenuItem _addAltitudeOffsetItem = null;
7376 private JMenuItem _mergeSegmentsItem = null;
74 private JMenu _rearrangeMenu = null;
77 private JMenuItem _rearrangeWaypointsItem = null;
7578 private JMenuItem _splitSegmentsItem = null;
7679 private JMenuItem _sewSegmentsItem = null;
7780 private JMenuItem _cutAndMoveItem = null;
308311 });
309312 _deleteMarkedPointsItem.setEnabled(false);
310313 trackMenu.add(_deleteMarkedPointsItem);
314 _deleteByDateItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_BY_DATE, false);
315 trackMenu.add(_deleteByDateItem);
311316 trackMenu.addSeparator();
312317 // Rearrange waypoints
313 _rearrangeMenu = new JMenu(I18nManager.getText("menu.track.rearrange"));
314 _rearrangeMenu.setEnabled(false);
315 JMenuItem rearrangeStartItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.start"));
316 rearrangeStartItem.addActionListener(new ActionListener() {
317 public void actionPerformed(ActionEvent e) {
318 FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_START);
319 }
320 });
321 rearrangeStartItem.setEnabled(true);
322 _rearrangeMenu.add(rearrangeStartItem);
323 JMenuItem rearrangeEndItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.end"));
324 rearrangeEndItem.addActionListener(new ActionListener() {
325 public void actionPerformed(ActionEvent e) {
326 FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_END);
327 }
328 });
329 rearrangeEndItem.setEnabled(true);
330 _rearrangeMenu.add(rearrangeEndItem);
331 JMenuItem rearrangeNearestItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.nearest"));
332 rearrangeNearestItem.addActionListener(new ActionListener() {
333 public void actionPerformed(ActionEvent e) {
334 FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_NEAREST);
335 }
336 });
337 rearrangeNearestItem.setEnabled(true);
338 _rearrangeMenu.add(rearrangeNearestItem);
339 trackMenu.add(_rearrangeMenu);
318 _rearrangeWaypointsItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS, false);
319 trackMenu.add(_rearrangeWaypointsItem);
340320 // Split track segments
341321 _splitSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SPLIT_SEGMENTS, false);
342322 trackMenu.add(_splitSegmentsItem);
368348 }
369349 });
370350 rangeMenu.add(_selectNoneItem);
351 _selectSegmentItem = makeMenuItem(FunctionLibrary.FUNCTION_SELECT_SEGMENT);
352 rangeMenu.add(_selectSegmentItem);
371353 rangeMenu.addSeparator();
372354 _selectStartItem = new JMenuItem(I18nManager.getText("menu.range.start"));
373355 _selectStartItem.setEnabled(false);
415397 _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES, false);
416398 rangeMenu.add(_deleteFieldValuesItem);
417399 rangeMenu.addSeparator();
418 _interpolateItem = makeMenuItem(FunctionLibrary.FUNCTION_INTERPOLATE, false);
400 _interpolateItem = makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_INTERPOLATE), false);
419401 rangeMenu.add(_interpolateItem);
420402 _averageItem = new JMenuItem(I18nManager.getText("menu.range.average"));
421403 _averageItem.addActionListener(new ActionListener() {
656638 // Set colours
657639 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS));
658640 // Set line width used for drawing
659 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LINE_WIDTH));
641 settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_LINE_WIDTH)));
660642 // Set language
661643 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE));
644 // Set altitude tolerance
645 settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_ALTITUDE_TOLERANCE)));
662646 settingsMenu.addSeparator();
663647 // Save configuration
664648 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SAVECONFIG));
876860 _compressItem.setEnabled(hasData);
877861 _markRectangleItem.setEnabled(hasData);
878862 _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints());
879 _rearrangeMenu.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
863 _rearrangeWaypointsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
880864 _splitSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3);
881865 _sewSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3);
882866 _selectAllItem.setEnabled(hasData);
894878 _findWaypointItem.setEnabled(hasData && _track.hasWaypoints());
895879 // have we got a cache?
896880 _downloadSrtmItem.setEnabled(hasData && Config.getConfigString(Config.KEY_DISK_CACHE) != null);
881 // have we got any timestamps?
882 _deleteByDateItem.setEnabled(hasData && _track.hasData(Field.TIMESTAMP));
897883
898884 // is undo available?
899885 boolean hasUndo = !_app.getUndoStack().isEmpty();
901887 _undoButton.setEnabled(hasUndo);
902888 _clearUndoItem.setEnabled(hasUndo);
903889 // is there a current point?
904 boolean hasPoint = (hasData && _selection.getCurrentPointIndex() >= 0);
890 DataPoint currPoint = _app.getTrackInfo().getCurrentPoint();
891 boolean hasPoint = (currPoint != null);
905892 _editPointItem.setEnabled(hasPoint);
906893 _editPointButton.setEnabled(hasPoint);
907894 _editWaypointNameItem.setEnabled(hasPoint);
912899 _selectEndItem.setEnabled(hasPoint);
913900 _selectEndButton.setEnabled(hasPoint);
914901 _duplicatePointItem.setEnabled(hasPoint);
902 // is it a waypoint?
903 _selectSegmentItem.setEnabled(hasPoint && !currPoint.isWaypoint());
915904 // are there any photos?
916905 boolean anyPhotos = _app.getTrackInfo().getPhotoList().getNumPhotos() > 0;
917906 _saveExifItem.setEnabled(anyPhotos && _app.getTrackInfo().getPhotoList().hasMediaWithFile());
938927 _selectNoAudioItem.setEnabled(hasAudio);
939928 _removeAudioItem.setEnabled(hasAudio);
940929 _connectAudioItem.setEnabled(hasAudio && hasPoint && currentAudio.getDataPoint() == null);
941 _disconnectAudioItem.setEnabled(hasAudio && _app.getTrackInfo().getCurrentAudio().getDataPoint() != null);
930 _disconnectAudioItem.setEnabled(hasAudio && currentAudio.getDataPoint() != null);
942931 _correlateAudiosItem.setEnabled(anyAudios && hasData);
943932 // is there a current range?
944933 boolean hasRange = (hasData && _selection.hasRangeSelected());
982971 for (int i=0; i<numRecentFiles; i++)
983972 {
984973 JMenuItem item = _recentFileMenu.getItem(i);
985 item.setText(rfl.getFile(i)==null?"":rfl.getFile(i).getFile().getName());
986 item.setToolTipText(rfl.getFile(i)==null?null:rfl.getFile(i).getFile().getAbsolutePath());
974 RecentFile rf = rfl.getFile(i);
975 item.setText(rf==null?"":rf.getFile().getName());
976 item.setToolTipText(rf==null?null:rf.getFile().getAbsolutePath());
987977 }
988978 }
989979 else
1010 import javax.swing.JPanel;
1111
1212 import tim.prune.I18nManager;
13 import tim.prune.config.Config;
1314 import tim.prune.threedee.TerrainDefinition;
1415
1516 /**
4344 JLabel label = new JLabel(I18nManager.getText("dialog.3d.terraingridsize") + ": ");
4445 add(label);
4546 _gridSizeField = new WholeNumberField(4);
46 _gridSizeField.setValue(50); // default grid size
47 _gridSizeField.setValue(Config.getConfigInt(Config.KEY_TERRAIN_GRID_SIZE)); // default grid size
4748 _gridSizeField.setMaximumSize(new Dimension(100, 50));
4849 _gridSizeField.setEnabled(false);
4950 add(_gridSizeField);
00 package tim.prune.gui;
11
22 import javax.swing.JTextField;
3 import javax.swing.event.DocumentEvent;
4 import javax.swing.event.DocumentListener;
35 import javax.swing.text.AttributeSet;
46 import javax.swing.text.BadLocationException;
57 import javax.swing.text.PlainDocument;
5860 {
5961 super(inMaxDigits);
6062 setDocument(new WholeNumberDocument(inMaxDigits));
63 getDocument().addDocumentListener(new DocumentListener() {
64 public void removeUpdate(DocumentEvent arg0) {fireActionPerformed();}
65 public void insertUpdate(DocumentEvent arg0) {fireActionPerformed();}
66 public void changedUpdate(DocumentEvent arg0) {fireActionPerformed();}
67 });
6168 }
6269
6370 /**
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 import tim.prune.data.DataPoint;
5 import tim.prune.data.Track;
6 import tim.prune.data.TrackInfo;
7
8 /**
9 * Colourer based on altitude values
10 */
11 public class AltitudeColourer extends ContinuousPointColourer
12 {
13 /**
14 * Constructor
15 * @param inStartColour start colour
16 * @param inEndColour end colour
17 */
18 public AltitudeColourer(Color inStartColour, Color inEndColour)
19 {
20 super(inStartColour, inEndColour);
21 }
22
23 @Override
24 public void calculateColours(TrackInfo inTrackInfo)
25 {
26 Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
27 final int numPoints = track == null ? 0 : track.getNumPoints();
28 DataPoint point = null;
29
30 // Figure out altitude range
31 double minAltitude = 0.0;
32 double maxAltitude = 0.0;
33 boolean altFound = false;
34 for (int i=0; i<numPoints; i++)
35 {
36 point = track.getPoint(i);
37 if (point != null && point.hasAltitude())
38 {
39 double altValue = point.getAltitude().getMetricValue();
40 if (altValue < minAltitude || !altFound) {minAltitude = altValue;}
41 if (altValue > maxAltitude || !altFound) {maxAltitude = altValue;}
42 altFound = true;
43 }
44 }
45
46 if ((maxAltitude - minAltitude) < 1.0)
47 {
48 // not enough altitude range, set all to null
49 init(0);
50 }
51 else
52 {
53 // initialise the array to the right size
54 init(numPoints);
55 // loop over track points to calculate colours
56 for (int i=0; i<numPoints; i++)
57 {
58 point = track.getPoint(i);
59 if (point != null && point.hasAltitude() && !point.isWaypoint())
60 {
61 double altValue = point.getAltitude().getMetricValue();
62 double fraction = (altValue - minAltitude) / (maxAltitude - minAltitude);
63 setColour(i, mixColour((float) fraction));
64 }
65 else setColour(i, null);
66 }
67 }
68 }
69 }
0 package tim.prune.gui.colour;
1
2 import java.awt.BorderLayout;
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.FlowLayout;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8
9 import javax.swing.BoxLayout;
10 import javax.swing.JButton;
11 import javax.swing.JDialog;
12 import javax.swing.JLabel;
13 import javax.swing.JPanel;
14 import javax.swing.JSlider;
15 import javax.swing.event.ChangeEvent;
16 import javax.swing.event.ChangeListener;
17
18 import tim.prune.I18nManager;
19
20 /**
21 * Class to offer a dialog to choose a colour.
22 * Normally a JColorChooser would be used, but this is too buggy
23 * in Java 1.6 and extremely prone to thread-locking, meaning
24 * that the application has to be killed (and all data lost).
25 */
26 public class ColourChooser
27 {
28 /** main dialog object */
29 private JDialog _dialog = null;
30 /** array of three slider objects for rgb */
31 private JSlider[] _rgbSliders = null;
32 /** array of labels for rgb values */
33 private JLabel[] _rgbLabels = null;
34 /** colour patch */
35 private ColourPatch _patch = null;
36 /** chosen colour */
37 private Color _chosenColour = null;
38
39
40 /**
41 * Constructor
42 * @param inParent parent dialog
43 */
44 public ColourChooser(JDialog inParent)
45 {
46 _dialog = new JDialog(inParent, I18nManager.getText("dialog.colourchooser.title"), true);
47 _dialog.setLocationRelativeTo(inParent);
48 _dialog.getContentPane().add(makeContents());
49 _dialog.pack();
50 }
51
52 /**
53 * Make the dialog contents
54 * @return JPanel containing dialog elements
55 */
56 private JPanel makeContents()
57 {
58 JPanel mainPanel = new JPanel();
59 mainPanel.setLayout(new BorderLayout());
60 _rgbSliders = new JSlider[3];
61 _rgbLabels = new JLabel[3];
62 _patch = new ColourPatch(Color.WHITE);
63 JPanel centrePanel = new JPanel();
64 centrePanel.setLayout(new BorderLayout());
65 centrePanel.add(_patch, BorderLayout.CENTER);
66
67 JPanel sliderPanel = new JPanel();
68 sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.Y_AXIS));
69 final String[] labelKeys = {"red", "green", "blue"};
70 for (int i=0; i<3; i++)
71 {
72 String key = I18nManager.getText("dialog.colourchooser." + labelKeys[i]);
73 sliderPanel.add(new JLabel(key));
74 _rgbSliders[i] = new JSlider(0, 255);
75 _rgbSliders[i].addChangeListener(new ChangeListener() {
76 public void stateChanged(ChangeEvent arg0) {
77 updatePatch();
78 }
79 });
80 _rgbSliders[i].setToolTipText(key);
81 JPanel sliderHolder = new JPanel();
82 sliderHolder.setLayout(new BorderLayout(5, 0));
83 sliderHolder.add(_rgbSliders[i], BorderLayout.CENTER);
84 _rgbLabels[i] = new JLabel("255");
85 _rgbLabels[i].setPreferredSize(new Dimension(40, 1));
86 sliderHolder.add(_rgbLabels[i], BorderLayout.EAST);
87 sliderPanel.add(sliderHolder);
88 }
89 centrePanel.add(sliderPanel, BorderLayout.SOUTH);
90 mainPanel.add(centrePanel, BorderLayout.CENTER);
91
92 // Button panel for ok, cancel
93 JPanel buttonPanel = new JPanel();
94 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
95 JButton okButton = new JButton(I18nManager.getText("button.ok"));
96 okButton.addActionListener(new ActionListener() {
97 public void actionPerformed(ActionEvent e) {
98 _chosenColour = _patch.getBackground();
99 _dialog.setVisible(false);
100 }
101 });
102 buttonPanel.add(okButton);
103 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
104 cancelButton.addActionListener(new ActionListener() {
105 public void actionPerformed(ActionEvent e) {
106 _chosenColour = null;
107 _dialog.setVisible(false);
108 }
109 });
110 buttonPanel.add(cancelButton);
111 mainPanel.add(buttonPanel, BorderLayout.SOUTH);
112 return mainPanel;
113 }
114
115 /**
116 * Show the dialog to choose a colour
117 * @param inStartColour current colour
118 */
119 public void showDialog(Color inStartColour)
120 {
121 // Initialise sliders
122 _rgbSliders[0].setValue(inStartColour.getRed());
123 _rgbSliders[1].setValue(inStartColour.getGreen());
124 _rgbSliders[2].setValue(inStartColour.getBlue());
125 updatePatch();
126 _dialog.setLocationRelativeTo(_dialog.getParent());
127 _dialog.setVisible(true);
128 }
129
130 /**
131 * Update the patch colour from the slider values
132 */
133 private void updatePatch()
134 {
135 for (int i=0; i<3; i++) {
136 _rgbLabels[i].setText("" + _rgbSliders[i].getValue());
137 }
138 final Color colour = new Color(_rgbSliders[0].getValue(),
139 _rgbSliders[1].getValue(),_rgbSliders[2].getValue());
140 _patch.setColour(colour);
141 }
142
143 /**
144 * @return the selected colour, or null if cancel pressed
145 */
146 public Color getChosenColour()
147 {
148 return _chosenColour;
149 }
150 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3 import java.awt.Dimension;
4 import javax.swing.JPanel;
5
6 import tim.prune.config.ColourUtils;
7
8 /**
9 * Class to act as a colour patch to illustrate a chosen colour
10 */
11 public class ColourPatch extends JPanel
12 {
13 /**
14 * Constructor
15 * @param inColour starting colour
16 */
17 public ColourPatch(Color inColour)
18 {
19 Dimension size = new Dimension(80, 50);
20 setMinimumSize(size);
21 setPreferredSize(size);
22 setColour(inColour);
23 }
24
25 /**
26 * Set the colour of the patch
27 * @param inColour Color to use
28 */
29 public void setColour(Color inColour)
30 {
31 super.setBackground(inColour);
32 setToolTipText(ColourUtils.makeHexCode(inColour));
33 }
34 }
0 package tim.prune.gui.colour;
1
2 import tim.prune.App;
3 import tim.prune.DataSubscriber;
4
5 /**
6 * Caretaker of the current PointColourer, responsible for listening
7 * to data changes and updating the colourer
8 */
9 public class ColourerCaretaker implements DataSubscriber
10 {
11 /** App object for getting the track */
12 private App _app = null;
13 /** PointColourer object for passing details to */
14 private PointColourer _colourer = null;
15
16 /**
17 * Constructor
18 * @param inApp app object to use
19 */
20 public ColourerCaretaker(App inApp)
21 {
22 _app = inApp;
23 }
24
25 /**
26 * @param inColourer current colourer object
27 */
28 public void setColourer(PointColourer inColourer)
29 {
30 _colourer = inColourer;
31 dataUpdated(ALL);
32 }
33
34 /**
35 * @return point colourer, or null
36 */
37 public PointColourer getColourer()
38 {
39 return _colourer;
40 }
41
42 /**
43 * Data has been updated
44 */
45 public void dataUpdated(byte inUpdateType)
46 {
47 if ((inUpdateType &
48 (DataSubscriber.DATA_ADDED_OR_REMOVED | DataSubscriber.DATA_EDITED)) > 0
49 && _colourer != null)
50 {
51 _colourer.calculateColours(_app.getTrackInfo());
52 }
53 }
54
55 /** Don't care about status */
56 public void actionCompleted(String inMessage) {}
57 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 import tim.prune.config.ColourUtils;
5
6 /**
7 * Factory for the creation of PointColourer objects
8 */
9 public abstract class ColourerFactory
10 {
11 /** Enumeration of colourer types */
12 public enum ColourerId
13 {
14 NONE,
15 BY_FILE,
16 BY_SEGMENT,
17 BY_ALTITUDE,
18 BY_SPEED,
19 BY_VSPEED,
20 BY_GRADIENT,
21 BY_DATE
22 }
23
24 /**
25 * Does the specified colourer need a field for maximum number of colours?
26 * @param inId id of colourer
27 * @return true if max colours required, false otherwise
28 */
29 public static boolean isMaxColoursRequired(ColourerId inId)
30 {
31 switch (inId)
32 {
33 case NONE: return false;
34 case BY_FILE: return FileColourer.isMaxColoursRequired();
35 case BY_SEGMENT: return SegmentColourer.isMaxColoursRequired();
36 case BY_ALTITUDE: return AltitudeColourer.isMaxColoursRequired();
37 case BY_SPEED: return SpeedColourer.isMaxColoursRequired();
38 case BY_VSPEED: return VertSpeedColourer.isMaxColoursRequired();
39 case BY_GRADIENT: return GradientColourer.isMaxColoursRequired();
40 case BY_DATE: return DateColourer.isMaxColoursRequired();
41 }
42 return false;
43 }
44
45 /**
46 * Does the specified colourer need fields for start and end colours?
47 * @param inId id of colourer
48 * @return true if colours required, false otherwise
49 */
50 public static boolean areColoursRequired(ColourerId inId)
51 {
52 // all of them except NONE need start and end colours
53 return inId != ColourerId.NONE;
54 }
55
56 /**
57 * @param inDesc Single character used as a code (in Config string)
58 * @return associated ColourerId
59 */
60 private static ColourerId getColourerId(char inDesc)
61 {
62 switch (inDesc)
63 {
64 case 'f': return ColourerId.BY_FILE;
65 case 's': return ColourerId.BY_SEGMENT;
66 case 'a': return ColourerId.BY_ALTITUDE;
67 case 'p': return ColourerId.BY_SPEED;
68 case 'v': return ColourerId.BY_VSPEED;
69 case 'g': return ColourerId.BY_GRADIENT;
70 case 'd': return ColourerId.BY_DATE;
71 }
72 return ColourerId.NONE;
73 }
74
75 /**
76 * Create a new PointColourer object given the parameters
77 * @param inId id of colourer to create
78 * @param inStartColour start colour
79 * @param inEndColour end colour
80 * @param inMaxColours maximum number of colours
81 * @return PointColourer object, or null
82 */
83 public static PointColourer createColourer(ColourerId inId, Color inStartColour, Color inEndColour, String inMaxColours)
84 {
85 try
86 {
87 switch (inId)
88 {
89 case NONE: return null;
90 case BY_FILE: return new FileColourer(inStartColour, inEndColour, Integer.parseInt(inMaxColours));
91 case BY_SEGMENT: return new SegmentColourer(inStartColour, inEndColour, Integer.parseInt(inMaxColours));
92 case BY_ALTITUDE: return new AltitudeColourer(inStartColour, inEndColour);
93 case BY_SPEED: return new SpeedColourer(inStartColour, inEndColour);
94 case BY_VSPEED: return new VertSpeedColourer(inStartColour, inEndColour);
95 case BY_GRADIENT: return new GradientColourer(inStartColour, inEndColour);
96 case BY_DATE: return new DateColourer(inStartColour, inEndColour, Integer.parseInt(inMaxColours));
97 }
98 }
99 catch (NumberFormatException nfe) {} // drop out to return null
100 return null;
101 }
102
103 /**
104 * Create a PointColourer object from the given description
105 * @param inString string from config
106 * @return PointColourer object, or null if string was invalid
107 */
108 public static PointColourer createColourer(String inString)
109 {
110 try
111 {
112 String[] comps = inString.split(";");
113 if (comps.length == 4)
114 {
115 ColourerId colourerType = getColourerId(comps[0].charAt(0));
116 Color startColour = ColourUtils.colourFromHex(comps[1]);
117 Color endColour = ColourUtils.colourFromHex(comps[2]);
118 String maxColours = comps[3];
119 return createColourer(colourerType, startColour, endColour, maxColours);
120 }
121 }
122 catch (NullPointerException npe) {}
123 catch (NumberFormatException nfe) {}
124 return null;
125 }
126
127 /**
128 * Convert the given PointColourer object into a string for the config
129 * @param inColourer PointColourer object
130 * @return string describing object (for later re-creation) or null
131 */
132 public static String PointColourerToString(PointColourer inColourer)
133 {
134 if (inColourer != null)
135 {
136 final String startColour = ColourUtils.makeHexCode(inColourer.getStartColour());
137 final String endColour = ColourUtils.makeHexCode(inColourer.getEndColour());
138 final int maxColours = inColourer.getMaxColours();
139 if (inColourer instanceof FileColourer) {
140 return "f;" + startColour + ";" + endColour + ";" + maxColours;
141 }
142 else if (inColourer instanceof SegmentColourer) {
143 return "s;" + startColour + ";" + endColour + ";" + maxColours;
144 }
145 else if (inColourer instanceof AltitudeColourer) {
146 return "a;" + startColour + ";" + endColour + ";0";
147 }
148 else if (inColourer instanceof SpeedColourer) {
149 return "p;" + startColour + ";" + endColour + ";0";
150 }
151 else if (inColourer instanceof VertSpeedColourer) {
152 return "v;" + startColour + ";" + endColour + ";0";
153 }
154 else if (inColourer instanceof GradientColourer) {
155 return "g;" + startColour + ";" + endColour + ";0";
156 }
157 else if (inColourer instanceof DateColourer) {
158 return "d;" + startColour + ";" + endColour + ";" + maxColours;
159 }
160 }
161 return null;
162 }
163
164 /**
165 * Get the colourer-specific end of the description key for translation
166 * @param inId id of colourer
167 * @return end of description key for combobox text
168 */
169 public static String getDescriptionKey(ColourerId inId)
170 {
171 switch (inId)
172 {
173 case NONE: return "none";
174 case BY_FILE: return "byfile";
175 case BY_SEGMENT: return "bysegment";
176 case BY_ALTITUDE: return "byaltitude";
177 case BY_SPEED: return "byspeed";
178 case BY_VSPEED: return "byvertspeed";
179 case BY_GRADIENT: return "bygradient";
180 case BY_DATE: return "bydate";
181 }
182 return null;
183 }
184
185 /**
186 * Get the id of the given colourer, according to its class
187 * @param inColourer point colourer object, or null
188 * @return id, for example for selection in dropdown
189 */
190 public static ColourerId getId(PointColourer inColourer)
191 {
192 if (inColourer != null)
193 {
194 if (inColourer instanceof FileColourer) {return ColourerId.BY_FILE;}
195 if (inColourer instanceof SegmentColourer) {return ColourerId.BY_SEGMENT;}
196 if (inColourer instanceof AltitudeColourer) {return ColourerId.BY_ALTITUDE;}
197 if (inColourer instanceof SpeedColourer) {return ColourerId.BY_SPEED;}
198 if (inColourer instanceof VertSpeedColourer) {return ColourerId.BY_VSPEED;}
199 if (inColourer instanceof GradientColourer) {return ColourerId.BY_GRADIENT;}
200 if (inColourer instanceof DateColourer) {return ColourerId.BY_DATE;}
201 }
202 return ColourerId.NONE;
203 }
204 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3 import java.awt.Component;
4 import java.awt.GridLayout;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7
8 import javax.swing.BorderFactory;
9 import javax.swing.BoxLayout;
10 import javax.swing.JComboBox;
11 import javax.swing.JLabel;
12 import javax.swing.JPanel;
13 import javax.swing.border.EtchedBorder;
14
15 import tim.prune.I18nManager;
16 import tim.prune.gui.GuiGridLayout;
17 import tim.prune.gui.colour.ColourerFactory.ColourerId;
18
19 /**
20 * Class to provide a gui panel for selecting a colourer
21 * including which colourer, the start/end colours and
22 * optionally the max number of colours
23 */
24 public class ColourerSelectorPanel extends JPanel
25 {
26 /** Combo box for selecting the type of colourer */
27 private JComboBox<String> _typeCombo = null;
28 /** Array of type ids as stored in combo box */
29 private ColourerId[] _typeIds = null;
30 /** Panel object holding the colour patches */
31 private JPanel _patchPanel = null;
32 /** Array of colour patches for start and end */
33 private ColourPatch[] _startEndPatches = null;
34 /** Panel holding the max colours selection */
35 JPanel _maxColoursPanel = null;
36 private JComboBox<String> _maxColoursCombo = null;
37
38 /** Array of label keys for the 2 patches */
39 private static final String[] LABEL_KEYS = {"start", "end"};
40
41
42 /**
43 * Constructor
44 * @param inColourChooser colour chooser to use (needs reference to parent dialog)
45 */
46 public ColourerSelectorPanel(ColourChooser inColourChooser)
47 {
48 _typeIds = new ColourerId[] {ColourerId.NONE, ColourerId.BY_FILE,
49 ColourerId.BY_SEGMENT, ColourerId.BY_DATE, ColourerId.BY_ALTITUDE,
50 ColourerId.BY_SPEED, ColourerId.BY_VSPEED, ColourerId.BY_GRADIENT};
51 makeGuiComponents(inColourChooser);
52 }
53
54
55 /**
56 * Create all the gui components and lay them out in the panel
57 * @param inColourChooser colour chooser to use
58 */
59 private void makeGuiComponents(ColourChooser inColourChooser)
60 {
61 // Etched border and vertical layout
62 setBorder(BorderFactory.createCompoundBorder(
63 BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
64 );
65 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
66
67 // Label at the top
68 JLabel introLabel = new JLabel(I18nManager.getText("dialog.colourer.intro"));
69 introLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
70 add(introLabel);
71
72 // Combo box for selecting which colourer to use
73 JPanel typePanel = new JPanel();
74 GuiGridLayout grid = new GuiGridLayout(typePanel);
75 final String keyPrefix = "dialog.colourer.type.";
76 String[] colourerTypes = new String[_typeIds.length];
77 for (int i=0; i<colourerTypes.length; i++)
78 {
79 colourerTypes[i] = I18nManager.getText(keyPrefix +
80 ColourerFactory.getDescriptionKey(_typeIds[i]));
81 }
82 _typeCombo = new JComboBox<String>(colourerTypes);
83 _typeCombo.setAlignmentX(Component.LEFT_ALIGNMENT);
84 _typeCombo.addActionListener(new ActionListener() {
85 public void actionPerformed(ActionEvent e)
86 {
87 onColourerTypeChanged();
88 }
89 });
90 // Add to the panel
91 grid.add(new JLabel(I18nManager.getText("dialog.colourer.type")));
92 grid.add(_typeCombo);
93 typePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
94 add(typePanel);
95
96 // Make panel for colour patches
97 _patchPanel = new JPanel();
98 _patchPanel.setLayout(new GridLayout());
99 _startEndPatches = new ColourPatch[2];
100
101 // Blank column
102 JPanel blankColumn = new JPanel();
103 ColourPatch blankPatch = new ColourPatch(Color.BLACK);
104 blankPatch.setVisible(false);
105 blankColumn.add(blankPatch);
106 _patchPanel.add(blankColumn);
107
108 // Loop over two columns of patches
109 for (int i=0; i<2; i++)
110 {
111 JPanel colPanel = new JPanel();
112 colPanel.setLayout(new BoxLayout(colPanel, BoxLayout.Y_AXIS));
113 // Top label and patch
114 colPanel.add(new JLabel(I18nManager.getText("dialog.colourer." + LABEL_KEYS[i])));
115 ColourPatch patch = new ColourPatch(Color.BLUE); // will be set by init() method shortly
116 patch.addMouseListener(new PatchListener(patch, inColourChooser));
117 colPanel.add(patch);
118 _startEndPatches[i] = patch;
119
120 // Add column to panel
121 colPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
122 _patchPanel.add(colPanel);
123 }
124
125 // Blank column
126 blankColumn = new JPanel();
127 blankPatch = new ColourPatch(Color.BLACK);
128 blankPatch.setVisible(false);
129 blankColumn.add(blankPatch);
130 _patchPanel.add(blankColumn);
131
132 _patchPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
133 add(_patchPanel);
134
135 // Combo box for selecting max colours
136 _maxColoursPanel = new JPanel();
137 grid = new GuiGridLayout(_maxColoursPanel);
138 grid.add(new JLabel(I18nManager.getText("dialog.colourer.maxcolours")));
139 String[] colourOptions = new String[] {"2", "3", "5", "10", "15"};
140 _maxColoursCombo = new JComboBox<String>(colourOptions);
141 grid.add(_maxColoursCombo);
142 _maxColoursPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
143 add(_maxColoursPanel);
144 }
145
146 /**
147 * Init the colours from the colourer (if possible) or the default colour
148 * @param inColourer current colourer object, or null
149 * @param inDefaultColour current colour for points
150 */
151 public void init(PointColourer inColourer, Color inDefaultColour)
152 {
153 Color startColour = null, endColour = null;
154 if (inColourer != null)
155 {
156 selectColourerType(ColourerFactory.getId(inColourer));
157 startColour = inColourer.getStartColour();
158 endColour = inColourer.getEndColour();
159 _maxColoursCombo.setSelectedItem("" + inColourer.getMaxColours());
160 }
161 else
162 {
163 // no colourer, so default to 5 colours maximum
164 _maxColoursCombo.setSelectedIndex(2);
165 }
166 if (startColour == null) {startColour = inDefaultColour;}
167 if (endColour == null) {endColour = makeDefaultEndColour(inDefaultColour);}
168 if (startColour != null) {_startEndPatches[0].setBackground(startColour);}
169 if (endColour != null) {_startEndPatches[1].setBackground(endColour);}
170 onColourerTypeChanged(); // make sure gui is updated
171 }
172
173 /**
174 * Make a default end colour if there isn't one already defined
175 * @param inStartColour start colour
176 * @return end colour, with the hue shifted by a third from the start
177 */
178 private static Color makeDefaultEndColour(Color inStartColour)
179 {
180 float[] defaultHSB = Color.RGBtoHSB(inStartColour.getRed(), inStartColour.getGreen(), inStartColour.getBlue(), null);
181 // add 120 degrees to the hue
182 defaultHSB[0] += (1.0f/3f);
183 return Color.getHSBColor(defaultHSB[0], defaultHSB[1], defaultHSB[2]);
184 }
185
186 /**
187 * React to the colourer type being changed
188 * by showing / hiding gui elements
189 */
190 private void onColourerTypeChanged()
191 {
192 final ColourerId id = _typeIds[_typeCombo.getSelectedIndex()];
193 // Set visibility of controls according to whether they're needed for the selected type
194 _patchPanel.setVisible(ColourerFactory.areColoursRequired(id));
195 _maxColoursPanel.setVisible(ColourerFactory.isMaxColoursRequired(id));
196 }
197
198 /**
199 * @return the selected colourer object, or null
200 */
201 public PointColourer getSelectedColourer()
202 {
203 final ColourerId id = _typeIds[_typeCombo.getSelectedIndex()];
204 return ColourerFactory.createColourer(id, _startEndPatches[0].getBackground(),
205 _startEndPatches[1].getBackground(), _maxColoursCombo.getSelectedItem().toString());
206 }
207
208 /**
209 * Select the appropriate item in the dropdown
210 * @param inId id of colourer to choose
211 */
212 private void selectColourerType(ColourerId inId)
213 {
214 int selIndex = -1;
215 for (int i=0; i<_typeIds.length; i++)
216 {
217 if (_typeIds[i] == inId) {
218 selIndex = i;
219 break;
220 }
221 }
222 if (selIndex < 0) {
223 System.err.println("Id " + inId + " not found in _typeIds!");
224 }
225 else {
226 _typeCombo.setSelectedIndex(selIndex);
227 }
228 }
229 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 public abstract class ContinuousPointColourer extends PointColourer
5 {
6 /** array of colours to use */
7 private Color[] _colours = null;
8
9 /**
10 * Constructor
11 * @param inStartColour start colour
12 * @param inEndColour end colour
13 */
14 public ContinuousPointColourer(Color inStartColour, Color inEndColour)
15 {
16 super(inStartColour, inEndColour);
17 }
18
19 /** Continuous colourers don't need a maximum count */
20 public static boolean isMaxColoursRequired() {
21 return false;
22 }
23
24 /**
25 * Initialise the array to the right size
26 * @param inNumPoints number of points in the track
27 */
28 protected void init(int inNumPoints)
29 {
30 if (_colours == null || _colours.length != inNumPoints)
31 {
32 // Array needs to be created or resized
33 if (inNumPoints > 0) {
34 _colours = new Color[inNumPoints];
35 }
36 else {
37 _colours = null;
38 }
39 }
40 }
41
42 /**
43 * Set the colour at the given index
44 * @param inPointIndex point index
45 * @param inColour colour to use, or null
46 */
47 protected void setColour(int inPointIndex, Color inColour)
48 {
49 if (_colours != null && _colours.length > inPointIndex && inPointIndex >= 0)
50 {
51 _colours[inPointIndex] = inColour;
52 }
53 }
54
55 /**
56 * Get the colour for the given point index
57 * @param inPointIndex index of point in track
58 * @return colour object
59 */
60 public Color getColour(int inPointIndex)
61 {
62 Color colour = null;
63 if (_colours != null && _colours.length > inPointIndex && inPointIndex >= 0)
64 {
65 colour = _colours[inPointIndex];
66 }
67 if (colour == null) {
68 // not found, use default
69 colour = super.getDefaultColour();
70 }
71 return colour;
72 }
73 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3 import java.text.DateFormat;
4 import java.util.Calendar;
5 import java.util.HashMap;
6 import java.util.TimeZone;
7
8 import tim.prune.data.DataPoint;
9 import tim.prune.data.Timestamp;
10 import tim.prune.data.Track;
11 import tim.prune.data.TrackInfo;
12
13 /**
14 * Point colourer giving a different colour to each date
15 * Uses the system timezone so may give funny results for
16 * data from other timezones (eg far-away holidays)
17 */
18 public class DateColourer extends DiscretePointColourer
19 {
20 // Doesn't really matter what format is used here, as long as dates are different
21 private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance();
22
23 /**
24 * Constructor
25 * @param inStartColour start colour of scale
26 * @param inEndColour end colour of scale
27 * @param inWrapLength number of unique colours before wrap
28 */
29 public DateColourer(Color inStartColour, Color inEndColour, int inWrapLength)
30 {
31 super(inStartColour, inEndColour, inWrapLength);
32 }
33
34 /**
35 * Calculate the colours for each of the points in the given track
36 * @param inTrackInfo track info object
37 */
38 @Override
39 public void calculateColours(TrackInfo inTrackInfo)
40 {
41 // initialise the array to the right size
42 Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
43 final int numPoints = track == null ? 0 : track.getNumPoints();
44 init(numPoints);
45 // Make a hashmap of the already-used dates
46 HashMap<String, Integer> usedDates = new HashMap<String, Integer>(20);
47 // Also store the previous one, because they're probably consecutive
48 String prevDate = null;
49 int prevIndex = -1;
50
51 // loop over track points
52 int dayIndex = -1;
53 for (int i=0; i<numPoints; i++)
54 {
55 DataPoint p = track.getPoint(i);
56 if (p != null && !p.isWaypoint())
57 {
58 dayIndex = 0; // default index 0 will be used if no date found
59 String date = getDate(p.getTimestamp());
60 if (date != null)
61 {
62 // Check if it's the previous one
63 if (prevDate != null && date.equals(prevDate)) {
64 dayIndex = prevIndex;
65 }
66 else
67 {
68 // Look up in the hashmap to see if it's been used before
69 Integer foundIndex = usedDates.get(date);
70 if (foundIndex == null)
71 {
72 // not been used before, so add it
73 dayIndex = usedDates.size() + 1;
74 usedDates.put(date, dayIndex);
75 }
76 else
77 {
78 // found it
79 dayIndex = foundIndex;
80 }
81 // Remember what we've got for the next point
82 prevDate = date;
83 prevIndex = dayIndex;
84 }
85 }
86 // if date is null (no timestamp or invalid) then dayIndex remains 0
87 setColour(i, dayIndex);
88 }
89 }
90 // generate the colours needed
91 generateDiscreteColours(usedDates.size() + 1);
92 }
93
94
95 /**
96 * Find which date (in the system timezone) the given timestamp falls on
97 * @param inTimestamp timestamp
98 * @return String containing description of date, or null
99 */
100 private static String getDate(Timestamp inTimestamp)
101 {
102 if (inTimestamp == null || !inTimestamp.isValid()) {
103 return null;
104 }
105 Calendar cal = inTimestamp.getCalendar();
106 // use system time zone
107 cal.setTimeZone(TimeZone.getDefault());
108 return DEFAULT_DATE_FORMAT.format(cal.getTime());
109 }
110 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 /**
5 * Abstract class to do the discrete colouring of points,
6 * using start and end colours and a wrapping index
7 */
8 public abstract class DiscretePointColourer extends PointColourer
9 {
10 /** array of discrete colours to use */
11 private Color[] _discreteColours = null;
12 /** array of colour indexes */
13 private int[] _colourIndexes = null;
14
15
16 /**
17 * Constructor
18 * @param inStartColour start colour of scale
19 * @param inEndColour end colour of scale
20 * @param inMaxColours number of unique colours before wrap
21 */
22 public DiscretePointColourer(Color inStartColour, Color inEndColour, int inMaxColours)
23 {
24 super(inStartColour, inEndColour, inMaxColours);
25 }
26
27 /** max number of colours is required here */
28 public static boolean isMaxColoursRequired() {
29 return true;
30 }
31
32 /**
33 * Initialise the array to the right size
34 * @param inNumPoints number of points in the track
35 */
36 protected void init(int inNumPoints)
37 {
38 if (_colourIndexes == null || _colourIndexes.length != inNumPoints)
39 {
40 // Array needs to be created or resized
41 if (inNumPoints > 0) {
42 _colourIndexes = new int[inNumPoints];
43 }
44 else {
45 _colourIndexes = null;
46 }
47 }
48 }
49
50 /**
51 * Set the colour at the given index
52 * @param inPointIndex point index
53 * @param inColourIndex index of colour to use
54 */
55 protected void setColour(int inPointIndex, int inColourIndex)
56 {
57 if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0)
58 {
59 _colourIndexes[inPointIndex] = inColourIndex;
60 }
61 }
62
63 /**
64 * Get the colour for the given point index
65 * @param inPointIndex index of point in track
66 * @return colour object
67 */
68 public Color getColour(int inPointIndex)
69 {
70 if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0 && getMaxColours() > 0)
71 {
72 int colourIndex = _colourIndexes[inPointIndex] % getMaxColours();
73 if (colourIndex >= 0 && _discreteColours != null && colourIndex < _discreteColours.length) {
74 return _discreteColours[colourIndex];
75 }
76 }
77 // not found, use default
78 return super.getDefaultColour();
79 }
80
81 /**
82 * Generate the set of discrete colours to use
83 * @param inNumCategories number of different categories found in the data
84 */
85 protected void generateDiscreteColours(int inNumCategories)
86 {
87 int maxColours = getMaxColours();
88 if (maxColours <= 1) {maxColours = 2;}
89 if (inNumCategories < 1) {inNumCategories = 1;}
90 else if (inNumCategories > maxColours) {inNumCategories = maxColours;}
91
92 // Use this number of categories to generate the colours
93 _discreteColours = new Color[inNumCategories];
94 for (int i=0; i<inNumCategories; i++) {
95 _discreteColours[i] = mixColour(i, inNumCategories);
96 }
97 }
98
99 /**
100 * Mix the given colours together by interpolating H,S,B values
101 * @param inIndex index from 0 to inWrap-1
102 * @param inWrap wrap length
103 * @return mixed colour
104 */
105 private Color mixColour(int inIndex, int inWrap)
106 {
107 float fraction = inWrap < 2 ? 0.0f : (float) inIndex / (float) (inWrap - 1);
108 return mixColour(fraction);
109 }
110
111 /**
112 * @param inIndex specified colour index
113 * @return precalculated colour at the given index
114 */
115 protected Color getDiscreteColour(int inIndex)
116 {
117 if (_discreteColours == null || inIndex < 0 || getMaxColours() <= 1) {
118 return getDefaultColour();
119 }
120 return _discreteColours[inIndex % getMaxColours()];
121 }
122
123 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3 import java.util.ArrayList;
4
5 import tim.prune.data.DataPoint;
6 import tim.prune.data.FileInfo;
7 import tim.prune.data.SourceInfo;
8 import tim.prune.data.TrackInfo;
9
10 /**
11 * Colours points according to which file (or source) they came from
12 */
13 public class FileColourer extends DiscretePointColourer
14 {
15 /**
16 * Constructor
17 * @param inStartColour start colour of scale
18 * @param inEndColour end colour of scale
19 * @param inWrapLength number of unique colours before wrap
20 */
21 public FileColourer(Color inStartColour, Color inEndColour, int inWrapLength)
22 {
23 super(inStartColour, inEndColour, inWrapLength);
24 }
25
26 /**
27 * Calculate the colours for each of the points in the given track
28 * @param inTrack track object
29 */
30 @Override
31 public void calculateColours(TrackInfo inTrackInfo)
32 {
33 // initialise the array to the right size
34 final int numPoints = inTrackInfo == null ? 0 : inTrackInfo.getTrack().getNumPoints();
35 init(numPoints);
36
37 // loop over track points
38 FileInfo fInfo = inTrackInfo.getFileInfo();
39 ArrayList<SourceInfo> sourceList = new ArrayList<SourceInfo>();
40 for (int i=0; i<numPoints; i++)
41 {
42 DataPoint p = inTrackInfo.getTrack().getPoint(i);
43 if (p != null && !p.isWaypoint())
44 {
45 SourceInfo sInfo = fInfo.getSourceForPoint(p);
46 // Is this info object already in the list?
47 int foundIndex = -1;
48 int sIndex = 0;
49 for (SourceInfo si : sourceList) {
50 if (si == sInfo) {
51 foundIndex = sIndex;
52 break;
53 }
54 sIndex++;
55 }
56 // Add source info to list
57 if (foundIndex < 0)
58 {
59 sourceList.add(sInfo);
60 foundIndex = sourceList.size()-1;
61 }
62 // use this foundIndex to find the colour
63 setColour(i, foundIndex);
64 }
65 }
66 // generate the colours needed
67 generateDiscreteColours(sourceList.size());
68 }
69 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 import tim.prune.data.Track;
5 import tim.prune.data.TrackInfo;
6 import tim.prune.gui.profile.GradientData;
7
8 /**
9 * Colourer based on gradient or glide slope values
10 */
11 public class GradientColourer extends ProfileDataColourer
12 {
13 /**
14 * Constructor
15 * @param inStartColour start colour
16 * @param inEndColour end colour
17 */
18 public GradientColourer(Color inStartColour, Color inEndColour)
19 {
20 super(inStartColour, inEndColour);
21 }
22
23 @Override
24 public void calculateColours(TrackInfo inTrackInfo)
25 {
26 Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
27 // Calculate gradient value for each point
28 GradientData data = new GradientData(track);
29 calculateColours(track, data);
30 }
31 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3 import java.awt.event.MouseAdapter;
4 import java.awt.event.MouseEvent;
5
6
7 /**
8 * Listener class to react to patch clicks
9 */
10 public class PatchListener extends MouseAdapter
11 {
12 /** Associated patch */
13 private ColourPatch _patch = null;
14 /** Colour chooser object, shared between listeners */
15 private ColourChooser _colourChooser = null;
16
17 /**
18 * Constructor
19 * @param inPatch patch object to listen to
20 * @param inChooser colour chooser to use for selection
21 */
22 public PatchListener(ColourPatch inPatch, ColourChooser inChooser)
23 {
24 _patch = inPatch;
25 _colourChooser = inChooser;
26 }
27
28 /** React to mouse clicks */
29 public void mouseClicked(MouseEvent e)
30 {
31 _colourChooser.showDialog(_patch.getBackground());
32 Color colour = _colourChooser.getChosenColour();
33 if (colour != null) _patch.setColour(colour);
34 }
35 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 import tim.prune.data.TrackInfo;
5
6 /**
7 * Abstract class to do the colouring of points,
8 * that is holding a colour for each track point
9 * in the current track
10 */
11 public abstract class PointColourer
12 {
13 /** default colour */
14 private Color _defaultColour = Color.BLUE;
15 /** start and end colours */
16 private Color _startColour = null, _endColour = null;
17 /** max number of unique colours before wrapping */
18 private int _maxColours = 1;
19
20
21 /**
22 * Constructor
23 * @param inStartColour start colour
24 * @param inEndColour end colour
25 * @param inMaxColours max number of colours
26 */
27 public PointColourer(Color inStartColour, Color inEndColour, int inMaxColours)
28 {
29 _startColour = inStartColour;
30 _endColour = inEndColour;
31 _maxColours = inMaxColours;
32 }
33
34 /**
35 * Constructor
36 * @param inStartColour start colour
37 * @param inEndColour end colour
38 */
39 public PointColourer(Color inStartColour, Color inEndColour)
40 {
41 this(inStartColour, inEndColour, -1);
42 }
43
44 /**
45 * Calculate the colours for each of the points in the given track
46 * @param inTrackInfo track info object
47 */
48 public abstract void calculateColours(TrackInfo inTrackInfo);
49
50 /**
51 * Get the colour for the given point index
52 * @param inPointIndex index of point in track
53 * @return colour object
54 */
55 public Color getColour(int inPointIndex)
56 {
57 return _defaultColour;
58 }
59
60 /**
61 * @param inColor default colour to use
62 */
63 protected void setDefaultColour(Color inColour)
64 {
65 if (inColour != null) {
66 _defaultColour = inColour;
67 }
68 }
69
70 /**
71 * @return default colour
72 */
73 protected Color getDefaultColour() {
74 return _defaultColour;
75 }
76
77 /**
78 * @return start colour
79 */
80 protected Color getStartColour() {
81 return _startColour;
82 }
83
84 /**
85 * @return end colour
86 */
87 protected Color getEndColour() {
88 return _endColour;
89 }
90
91 /**
92 * @return maximum number of colours, or -1
93 */
94 protected int getMaxColours() {
95 return _maxColours;
96 }
97
98 /**
99 * Mix the given colours together using HSB values instead of interpolating RGB
100 * @param inFraction between 0.0 (start) and 1.0 (end)
101 * @return mixed colour
102 */
103 protected Color mixColour(float inFraction)
104 {
105 if (_startColour == null && _endColour == null) return getDefaultColour();
106 if (_startColour == null) return _endColour;
107 if (_endColour == null || inFraction < 0.0 || inFraction > 1.0) return _startColour;
108
109 // Convert both colours to hsb, and interpolate
110 float[] startHSB = Color.RGBtoHSB(_startColour.getRed(), _startColour.getGreen(), _startColour.getBlue(), null);
111 float[] endHSB = Color.RGBtoHSB(_endColour.getRed(), _endColour.getGreen(), _endColour.getBlue(), null);
112 // Note that if end hue is less than start hue, hue will go backwards rather than forwards with wrap around 0
113
114 return Color.getHSBColor(startHSB[0] + (endHSB[0]-startHSB[0]) * inFraction,
115 startHSB[1] + (endHSB[1]-startHSB[1]) * inFraction,
116 startHSB[2] + (endHSB[2]-startHSB[2]) * inFraction);
117 }
118 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 import tim.prune.config.Config;
5 import tim.prune.data.Track;
6 import tim.prune.gui.profile.ProfileData;
7
8 /**
9 * Colourer based on speed values
10 */
11 public abstract class ProfileDataColourer extends ContinuousPointColourer
12 {
13 /**
14 * Constructor
15 * @param inStartColour start colour
16 * @param inEndColour end colour
17 */
18 public ProfileDataColourer(Color inStartColour, Color inEndColour)
19 {
20 super(inStartColour, inEndColour);
21 }
22
23 /**
24 * Calculate the colours according to the track and the profile data
25 */
26 public void calculateColours(Track inTrack, ProfileData inData)
27 {
28 final int numPoints = inTrack == null ? 0 : inTrack.getNumPoints();
29
30 // Calculate values for each point
31 inData.init(Config.getUnitSet());
32 // Figure out speed range
33 double minValue = inData.getMinValue();
34 double maxValue = inData.getMaxValue();
35 if (!inData.hasData() || (maxValue - minValue) < 0.1)
36 {
37 // not enough value range, set all to null
38 init(0);
39 }
40 else
41 {
42 // initialise the array to the right size
43 init(numPoints);
44 // loop over track points to calculate colours
45 for (int i=0; i<numPoints; i++)
46 {
47 if (inData.hasData(i))
48 {
49 double fraction = (inData.getData(i) - minValue) / (maxValue - minValue);
50 setColour(i, mixColour((float) fraction));
51 }
52 else setColour(i, null);
53 }
54 }
55 }
56 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 import tim.prune.data.DataPoint;
5 import tim.prune.data.Track;
6 import tim.prune.data.TrackInfo;
7
8 /**
9 * Point colourer using the segment indices
10 */
11 public class SegmentColourer extends DiscretePointColourer
12 {
13 /**
14 * Constructor
15 * @param inStartColour start colour of scale
16 * @param inEndColour end colour of scale
17 * @param inWrapLength number of unique colours before wrap
18 */
19 public SegmentColourer(Color inStartColour, Color inEndColour, int inWrapLength)
20 {
21 super(inStartColour, inEndColour, inWrapLength);
22 }
23
24 /**
25 * Calculate the colours for each of the points in the given track
26 * @param inTrackInfo track info object
27 */
28 @Override
29 public void calculateColours(TrackInfo inTrackInfo)
30 {
31 // initialise the array to the right size
32 Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
33 final int numPoints = track == null ? 0 : track.getNumPoints();
34 init(numPoints);
35 // loop over track points
36 int c = -1; // first track point will increment this to 0
37 for (int i=0; i<numPoints; i++)
38 {
39 DataPoint p = track.getPoint(i);
40 if (p != null && !p.isWaypoint())
41 {
42 if (p.getSegmentStart()) {
43 c++;
44 }
45 setColour(i, c);
46 }
47 }
48 // generate the colours needed
49 generateDiscreteColours(c+1);
50 }
51 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 import tim.prune.data.Track;
5 import tim.prune.data.TrackInfo;
6 import tim.prune.gui.profile.SpeedData;
7
8 /**
9 * Colourer based on speed values
10 */
11 public class SpeedColourer extends ProfileDataColourer
12 {
13 /**
14 * Constructor
15 * @param inStartColour start colour
16 * @param inEndColour end colour
17 */
18 public SpeedColourer(Color inStartColour, Color inEndColour)
19 {
20 super(inStartColour, inEndColour);
21 }
22
23 @Override
24 public void calculateColours(TrackInfo inTrackInfo)
25 {
26 Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
27 // Calculate speed value for each point
28 SpeedData data = new SpeedData(track);
29 calculateColours(track, data);
30 }
31 }
0 package tim.prune.gui.colour;
1
2 import java.awt.Color;
3
4 import tim.prune.data.Track;
5 import tim.prune.data.TrackInfo;
6 import tim.prune.gui.profile.VerticalSpeedData;
7
8 /**
9 * Colourer based on vertical speed values
10 */
11 public class VertSpeedColourer extends ProfileDataColourer
12 {
13 /**
14 * Constructor
15 * @param inStartColour start colour
16 * @param inEndColour end colour
17 */
18 public VertSpeedColourer(Color inStartColour, Color inEndColour)
19 {
20 super(inStartColour, inEndColour);
21 }
22
23 @Override
24 public void calculateColours(TrackInfo inTrackInfo)
25 {
26 Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
27 // Calculate speed value for each point
28 VerticalSpeedData data = new VerticalSpeedData(track);
29 calculateColours(track, data);
30 }
31 }
tim/prune/gui/images/window_icon.png less more
Binary diff not shown
00 package tim.prune.gui.map;
11
2 import java.awt.AlphaComposite;
23 import java.awt.BasicStroke;
34 import java.awt.BorderLayout;
45 import java.awt.Color;
5758 import tim.prune.function.edit.FieldEdit;
5859 import tim.prune.function.edit.FieldEditList;
5960 import tim.prune.gui.IconManager;
61 import tim.prune.gui.colour.PointColourer;
6062 import tim.prune.tips.TipManager;
6163
6264 /**
8385 private MapTileManager _tileManager = new MapTileManager(this);
8486 /** Image to display */
8587 private BufferedImage _mapImage = null;
88 /** Second image for drawing track (only needed for alpha blending) */
89 private BufferedImage _trackImage = null;
8690 /** Slider for transparency */
8791 private JSlider _transparencySlider = null;
8892 /** Checkbox for scale bar */
483487 else if (px > (getWidth()-PAN_DISTANCE)) {
484488 panX = AUTOPAN_DISTANCE + px - getWidth();
485489 }
486 if (py < PAN_DISTANCE) {
490 if (py < (2*PAN_DISTANCE)) {
487491 panY = py - AUTOPAN_DISTANCE;
488492 }
489493 if (py > (getHeight()-PAN_DISTANCE)) {
579583 }
580584 }
581585
582 // Paint the track points on top
583 int pointsPainted = 1;
584 try
585 {
586 pointsPainted = paintPoints(g);
587 }
588 catch (NullPointerException npe) {} // ignore, probably due to data being changed during drawing
589 catch (ArrayIndexOutOfBoundsException obe) {} // also ignore
586 // Work out track opacity according to slider
587 final float[] opacities = {1.0f, 0.75f, 0.5f, 0.3f, 0.15f, 0.0f};
588 float trackOpacity = 1.0f;
589 if (_transparencySlider.getValue() < 0) {
590 trackOpacity = opacities[-1 - _transparencySlider.getValue()];
591 }
592
593 if (trackOpacity > 0.0f)
594 {
595 // Paint the track points on top
596 int pointsPainted = 1;
597 try
598 {
599 if (trackOpacity > 0.9f)
600 {
601 // Track is fully opaque, just draw it directly
602 pointsPainted = paintPoints(g);
603 _trackImage = null;
604 }
605 else
606 {
607 // Track is partly transparent, so use a separate BufferedImage
608 if (_trackImage == null || _trackImage.getWidth() != getWidth() || _trackImage.getHeight() != getHeight())
609 {
610 _trackImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
611 }
612 // Clear to transparent
613 Graphics2D gTrack = _trackImage.createGraphics();
614 gTrack.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
615 gTrack.fillRect(0, 0, getWidth(), getHeight());
616 gTrack.setPaintMode();
617 // Draw the track onto this separate image
618 pointsPainted = paintPoints(gTrack);
619 ((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, trackOpacity));
620 g.drawImage(_trackImage, 0, 0, null);
621 }
622 }
623 catch (NullPointerException npe) {} // ignore, probably due to data being changed during drawing
624 catch (ArrayIndexOutOfBoundsException obe) {} // also ignore
625
626 // Zoom to fit if no points found
627 if (pointsPainted <= 0 && _checkBounds)
628 {
629 zoomToFit();
630 _recalculate = true;
631 repaint();
632 }
633 }
590634
591635 // free g
592636 g.dispose();
593637
594 // Zoom to fit if no points found
595 if (pointsPainted <= 0 && _checkBounds) {
596 zoomToFit();
597 _recalculate = true;
598 repaint();
599 }
600638 _checkBounds = false;
601639 // enable / disable transparency slider
602640 _transparencySlider.setEnabled(showMap);
612650 {
613651 // Set up colours
614652 final ColourScheme cs = Config.getColourScheme();
615 final int[] opacities = {255, 190, 130, 80, 40, 0};
616 int opacity = 255;
617 if (_transparencySlider.getValue() < 0)
618 opacity = opacities[-1 - _transparencySlider.getValue()];
619 final Color pointColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_POINT), opacity);
620 final Color rangeColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SELECTION), opacity);
621 final Color currentColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_PRIMARY), opacity);
622 final Color secondColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SECONDARY), opacity);
623 final Color textColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_TEXT), opacity);
653 final Color pointColour = cs.getColour(ColourScheme.IDX_POINT);
654 final Color rangeColour = cs.getColour(ColourScheme.IDX_SELECTION);
655 final Color currentColour = cs.getColour(ColourScheme.IDX_PRIMARY);
656 final Color secondColour = cs.getColour(ColourScheme.IDX_SECONDARY);
657 final Color textColour = cs.getColour(ColourScheme.IDX_TEXT);
658 final PointColourer pointColourer = _app.getPointColourer();
624659
625660 final int winWidth = getWidth();
626661 final int winHeight = getHeight();
658693 currPointVisible = px >= 0 && px < winWidth && py >= 0 && py < winHeight;
659694 isWaypoint = _track.getPoint(i).isWaypoint();
660695 anyWaypoints = anyWaypoints || isWaypoint;
661 if (currPointVisible)
662 {
663 if (!isWaypoint)
696 if (!isWaypoint)
697 {
698 if (currPointVisible || prevPointVisible)
664699 {
665 // Draw rectangle for track point
700 // For track points, work out which colour to use
666701 if (_track.getPoint(i).getDeleteFlag()) {
667702 inG.setColor(currentColour);
668703 }
669 else {
704 else if (pointColourer != null)
705 { // use the point colourer if there is one
706 Color trackColour = pointColourer.getColour(i);
707 inG.setColor(trackColour);
708 }
709 else
710 {
670711 inG.setColor(pointColour);
671712 }
672 inG.drawRect(px-2, py-2, 3, 3);
673 pointsPainted++;
674 }
675 }
676 if (!isWaypoint)
677 {
713
714 // Draw rectangle for track point if it's visible
715 if (currPointVisible)
716 {
717 inG.drawRect(px-2, py-2, 3, 3);
718 pointsPainted++;
719 }
720 }
721
678722 // Connect track points if either of them are visible
679 if (connectPoints && (currPointVisible || prevPointVisible)
680 && !(prevX == -1 && prevY == -1)
723 if (connectPoints && !(prevX == -1 && prevY == -1)
681724 && !_track.getPoint(i).getSegmentStart())
682725 {
683726 inG.drawLine(prevX, prevY, px, py);
859902 }
860903
861904 /**
862 * Make a semi-transparent colour for drawing with
863 * @param inColour base colour (fully opaque)
864 * @param inOpacity opacity where 0=invisible and 255=full
865 * @return new colour object
866 */
867 private static Color makeTransparentColour(Color inColour, int inOpacity)
868 {
869 if (inOpacity > 240) return inColour;
870 return new Color(inColour.getRed(), inColour.getGreen(), inColour.getBlue(), inOpacity);
871 }
872
873 /**
874905 * Inform that tiles have been updated and the map can be repainted
875906 * @param inIsOk true if data loaded ok, false for error
876907 */
10491080 else if (_drawMode == MODE_DRAW_POINTS_CONT)
10501081 {
10511082 DataPoint point = createPointFromClick(inE.getX(), inE.getY());
1052 _app.createPoint(point);
1053 point.setSegmentStart(false);
1083 _app.createPoint(point, false); // not a new segment
10541084 }
10551085 }
10561086 else if (inE.getClickCount() == 2)
8686 // check prefix
8787 try {
8888 new URL(urlstr.replace('[', 'w').replace(']', 'w'));
89 // There's a warning that this URL object isn't used, but it's enough to know the parse
90 // was successful without an exception being thrown.
8991 }
9092 catch (MalformedURLException e)
9193 {
0 package tim.prune.gui.profile;
1
2 import tim.prune.I18nManager;
3 import tim.prune.data.GradientCalculator;
4 import tim.prune.data.SpeedValue;
5 import tim.prune.data.Track;
6 import tim.prune.data.UnitSet;
7
8 /**
9 * Class to provide a source of gradient data for the profile chart
10 * or for the point colourer
11 */
12 public class GradientData extends ProfileData
13 {
14 /**
15 * Constructor
16 * @param inTrack track object
17 */
18 public GradientData(Track inTrack) {
19 super(inTrack);
20 }
21
22 /**
23 * Get the data and populate the instance arrays
24 */
25 public void init(UnitSet inUnitSet)
26 {
27 setUnitSet(inUnitSet);
28 initArrays();
29 _hasData = false;
30 _minValue = _maxValue = 0.0;
31 SpeedValue speed = new SpeedValue();
32 if (_track != null)
33 {
34 for (int i=0; i<_track.getNumPoints(); i++)
35 {
36 // Get the gradient either from the speed values or from the distances and altitudes
37 GradientCalculator.calculateGradient(_track, i, speed);
38 if (speed.isValid())
39 {
40 double speedValue = speed.getValue();
41 _pointValues[i] = speedValue;
42 if (speedValue < _minValue || !_hasData) {_minValue = speedValue;}
43 if (speedValue > _maxValue || !_hasData) {_maxValue = speedValue;}
44 _hasData = true;
45 }
46 _pointHasData[i] = speed.isValid();
47 }
48 }
49 }
50
51 /**
52 * @return text description
53 */
54 public String getLabel()
55 {
56 return I18nManager.getText("fieldname.gradient");
57 }
58
59 /**
60 * @return key for message when no altitudes present
61 */
62 public String getNoDataKey() {
63 return "display.noaltitudes";
64 }
65 }
3838 {
3939 double speedValue = speed.getValue();
4040 _pointValues[i] = speedValue;
41 if (speedValue < _minValue || _minValue == 0.0) {_minValue = speedValue;}
42 if (speedValue > _maxValue) {_maxValue = speedValue;}
41 if (speedValue < _minValue || !_hasData) {_minValue = speedValue;}
42 if (speedValue > _maxValue || !_hasData) {_maxValue = speedValue;}
4343 _hasData = true;
4444 }
4545 _pointHasData[i] = speed.isValid();
3939 // Store the value and maintain max and min values
4040 double speedValue = speed.getValue();
4141 _pointValues[i] = speedValue;
42 if (speedValue < _minValue || _minValue == 0.0) {_minValue = speedValue;}
43 if (speedValue > _maxValue) {_maxValue = speedValue;}
42 if (speedValue < _minValue || !_hasData) {_minValue = speedValue;}
43 if (speedValue > _maxValue || !_hasData) {_maxValue = speedValue;}
4444 _hasData = true;
4545 }
4646 _pointHasData[i] = speed.isValid();
99 menu.track.undo=Herroep
1010 menu.track.clearundo=Herroep Lys Skoonmaak
1111 menu.track.deletemarked=Gemerkde Punt Uitvee
12 menu.track.rearrange=Herrangskik bakens
13 menu.track.rearrange.start=Bakens na begin van l\u00eaer
14 menu.track.rearrange.end=Bakens na einde van l\u00eaer
15 menu.track.rearrange.nearest=Beweeg elk na naaste spoor punt
12 function.rearrangewaypoints=Herrangskik bakens
1613 menu.range=Reeks
1714 menu.range.all=Selekteer Alles
1815 menu.range.none=Selekteer Niks
274271 dialog.correlate.timestamp.beginning=Begin
275272 dialog.correlate.timestamp.middle=Middel
276273 dialog.correlate.timestamp.end=Einde
277 dialog.rearrangephotos.tostart=Beweeg na begin
278 dialog.rearrangephotos.toend=Beweeg na einde
279 dialog.rearrangephotos.nosort=Nie sorteer
280 dialog.rearrangephotos.sortbyfilename=Sorteer volgens leernaam
281 dialog.rearrangephotos.sortbytime=Sorteer volgens tyd
274 dialog.rearrange.tostart=Beweeg na begin
275 dialog.rearrange.toend=Beweeg na einde
276 dialog.rearrange.tonearest=Beweeg elk na naaste spoor punt
277 dialog.rearrange.nosort=Nie sorteer
278 dialog.rearrange.sortbyfilename=Sorteer volgens leernaam
279 dialog.rearrange.sortbyname=Sorteer volgens naam
280 dialog.rearrange.sortbytime=Sorteer volgens tyd
282281 dialog.compress.closepoints.title=Naby punt verwydering
283282 dialog.compress.closepoints.paramdesc=Span faktor
284283 dialog.compress.wackypoints.title=Gekkige punt verwydering
1212 menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo
1313 menu.track.markrectangle=Ozna\u010dit body v obd\u00e9ln\u00edku
1414 menu.track.deletemarked=Smazat ozna\u010den\u00e9 body
15 menu.track.rearrange=P\u0159euspo\u0159\u00e1dat z\u00e1jmov\u00e9 body
16 menu.track.rearrange.start=V\u0161e na po\u010d\u00e1tek
17 menu.track.rearrange.end=V\u0161e na konec
18 menu.track.rearrange.nearest=Zarovnat body na stopu
15 function.rearrangewaypoints=P\u0159euspo\u0159\u00e1dat z\u00e1jmov\u00e9 body
1916 menu.range=Rozmez\u00ed
2017 menu.range.all=Vybrat v\u0161e
2118 menu.range.none=Zru\u0161it v\u00fdb\u011br
412409 dialog.correlate.select.audioname=N\u00e1zev audionahr\u00e1vky
413410 dialog.correlate.select.audiolater=Audio pozd\u011bj\u0161\u00ed
414411 dialog.rearrangephotos.desc=Vyberte um\u00edst\u011bn\u00ed a uspo\u0159\u00e1d\u00e1n\u00ed bod\u016f fotografi\u00ed
415 dialog.rearrangephotos.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek
416 dialog.rearrangephotos.toend=P\u0159en\u00e9st na konec
417 dialog.rearrangephotos.nosort=Neuspo\u0159\u00e1d\u00e1vat
418 dialog.rearrangephotos.sortbyfilename=Uspo\u0159\u00e1dat dle n\u00e1zvu souboru
419 dialog.rearrangephotos.sortbytime=Uspo\u0159\u00e1dat dle \u010dasu
412 dialog.rearrange.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek
413 dialog.rearrange.toend=P\u0159en\u00e9st na konec
414 dialog.rearrange.tonearest=Zarovnat body na stopu
415 dialog.rearrange.nosort=Neuspo\u0159\u00e1d\u00e1vat
416 dialog.rearrange.sortbyfilename=Uspo\u0159\u00e1dat dle n\u00e1zvu souboru
417 dialog.rearrange.sortbyname=Uspo\u0159\u00e1dat dle n\u00e1zvu
418 dialog.rearrange.sortbytime=Uspo\u0159\u00e1dat dle \u010dasu
420419 dialog.compress.closepoints.title=Odstran\u011bn\u00ed bl\u00edzk\u00fdch bod\u016f
421420 dialog.compress.closepoints.paramdesc=Koeficient bl\u00edzkosti
422421 dialog.compress.wackypoints.title=Odstran\u011bn\u00ed ust\u0159elen\u00fdch bod\u016f
848847 error.cache.notthere=Nepoda\u0159ilo se nal\u00e9zt adres\u00e1\u0159 s cache map.
849848 error.cache.empty=Adres\u00e1\u0159 s cache map je pr\u00e1zdn\u00fd.
850849 error.cache.cannotdelete=Nelze smazat soubory map.
851 error.interpolate.invalidparameter=Po\u010det bod\u016f mus\u00ed b\u00fdt mezi 1 a 1000
852850 error.learnestimationparams.failed=Na z\u00e1klad\u011b t\u00e9to stopy nelze vypo\u010d\u00edtat parametr.\nZkuste jin\u00e9 stopy.
853851 error.tracksplit.nosplit=Stopu nen\u00ed mo\u017en\u00e9 rozd\u011blit
854852 error.downloadsrtm.nocache=Nepoda\u0159ilo se ulo\u017eit soubory.\nPros\u00edm ov\u011b\u0159te diskovou cache.
99 menu.track.undo=Fortryd
1010 menu.track.clearundo=Nulstil fortrydelsesliste
1111 menu.track.deletemarked=Slet markerede punkter
12 menu.track.rearrange=Omorganis\u00e9r waypoints
13 menu.track.rearrange.nearest=Hvert waypoint til n\u00e6rmeste nabo
12 function.rearrangewaypoints=Omorganis\u00e9r waypoints
13 dialog.rearrange.tonearest=Hvert waypoint til n\u00e6rmeste nabo
1414 menu.range=Omr\u00e5de
1515 menu.range.all=V\u00e6lg alle
1616 menu.range.none=Ingen valgt
1212 menu.track.clearundo=Liste der letzten \u00c4nderungen l\u00f6schen
1313 menu.track.markrectangle=Punkte im Viereck markieren
1414 menu.track.deletemarked=Markierte Punkte l\u00f6schen
15 menu.track.rearrange=Wegpunkte neu anordnen
16 menu.track.rearrange.start=Alle Wegpunkte zum Anfang
17 menu.track.rearrange.end=Alle Wegpunkte ans Ende
18 menu.track.rearrange.nearest=Jeden Wegpunkt zum n\u00e4chsten Trackpunkt verschieben
15 function.rearrangewaypoints=Wegpunkte neu anordnen
1916 menu.range=Bereich
2017 menu.range.all=Alles markieren
2118 menu.range.none=Nichts markieren
8683 function.exportpov=POV exportieren
8784 function.exportsvg=SVG exportieren
8885 function.exportimage=Bild exportieren
89 function.editwaypointname=Name des Punkts bearbeiten
86 function.editwaypointname=Namen des Punkts bearbeiten
9087 function.compress=Track komprimieren
9188 function.deleterange=Bereich l\u00f6schen
9289 function.croptrack=Track zuschneiden
9390 function.interpolate=Punkte interpolieren
91 function.deletebydate=Punkte nach Datum l\u00f6schen
9492 function.addtimeoffset=Zeitverschiebung aufrechnen
9593 function.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
9694 function.convertnamestotimes=Namen der Wegpunkte in Zeitstempel umwandeln
105103 function.learnestimationparams=Zeitparameter erlernen
106104 function.setmapbg=Karte Hintergrund setzen
107105 function.setpaths=Programmpfade setzen
106 function.selectsegment=Aktuellen Abschnitt markieren
108107 function.splitsegments=In Trackabschnitte schneiden
109108 function.sewsegments=Trackabschnitte zusammenf\u00fcgen
110109 function.getgpsies=Tracks bei GPSies.com herunterladen
140139 function.diskcache=Karten auf Festplatte speichern
141140 function.managetilecache=Kartenkacheln verwalten
142141 function.getweatherforecast=Wettervorhersage herunterladen
142 function.setaltitudetolerance=Altitudtoleranz einstellen
143143
144144 # Dialogs
145145 dialog.exit.confirm.title=GpsPrune beenden
378378 dialog.gpsies.activity.skating=Inline-Skating
379379 dialog.wikipedia.column.name=Artikelname
380380 dialog.wikipedia.column.distance=Entfernung
381 dialog.wikipedia.nonefound=Keine Punkte gefunden
381382 dialog.correlate.notimestamps=Die Punkte enthalten keine Zeitangaben, deshalb k\u00f6nnen die Fotos nicht zugeordnet werden.
382383 dialog.correlate.nouncorrelatedphotos=Alle Fotos sind schon zugeordnet.\nWollen Sie trotzdem fortfahren?
383384 dialog.correlate.nouncorrelatedaudios=Alle Audiodateien sind schon zugeordnet.\nWollen Sie trotzdem fortfahren?
411412 dialog.correlate.audioselect.intro=W\u00e4hlen Sie eines dieser Audiodateien aus, um die Zeitdifferenz zu berechnen
412413 dialog.correlate.select.audioname=Audio Name
413414 dialog.correlate.select.audiolater=Audio sp\u00e4ter
415 dialog.rearrangewaypoints.desc=Setzen Sie das Ziel und die Reihenfolge der Wegpunkte
414416 dialog.rearrangephotos.desc=Setzen Sie das Ziel und die Reihenfolge der Fotopunkte
415 dialog.rearrangephotos.tostart=Am Anfang
416 dialog.rearrangephotos.toend=Am Ende
417 dialog.rearrangephotos.nosort=Nicht sortieren
418 dialog.rearrangephotos.sortbyfilename=per Dateiname sortieren
419 dialog.rearrangephotos.sortbytime=per Zeitstempel sortieren
417 dialog.rearrange.tostart=Zum Anfang
418 dialog.rearrange.toend=Zum Ende
419 dialog.rearrange.tonearest=Zum n\u00e4chsten Trackpunkt
420 dialog.rearrange.nosort=Nicht sortieren
421 dialog.rearrange.sortbyfilename=nach Dateinamen sortieren
422 dialog.rearrange.sortbyname=nach Namen sortieren
423 dialog.rearrange.sortbytime=nach Zeitstempel sortieren
420424 dialog.compress.closepoints.title=Nahegelegene Punkte entfernen
421425 dialog.compress.closepoints.paramdesc=Span-Faktor
422426 dialog.compress.wackypoints.title=Ungew\u00f6hnliche Punkte entfernen
474478 dialog.checkversion.releasedate2=ver\u00f6ffentlicht worden.
475479 dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://gpsprune.activityworkshop.net/download.html.
476480 dialog.keys.intro=Anstelle der Maus k\u00f6nnen Sie folgende Tastenkombinationen nutzen
477 dialog.keys.keylist=<table><tr><td>Pfeil Tasten</td><td>Karte verschieben</td></tr><tr><td>Strg + Links-, Rechts-Pfeil</td><td>Vorherigen oder n\u00e4chsten Punkt markieren</td></tr><tr><td>Strg + Auf-, Abw\u00e4rts-Pfeil</td><td>Ein- oder Auszoomen</td></tr><tr><td>Strg + Bild auf, ab</td><td>Vorheriges oder n\u00e4chstes Segment markieren</td></tr><tr><td>Strg + Pos1, Ende</td><td>Ersten oder letzten Punkt markieren</td></tr><tr><td>Entf</td><td>Aktuellen Punkt entfernen</td></tr></table>
481 dialog.keys.keylist=<table><tr><td>Pfeil Tasten</td><td>Karte verschieben</td></tr><tr><td>Strg + Links-, Rechts-Pfeil</td><td>Vorherigen oder n\u00e4chsten Punkt markieren</td></tr><tr><td>Strg + Auf-, Abw\u00e4rts-Pfeil</td><td>Ein- oder Auszoomen</td></tr><tr><td>Strg + Bild auf, ab</td><td>Zum vorherigen oder n\u00e4chsten Abschnitt</td></tr><tr><td>Strg + Pos1, Ende</td><td>Ersten oder letzten Punkt markieren</td></tr><tr><td>Entf</td><td>Aktuellen Punkt entfernen</td></tr></table>
478482 dialog.keys.normalmodifier=Strg
479483 dialog.keys.macmodifier=Kommando
480484 dialog.saveconfig.desc=Die folgende Einstellungen k\u00f6nnen gespeichert werden:
514518 dialog.colourchooser.red=Rot
515519 dialog.colourchooser.green=Gr\u00fcn
516520 dialog.colourchooser.blue=Blau
521 dialog.colourer.intro=F\u00e4rb die Trackpunkte unterschiedlich ein
522 dialog.colourer.type=Unterschiedliche Farben
523 dialog.colourer.type.none=Keine
524 dialog.colourer.type.byfile=Nach Datei
525 dialog.colourer.type.bysegment=Nach Abschnitt
526 dialog.colourer.type.byaltitude=Nach H\u00f6henangaben
527 dialog.colourer.type.byspeed=Nach Geschwindigkeit
528 dialog.colourer.type.byvertspeed=Nach vertikalen Geschwindigkeit
529 dialog.colourer.type.bygradient=Nach Gef\u00e4lle
530 dialog.colourer.type.bydate=Nach Datum
531 dialog.colourer.start=Anfangsfarbe
532 dialog.colourer.end=Zielfarbe
533 dialog.colourer.maxcolours=Maximal Anzahl Farben
517534 dialog.setlanguage.firstintro=Sie k\u00f6nnen entweder eine von den mitgelieferten Sprachen<p>oder eine Text-Datei ausw\u00e4hlen.
518535 dialog.setlanguage.secondintro=Sie m\u00fcssen Ihre Einstellungen speichern und dann<p>GpsPrune neu starten, um die Sprache zu \u00e4ndern.
519536 dialog.setlanguage.language=Sprache
563580 dialog.weather.temp=Temp
564581 dialog.weather.humidity=R.L.
565582 dialog.weather.creditnotice=Diese Daten wurden von openweathermap.org zur Verf\u00fcgung gestellt. Die Webseite hat mehr Information.
583 dialog.deletebydate.onlyonedate=Die Punkte fallen alle am gleichen Tag.
584 dialog.deletebydate.intro=F\u00fcr jeden gefundenen Datum, k\u00f6nnen Sie die Punkte behalten oder l\u00f6schen.
585 dialog.deletebydate.nodate=Ohne Zeitangabe
586 dialog.deletebydate.column.keep=Behalten
587 dialog.deletebydate.column.delete=L\u00f6schen
588 dialog.setaltitudetolerance.text.metres=Mindestabweichung (Meter) f\u00fcr H\u00f6henunterschiede
589 dialog.setaltitudetolerance.text.feet=Mindestabweichung (Feet) f\u00fcr H\u00f6henunterschiede
566590
567591 # 3d window
568592 dialog.3d.title=GpsPrune-3D-Ansicht
717741 fieldname.altitude=H\u00f6he
718742 fieldname.timestamp=Zeitstempel
719743 fieldname.time=Zeit
744 fieldname.date=Datum
720745 fieldname.waypointname=Name
721746 fieldname.waypointtype=Typ
722 fieldname.newsegment=Segment
747 fieldname.newsegment=Abschnitt
723748 fieldname.custom=Custom
724749 fieldname.prefix=Feld
725750 fieldname.distance=L\u00e4nge
845870 error.cache.notthere=Der Ordner wurde nicht gefunden
846871 error.cache.empty=Der Ordner ist leer
847872 error.cache.cannotdelete=Es konnte keine Kacheln gel\u00f6scht werden
848 error.interpolate.invalidparameter=Die Anzahl der Punkte muss zwischen 1 und 1000 liegen
849873 error.learnestimationparams.failed=Mit diesem Track k\u00f6nnen die Parameter nicht berechnet werden.\nVersuchen Sie mit mehreren Tracks.
850874 error.tracksplit.nosplit=Der Track konnte nicht aufgesplittet werden.
851875 error.downloadsrtm.nocache=Die Dateien konnten nicht gespeichert werden.\nBitte pr\u00fcfen Sie den Kartenordner nach.
1212 menu.track.clearundo=Undo-Liste l\u00f6sche
1313 menu.track.markrectangle=P\u00fcnkte inem Viereck markiere
1414 menu.track.deletemarked=Markierte P\u00fcnkte l\u00f6sche
15 menu.track.rearrange=Waypoints reorganisiere
16 menu.track.rearrange.start=Alli zum Aafang
17 menu.track.rearrange.end=Alli zum \u00c4nde
18 menu.track.rearrange.nearest=Jede zum n\u00f6chsti Trackpunkt
15 function.rearrangewaypoints=Waypoints reorganisiere
1916 menu.range=Beriich
2017 menu.range.all=Alles selektiere
2118 menu.range.none=N\u00fc\u00fct selektiere
8582 function.exportpov=POV exportier\u00e4
8683 function.exportsvg=SVG exportier\u00e4
8784 function.exportimage=Bild exportier\u00e4
88 function.editwaypointname=Waypoint Name editiere
85 function.editwaypointname=Waypoint Namen editiere
8986 function.compress=Track komprimier\u00e4
9087 function.deleterange=Beriich l\u00f6sche
9188 function.croptrack=Track zuschniide
89 function.deletebydate=P\u00fcnkte na Datum l\u00f6sche
9290 function.addtimeoffset=Ziitverschiebig zutue
9391 function.addaltitudeoffset=H\u00f6chiverschiebig zutue
9492 function.findwaypoint=Waypoint suech\u00e4
102100 function.estimatetime=Ziit absch\u00e4tze
103101 function.learnestimationparams=Ziitparameter erlerne
104102 function.setmapbg=Karte Hintegrund setz\u00e4
103 function.selectsegment=Aktuelli Segm\u00e4nt selektiere
105104 function.splitsegments=In Tracksegm\u00e4nte schniide
106105 function.sewsegments=Tracksegm\u00e4nte z\u00e4mef\u00fcge
107106 function.getgpsies=Gpsies Tracks hol\u00e4
135134 function.diskcache=Karten uufem Disk speichere
136135 function.managetilecache=Kartebildli verwolte
137136 function.getweatherforecast=W\u00e4tterprognose abalade
137 function.setaltitudetolerance=H\u00f6chitoleranz iistelle
138138
139139 # Dialogs
140140 dialog.exit.confirm.title=GpsPrune be\u00e4nde
161161 dialog.openoptions.deliminfo.fields=F\u00e4ldere
162162 dialog.openoptions.deliminfo.norecords=Kei Rekords
163163 dialog.openoptions.altitudeunits=H\u00f6chi Masseiheite
164 dialog.openoptions.speedunits=Masseiheite f\u00fcr Geschwindigkeite
165 dialog.openoptions.vertspeedunits=Masseiheite f\u00fcr vertikale Geschwindigkeite
164 dialog.openoptions.speedunits=Masseiheite f\u00fcr Gschwindikeite
165 dialog.openoptions.vertspeedunits=Masseiheite f\u00fcr vertikale Gschwindikeite
166166 dialog.openoptions.vspeed.positiveup=Positive bed\u00fc\u00fctet uufe
167167 dialog.openoptions.vspeed.positivedown=Positive bed\u00fc\u00fctet abe
168168 dialog.open.contentsdoubled=Dieses File h\u00e4t zwei Kopien von j\u00e4dem Punkt,\neimol als Waypoint und eimol als Trackpunkt.
373373 dialog.gpsies.activity.skating=Inline-Skate
374374 dialog.wikipedia.column.name=Artikelname
375375 dialog.wikipedia.column.distance=Entf\u00e4rnig
376 dialog.wikipedia.nonefound=Kei Wiki-Iitr\u00e4ge gfunde
376377 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.
377378 dialog.correlate.nouncorrelatedphotos=Alle F\u00f6telis sin scho korreliert.\nWend Sie trotzdem fortsetz\u00e4?
378379 dialog.correlate.nouncorrelatedaudios=Alle Audios sin scho korreliert.\nWend Sie trotzdem fortsetz\u00e4?
406407 dialog.correlate.audioselect.intro=W\u00e4hlet Sie eini vo deren Audios uus, um die Ziitdiffer\u00e4nz zu ber\u00e4chn\u00e4
407408 dialog.correlate.select.audioname=Audio Name
408409 dialog.correlate.select.audiolater=Audio sp\u00f6ter
409 dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von d P\u00fcnkte setze
410 dialog.rearrangephotos.tostart=zum Aafang
411 dialog.rearrangephotos.toend=zum \u00c4nde
412 dialog.rearrangephotos.nosort=N\u00f6d sortiere
413 dialog.rearrangephotos.sortbyfilename=per Filename sortiere
414 dialog.rearrangephotos.sortbytime=per Ziit sortiere
410 dialog.rearrangewaypoints.desc=Bitte Ziel und Reihefolge von d Waypoints setze
411 dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von d F\u00f6telip\u00fcnkte setze
412 dialog.rearrange.tostart=zum Aafang
413 dialog.rearrange.toend=zum \u00c4nde
414 dialog.rearrange.tonearest=zum n\u00f6chsti Trackpunkt
415 dialog.rearrange.nosort=N\u00f6d sortiere
416 dialog.rearrange.sortbyfilename=nachem Filename sortiere
417 dialog.rearrange.sortbyname=nachem Name sortiere
418 dialog.rearrange.sortbytime=nachdr Ziit sortiere
415419 dialog.compress.duplicates.title=Duplikate entf\u00e4rn\u00e4
416420 dialog.compress.closepoints.title=N\u00f6chigl\u00e4geni P\u00fcnkte entf\u00e4rn\u00e4
417421 dialog.compress.closepoints.paramdesc=Span Faktor
509513 dialog.colourchooser.red=Rot
510514 dialog.colourchooser.green=Gr\u00fcen
511515 dialog.colourchooser.blue=Blau
516 dialog.colourer.intro=F\u00e4rb die Trackp\u00fcnkte unterschiedlich ii
517 dialog.colourer.type=Unterschiedliche Farbe
518 dialog.colourer.type.none=Kei
519 dialog.colourer.type.byfile=Nach Datei
520 dialog.colourer.type.bysegment=Nach Segm\u00e4nt
521 dialog.colourer.type.byaltitude=Nach H\u00f6chi
522 dialog.colourer.type.byspeed=Nach Gschwindikeit
523 dialog.colourer.type.byvertspeed=Nach uf/ab Gschwindikeit
524 dialog.colourer.type.bygradient=Nach Gef\u00e4lle
525 dialog.colourer.type.bydate=Nach Datum
526 dialog.colourer.start=Aafangsfarb
527 dialog.colourer.end=Zielfarb
528 dialog.colourer.maxcolours=Maximal Anzahl Farbe
512529 dialog.setlanguage.firstintro=Sie k\u00f6nnet entweder eini vo den iigebouti Sproche<p>oder ne Text-Datei uusw\u00e4hle.
513530 dialog.setlanguage.secondintro=Sie m\u00fcnt Ihri Iistellige speichere und dann<p>GpsPrune wieder neustarte um die Sproch z'\u00e4ndere.
514531 dialog.setlanguage.language=Sproch
558575 dialog.weather.temp=Temp
559576 dialog.weather.humidity=R.L.
560577 dialog.weather.creditnotice=Diese Date sin vo openweathermap.org zur Verf\u00fcegig gestellt worde. Uf d Websiite h\u00e4ts no meh Infos.
578 dialog.deletebydate.onlyonedate=Die P\u00fcnkte sin alli vom gliichen Tag.
579 dialog.deletebydate.intro=F\u00fcr jeden Tag, k\u00f6nnet Sie d P\u00fcnkte behalte oder l\u00f6sche.
580 dialog.deletebydate.nodate=Ohni Ziitaagab
581 dialog.deletebydate.column.keep=Behalte
582 dialog.deletebydate.column.delete=L\u00f6sche
583 dialog.setaltitudetolerance.text.metres=Mindeschtabweichig (Meter) f\u00fcr H\u00f6chiunterschied
584 dialog.setaltitudetolerance.text.feet=Mindeschtabweichig (Feet) f\u00fcr H\u00f6chinunterschied
561585
562586 # 3d window
563587 dialog.3d.title=GpsPrune Dr\u00fc\u00fc-d Aasicht
611635 tip.learntimeparams=Wenn Sie Track -> Ziitparameter erlerne mit Ihren Tracks benutze\ndann werdet d ber\u00e4chneti Werte gnauer.
612636 tip.downloadsrtm=Sie k\u00f6nnet d Funktion beschleunige indem Sie\nOnline -> SRTM Files abalade uufrufe\num d Date lokal z'speichere.
613637 tip.usesrtmfor3d=Dere Track h\u00e4t kei H\u00f6chiinformation.\nSie k\u00f6nnet d SRTM Funktione verw\u00e4nde, um\nH\u00f6chiwerte abz'sch\u00e4tze.
614 tip.manuallycorrelateone=Mit mindeschtens einem verbundenen Elem\u00e4nt kann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
638 tip.manuallycorrelateone=Mit mindeschtens einem verbundenen Elem\u00e4nt chann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
615639
616640 # Buttons
617641 button.ok=OK
712736 fieldname.altitude=H\u00f6chi
713737 fieldname.timestamp=Ziitst\u00e4mpel
714738 fieldname.time=Ziit
739 fieldname.date=Tag
715740 fieldname.waypointname=Name
716741 fieldname.waypointtype=Typ
717742 fieldname.newsegment=Segm\u00e4nt
840865 error.cache.notthere=D Ordner isch n\u00f6d gfunde worde
841866 error.cache.empty=D Ordner h\u00e4t n\u00fc\u00fct drinne
842867 error.cache.cannotdelete=Es sin kei Kachle gl\u00f6scht worde
843 error.interpolate.invalidparameter=D'Aazahl P\u00fcnkt muess zw\u00fcschet 1 und 1000 sii
844868 error.learnestimationparams.failed=Mit dere Track k\u00f6nnet die Parameter n\u00f6d br\u00e4chnet werde.\nVersuechet Sie mit mehreri Tracks.
845869 error.tracksplit.nosplit=Es isch n\u00f6d m\u00f6glech gsi, den Track uufz'schniide.
846870 error.downloadsrtm.nocache=Die Files k\u00f6nnet n\u00f6d gspeicheret werde.\nBitte pr\u00fcefet Sie den Kartenordner na.
1212 menu.track.clearundo=Clear undo list
1313 menu.track.markrectangle=Mark points in rectangle
1414 menu.track.deletemarked=Delete marked points
15 menu.track.rearrange=Rearrange waypoints
16 menu.track.rearrange.start=All to start of file
17 menu.track.rearrange.end=All to end of file
18 menu.track.rearrange.nearest=Each to nearest track point
1915 menu.range=Range
2016 menu.range.all=Select all
2117 menu.range.none=Select none
9187 function.deleterange=Delete range
9288 function.croptrack=Crop track
9389 function.interpolate=Interpolate points
90 function.deletebydate=Delete points by date
9491 function.addtimeoffset=Add time offset
9592 function.addaltitudeoffset=Add altitude offset
9693 function.findwaypoint=Find waypoint
94 function.rearrangewaypoints=Rearrange waypoints
9795 function.convertnamestotimes=Convert waypoint names to times
9896 function.deletefieldvalues=Delete field values
9997 function.pastecoordinates=Enter new coordinates
103101 function.fullrangedetails=Full range details
104102 function.estimatetime=Estimate time
105103 function.learnestimationparams=Learn time estimation parameters
104 function.selectsegment=Select current segment
106105 function.splitsegments=Split track into segments
107106 function.sewsegments=Sew track segments together
108107 function.getgpsies=Get Gpsies tracks
140139 function.diskcache=Save maps to disk
141140 function.managetilecache=Manage tile cache
142141 function.getweatherforecast=Get weather forecast
142 function.setaltitudetolerance=Set altitude tolerance
143143
144144 # Dialogs
145145 dialog.exit.confirm.title=Exit GpsPrune
217217 dialog.save.table.hasdata=Has data
218218 dialog.save.table.save=Save
219219 dialog.save.headerrow=Output header row
220 dialog.save.coordinateunits=Coordinate units
220 dialog.save.coordinateunits=Coordinate format
221221 dialog.save.altitudeunits=Altitude units
222222 dialog.save.timestampformat=Timestamp format
223223 dialog.save.overwrite.title=File already exists
378378 dialog.gpsies.activity.skating=Skating
379379 dialog.wikipedia.column.name=Article name
380380 dialog.wikipedia.column.distance=Distance
381 dialog.wikipedia.nonefound=No wikipedia entries found
381382 dialog.correlate.notimestamps=There are no timestamps in the data points, so there is nothing to correlate with the photos.
382383 dialog.correlate.nouncorrelatedphotos=There are no uncorrelated photos.\nAre you sure you want to continue?
383384 dialog.correlate.nouncorrelatedaudios=There are no uncorrelated audios.\nAre you sure you want to continue?
411412 dialog.correlate.audioselect.intro=Select one of these correlated audios to use as the time offset
412413 dialog.correlate.select.audioname=Audio name
413414 dialog.correlate.select.audiolater=Audio later
415 dialog.rearrangewaypoints.desc=Select the destination and sort order of the waypoints
414416 dialog.rearrangephotos.desc=Select the destination and sort order of the photo points
415 dialog.rearrangephotos.tostart=Move to start
416 dialog.rearrangephotos.toend=Move to end
417 dialog.rearrangephotos.nosort=Don't sort
418 dialog.rearrangephotos.sortbyfilename=Sort by filename
419 dialog.rearrangephotos.sortbytime=Sort by time
417 dialog.rearrange.tostart=Move to start
418 dialog.rearrange.toend=Move to end
419 dialog.rearrange.tonearest=Each to nearest track point
420 dialog.rearrange.nosort=Don't sort
421 dialog.rearrange.sortbyfilename=Sort by filename
422 dialog.rearrange.sortbyname=Sort by name
423 dialog.rearrange.sortbytime=Sort by time
420424 dialog.compress.duplicates.title=Duplicate removal
421425 dialog.compress.closepoints.title=Nearby point removal
422426 dialog.compress.closepoints.paramdesc=Span factor
514518 dialog.colourchooser.red=Red
515519 dialog.colourchooser.green=Green
516520 dialog.colourchooser.blue=Blue
521 dialog.colourer.intro=A point colourer can give track points different colours
522 dialog.colourer.type=Colourer type
523 dialog.colourer.type.none=None
524 dialog.colourer.type.byfile=By file
525 dialog.colourer.type.bysegment=By segment
526 dialog.colourer.type.byaltitude=By altitude
527 dialog.colourer.type.byspeed=By speed
528 dialog.colourer.type.byvertspeed=By vertical speed
529 dialog.colourer.type.bygradient=By gradient
530 dialog.colourer.type.bydate=By date
531 dialog.colourer.start=Start colour
532 dialog.colourer.end=End colour
533 dialog.colourer.maxcolours=Maximum number of colours
517534 dialog.setlanguage.firstintro=You can either select one of the included languages,<p>or select a text file to use instead.
518535 dialog.setlanguage.secondintro=You need to save your settings and then<p>restart GpsPrune to change the language.
519536 dialog.setlanguage.language=Language
563580 dialog.weather.temp=Temp
564581 dialog.weather.humidity=Humidity
565582 dialog.weather.creditnotice=This data is made available by openweathermap.org. Their website has more details.
583 dialog.deletebydate.onlyonedate=The points were all recorded on the same date.
584 dialog.deletebydate.intro=For each date in the track, you can choose to delete or keep the points
585 dialog.deletebydate.nodate=No timestamp
586 dialog.deletebydate.column.keep=Keep
587 dialog.deletebydate.column.delete=Delete
588 dialog.setaltitudetolerance.text.metres=Limit (in metres) below which small climbs and descents will be ignored
589 dialog.setaltitudetolerance.text.feet=Limit (in feet) below which small climbs and descents will be ignored
566590
567591 # 3d window
568592 dialog.3d.title=GpsPrune Three-d view
717741 fieldname.altitude=Altitude
718742 fieldname.timestamp=Time
719743 fieldname.time=Time
744 fieldname.date=Date
720745 fieldname.waypointname=Name
721746 fieldname.waypointtype=Type
722747 fieldname.newsegment=Segment
851876 error.cache.notthere=The tile cache directory was not found
852877 error.cache.empty=The tile cache directory is empty
853878 error.cache.cannotdelete=No tiles could be deleted
854 error.interpolate.invalidparameter=The number of points must be between 1 and 1000
855879 error.learnestimationparams.failed=Cannot learn the parameters from this track.\nTry loading more tracks.
856880 error.tracksplit.nosplit=The track could not be split
857881 error.downloadsrtm.nocache=The files could not be saved.\nPlease check the disk cache.
66 # Dialogs
77 dialog.exportkml.trackcolour=Track color
88 dialog.saveconfig.prune.languagecode=Language code (EN_US)
9 dialog.saveconfig.prune.colourscheme=Color scheme
10 dialog.saveconfig.prune.kmltrackcolour=KML track color
911 dialog.setcolours.intro=Click on a color patch to change the color
1012 dialog.colourchooser.title=Choose color
13 dialog.colourer.intro=A point colorer can give track points different colors
14 dialog.colourer.type=Colorer type
15 dialog.colourer.start=Start color
16 dialog.colourer.end=End color
17 dialog.colourer.maxcolours=Maximum number of colors
18 dialog.setaltitudetolerance.text.metres=Limit (in meters) below which small climbs and descents will be ignored
1119
1220 # Measurement units
1321 units.metres=Meters
1212 menu.track.clearundo=Despejar la lista de deshacer
1313 menu.track.markrectangle=Marcar puntos dentro de un rect\u00e1ngulo
1414 menu.track.deletemarked=Eliminar puntos marcados
15 menu.track.rearrange=Reorganizar waypoints
16 menu.track.rearrange.start=Volver al comienzo
17 menu.track.rearrange.end=Ir al final
18 menu.track.rearrange.nearest=Ir al m\u00e1s pr\u00f3ximo
15 function.rearrangewaypoints=Reorganizar waypoints
1916 menu.range=Rango
2017 menu.range.all=Seleccionar todo
2118 menu.range.none=No seleccionar nada
9188 function.deleterange=Eliminar rango
9289 function.croptrack=Truncar track
9390 function.interpolate=Interpolar puntos
91 function.deletebydate=Eliminar puntos de acuerdo a la fecha
9492 function.addtimeoffset=A\u00f1adir compensar tiempo
9593 function.addaltitudeoffset=A\u00f1adir compensar altitud
9694 function.convertnamestotimes=Convertir los nombres de los "waypoints" a tiempo
104102 function.estimatetime=Estimar duraci\u00f3n
105103 function.setmapbg=Configurar fondo de mapa
106104 function.setpaths=Configurar rutas del programas
105 function.selectsegment=Seleccionar segmento actual
107106 function.splitsegments=Segmentar el track
108107 function.sewsegments=Ensamblar los segmentos
109108 function.getgpsies=Bajar ruta de Gpsies
362361 dialog.gpsies.activity.skating=Patinaje
363362 dialog.wikipedia.column.name=Nombre del art\u00edculo
364363 dialog.wikipedia.column.distance=Distancia
364 dialog.wikipedia.nonefound=No se encontraron puntos
365365 dialog.correlate.notimestamps=No hay informaci\u00f3n de tiempo para los puntos, as\u00ed que no hay nada que correlacionar con las fotos.
366366 dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar?
367367 dialog.correlate.nouncorrelatedaudios=No hay audios no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar?
396396 dialog.correlate.select.audioname=Nombre del audio
397397 dialog.correlate.select.audiolater=Audio m\u00e1s adelante
398398 dialog.rearrangephotos.desc=Seleccionar el destino y sortear el orden de los puntos de las fotos
399 dialog.rearrangephotos.tostart=Mover al comienzo
400 dialog.rearrangephotos.toend=Mover al final
401 dialog.rearrangephotos.nosort=No sortear
402 dialog.rearrangephotos.sortbyfilename=Sortear por nombre del archivo
403 dialog.rearrangephotos.sortbytime=Sortear por tiempo
399 dialog.rearrange.tostart=Mover al comienzo
400 dialog.rearrange.toend=Mover al final
401 dialog.rearrange.tonearest=Mover al punto m\u00e1s pr\u00f3ximo
402 dialog.rearrange.nosort=No sortear
403 dialog.rearrange.sortbyfilename=Sortear por nombre del archivo
404 dialog.rearrange.sortbyname=Sortear por nombre
405 dialog.rearrange.sortbytime=Sortear por tiempo
404406 dialog.compress.closepoints.title=remover puntos cercanos
405407 dialog.compress.closepoints.paramdesc=Factor de extensi\u00f3n
406408 dialog.compress.wackypoints.title=Eliminar puntos an\u00f3malos
498500 dialog.colourchooser.red=Rojo
499501 dialog.colourchooser.green=Verde
500502 dialog.colourchooser.blue=Azul
503 dialog.colourer.start=Color de inicio
504 dialog.colourer.end=Color final
501505 dialog.setlanguage.firstintro=Puede usted seleccionar algunos de los lenguajes incluidos,<p>o puede en lugar de esto seleccionar un archivo de texto
502506 dialog.setlanguage.secondintro=Usted necesita guardar sus preferencias y luego<p>reiniciar GpsPrune para cambiar el lenguaje
503507 dialog.setlanguage.language=Lenguaje
545549 dialog.weather.wind=Viento
546550 dialog.weather.temp=Temp
547551 dialog.weather.humidity=Humedad
552 dialog.deletebydate.nodate=Sin marcas de tiempo
553 dialog.deletebydate.column.keep=Mantener
554 dialog.deletebydate.column.delete=Eliminar
548555
549556 # 3d window
550557 dialog.3d.title=GpsPrune vista 3-D
692699 fieldname.altitude=Altitud
693700 fieldname.timestamp=Informaci\u00f3n de tiempo
694701 fieldname.time=Tiempo
702 fieldname.date=Data
695703 fieldname.waypointname=Nombre
696704 fieldname.waypointtype=Tipo
697705 fieldname.newsegment=Segmento
826834 error.cache.notthere=No se encontr\u00f3 la carpeta del cache de recuadros
827835 error.cache.empty=La carpeta del cache de recuadros esta vac\u00edo
828836 error.cache.cannotdelete=No se pudieron borrar recuadros
829 error.interpolate.invalidparameter=El n\u00famero de puntos necesita ser entre 1 y 1000
830837 error.tracksplit.nosplit=Imposible segmentar el track
831838 error.downloadsrtm.nocache=Imposible guardar los archivos.\nPor favor, compruebe el cache.
832839 error.sewsegments.nothingdone=Imposible ensamblar los segmentos.\nEl track tiene ahora %d segmentos.
99 menu.track.undo=\u0628\u0627\u0637\u0644 \u06a9\u0631\u062f\u0646 \u06a9\u0627\u0631 \u0642\u0628\u0644\u064a
1010 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
1111 menu.track.deletemarked=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0646\u0642\u0627\u0637 \u0627\u0646\u062a\u062e\u0627\u0628 \u0634\u062f\u0647
12 menu.track.rearrange=\u0628\u0627\u0632 \u0686\u064a\u0646\u06cc \u0646\u0642\u0627\u0637 \u0645\u0633\u064a\u0631
13 menu.track.rearrange.start=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0628\u062a\u062f\u0627\u06cc \u0645\u0633\u064a\u0631
14 menu.track.rearrange.end=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0646\u062a\u0647\u0627\u06cc \u0645\u0633\u064a\u0631
15 menu.track.rearrange.nearest=\u0647\u0631 \u064a\u06a9 \u0628\u0647 \u0646\u0632\u062f\u064a\u06a9 \u0646\u0642\u0637\u0647 \u0645\u0633\u064a\u0631
12 function.rearrangewaypoints=\u0628\u0627\u0632 \u0686\u064a\u0646\u06cc \u0646\u0642\u0627\u0637 \u0645\u0633\u064a\u0631
13 dialog.rearrange.tostart=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0628\u062a\u062f\u0627\u06cc \u0645\u0633\u064a\u0631
14 dialog.rearrange.toend=\u0647\u0645\u0647 \u0646\u0642\u0627\u0637 \u0628\u0647 \u0627\u0646\u062a\u0647\u0627\u06cc \u0645\u0633\u064a\u0631
15 dialog.rearrange.tonearest=\u0647\u0631 \u064a\u06a9 \u0628\u0647 \u0646\u0632\u062f\u064a\u06a9 \u0646\u0642\u0637\u0647 \u0645\u0633\u064a\u0631
1616 menu.range=\u0686\u064a\u0646\u0634
1717 menu.range.all=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u0645\u0647 \u0646\u0642\u0627\u0637
1818 menu.range.none=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u064a\u0686 \u064a\u06a9 \u0627\u0632 \u0646\u0642\u0627\u0637
1212 menu.track.clearundo=Purger la liste d'annulation
1313 menu.track.markrectangle=S\u00e9lectionner les points dans un rectangle
1414 menu.track.deletemarked=Supprimer les points marqu\u00e9s
15 menu.track.rearrange=R\u00e9arranger les waypoints
16 menu.track.rearrange.start=Tous au d\u00e9but du fichier
17 menu.track.rearrange.end=Tous \u00e0 la fin du fichier
18 menu.track.rearrange.nearest=Chacun au point de trace le plus proche
15 function.rearrangewaypoints=R\u00e9arranger les waypoints
1916 menu.range=\u00c9tendue
2017 menu.range.all=Tout s\u00e9lectionner
2118 menu.range.none=Rien s\u00e9lectionner
10299 function.distances=Distances
103100 function.fullrangedetails=Montrer tous les d\u00e9tails
104101 function.estimatetime=Temps estim\u00e9
102 function.learnestimationparams=Apprendre les param\u00e8tres pour l'estimation
105103 function.setmapbg=D\u00e9finir le fond de carte
106104 function.setpaths=D\u00e9finir les chemins des programmes
107105 function.splitsegments=S\u00e9pare les segments
359357 dialog.gpsies.activity.skating=Skating
360358 dialog.wikipedia.column.name=Nom de l'article
361359 dialog.wikipedia.column.distance=Distance
360 dialog.wikipedia.nonefound=Aucune points trouv\u00e9e
362361 dialog.correlate.notimestamps=Les points n'ont pas d'indication de temps, il n'est pas possible de les corr\u00e9ler.
363362 dialog.correlate.nouncorrelatedphotos=Il n'y a pas de photos non-corr\u00e9l\u00e9es.\nVoulez-vous continuer ?
364363 dialog.correlate.nouncorrelatedaudios=Il n'y a pas d'audios non-corr\u00e9l\u00e9s.\nVoulez-vous continuer ?
393392 dialog.correlate.select.audioname=Nom du fichier audio
394393 dialog.correlate.select.audiolater=Audio apr\u00e8s
395394 dialog.rearrangephotos.desc=Choisissez la destination et l\u2019ordre des points des photos
396 dialog.rearrangephotos.tostart=Aller au d\u00e9but
397 dialog.rearrangephotos.toend=Aller \u00e0 la fin
398 dialog.rearrangephotos.nosort=Ne pas trier
399 dialog.rearrangephotos.sortbyfilename=Trier par nom de fichier
400 dialog.rearrangephotos.sortbytime=Trier par horodatage
395 dialog.rearrange.tostart=Au d\u00e9but
396 dialog.rearrange.toend=\u00e0 la fin
397 dialog.rearrange.tonearest=Au point de trace le plus proche
398 dialog.rearrange.nosort=Ne pas trier
399 dialog.rearrange.sortbyfilename=Trier par nom de fichier
400 dialog.rearrange.sortbyname=Trier par nom
401 dialog.rearrange.sortbytime=Trier par horodatage
401402 dialog.compress.closepoints.title=Suppression des points voisins
402403 dialog.compress.closepoints.paramdesc=Taille du voisinage
403404 dialog.compress.wackypoints.title=Suppression des points anormaux
544545 dialog.weather.temp=Temp
545546 dialog.weather.humidity=Humidit\u00e9
546547 dialog.weather.creditnotice=Ces donn\u00e9es sont fournies par openweathermap.org. Consultez la page pour plus de d\u00e9tails.
548 dialog.deletebydate.column.keep=Conserver
549 dialog.deletebydate.column.delete=Supprimer
547550
548551 # 3d window
549552 dialog.3d.title=Vue 3D de GpsPrune
830833 error.cache.notthere=Le dossier du cache n'a pas \u00e9t\u00e9 trouv\u00e9
831834 error.cache.empty=Le dossier du cache est vide
832835 error.cache.cannotdelete=Effacement des dalles impossible
833 error.interpolate.invalidparameter=Le nombre de points doit \u00eatre compris entre 1 et 1000
834836 error.tracksplit.nosplit=Impossible de s\u00e9parer les segments
1212 menu.track.clearundo=Visszavon\u00e1si lista t\u00f6rl\u00e9se
1313 menu.track.markrectangle=N\u00e9gyzeten bel\u00fcli pontok megjel\u00f6l\u00e9se
1414 menu.track.deletemarked=Jel\u00f6lt pontok t\u00f6rl\u00e9se
15 menu.track.rearrange=\u00datpontok \u00fajrarendez\u00e9se
16 menu.track.rearrange.start=\u00d6sszes a f\u00e1jl elej\u00e9re
17 menu.track.rearrange.end=\u00d6sszes a f\u00e1jl v\u00e9g\u00e9re
18 menu.track.rearrange.nearest=Egyenk\u00e9nt a legk\u00f6zelebbi nyomponthoz
15 function.rearrangewaypoints=\u00datpontok \u00fajrarendez\u00e9se
16 dialog.rearrange.tonearest=Egyenk\u00e9nt a legk\u00f6zelebbi nyomponthoz
1917 menu.range=Tartom\u00e1ny
2018 menu.range.all=Mindet kijel\u00f6l
2119 menu.range.none=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
412410 dialog.correlate.select.audioname=Hang neve
413411 dialog.correlate.select.audiolater=Hang k\u00e9s\u0151bb
414412 dialog.rearrangephotos.desc=V\u00e1lassza ki a f\u00e9nyk\u00e9ppontok c\u00e9lj\u00e1t \u00e9s rendez\u00e9si sorrendj\u00e9t
415 dialog.rearrangephotos.tostart=Mozgat\u00e1s a kezdet\u00e9hez
416 dialog.rearrangephotos.toend=Mozgat\u00e1s a v\u00e9g\u00e9hez
417 dialog.rearrangephotos.nosort=Ne rendezze
418 dialog.rearrangephotos.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint
419 dialog.rearrangephotos.sortbytime=Rendez\u00e9s id\u0151 szerint
413 dialog.rearrange.tostart=Mozgat\u00e1s a kezdet\u00e9hez
414 dialog.rearrange.toend=Mozgat\u00e1s a v\u00e9g\u00e9hez
415 dialog.rearrange.nosort=Ne rendezze
416 dialog.rearrange.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint
417 dialog.rearrange.sortbytime=Rendez\u00e9s id\u0151 szerint
420418 dialog.compress.closepoints.title=K\u00f6zeli pontok elt\u00e1vol\u00edt\u00e1sa
421419 dialog.compress.closepoints.paramdesc=Hat\u00f3t\u00e1vols\u00e1g
422420 dialog.compress.wackypoints.title=Kisz\u00e1m\u00edthatatlan pontok elt\u00e1vol\u00edt\u00e1sa
847845 error.cache.notthere=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem tal\u00e1lhat\u00f3
848846 error.cache.empty=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra \u00fcres
849847 error.cache.cannotdelete=Nincs t\u00f6r\u00f6lhet\u0151 csempe
850 error.interpolate.invalidparameter=A pontok sz\u00e1ma 1 \u00e9s 1000 k\u00f6z\u00f6tt kell legyen
851848 error.learnestimationparams.failed=Nem lehet param\u00e9tereket tanulni ebb\u0151l a nyomvonalb\u00f3l.\nT\u00f6lts be t\u00f6bb nyomvonalat.
852849 error.tracksplit.nosplit=A nyomvonal nem elv\u00e1ghat\u00f3
853850 error.downloadsrtm.nocache=A f\u00e1jlokat nem siker\u00fclt meneni.\nK\u00e9rlek, ellen\u0151rizd a gyors\u00edt\u00f3t\u00e1rat.
1212 menu.track.clearundo=Cancella lista annulla
1313 menu.track.markrectangle=Segnare i punti nel rettangolo
1414 menu.track.deletemarked=Cancella punti marcati
15 menu.track.rearrange=Riorganizza waypoint
16 menu.track.rearrange.start=Tutti all'inizio del file
17 menu.track.rearrange.end=Tutti alla fine del file
18 menu.track.rearrange.nearest=Sul punto pi\u00f9 vicino
15 function.rearrangewaypoints=Riorganizza waypoint
1916 menu.range=Serie
2017 menu.range.all=Seleziona tutto
2118 menu.range.none=Deseleziona tutto
9188 function.deleterange=Cancella la serie
9289 function.croptrack=Cima la traccia
9390 function.interpolate=Interpola i punti
91 function.deletebydate=Cancella punti secondo la data
9492 function.addtimeoffset=Aggiungi uno scarto temporale
9593 function.addaltitudeoffset=Aggiungi uno scarto di altitudine
9694 function.convertnamestotimes=Converti nomi dei waypoint in orari
105103 function.learnestimationparams=Apprendi parametri di stima
106104 function.setmapbg=Configura sfondo mappa
107105 function.setpaths=Configura percorsi programmi
106 function.selectsegment=Seleziona segmento corrente
108107 function.splitsegments=Dividi traccia in segmenti
109108 function.sewsegments=Riorganizza segmenti insieme
110109 function.getgpsies=Ottieni tracce da Gpsies
140139 function.diskcache=Salva mappe su disco
141140 function.managetilecache=Gestione del cache di tasselli
142141 function.getweatherforecast=Ottieni previsioni del tempo
142 function.setaltitudetolerance=Configura tolleranza di altitudini
143143
144144 # Dialogs
145145 dialog.exit.confirm.title=Esci da GpsPrune
378378 dialog.gpsies.activity.skating=Pattinaggio
379379 dialog.wikipedia.column.name=Titolo articolo
380380 dialog.wikipedia.column.distance=Distanza
381 dialog.wikipedia.nonefound=Nessuna punti trovata
381382 dialog.correlate.notimestamps=Non ci sono informazioni temporali tra i dati dei punti, non c'\u00e8 niente per collegarli con le foto.
382383 dialog.correlate.nouncorrelatedphotos=Non ci sono foto non correlate.\nSei sicuro di voler continuare?
383384 dialog.correlate.nouncorrelatedaudios=Non ci sono audio non correlati. \nSei sicuro di voler continuare?
412413 dialog.correlate.select.audioname=Nome ripresa audio
413414 dialog.correlate.select.audiolater=Ripresa audio successiva
414415 dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei punti foto
415 dialog.rearrangephotos.tostart=Sposta all'inizio
416 dialog.rearrangephotos.toend=Sposta alla fine
417 dialog.rearrangephotos.nosort=Non mettere in ordine
418 dialog.rearrangephotos.sortbyfilename=Metti in ordine di nome del file
419 dialog.rearrangephotos.sortbytime=Metti in ordine di tempo
416 dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei waypoint
417 dialog.rearrange.tostart=Sposta all'inizio
418 dialog.rearrange.toend=Sposta alla fine
419 dialog.rearrange.tonearest=Sul punto pi\u00f9 vicino
420 dialog.rearrange.nosort=Non mettere in ordine
421 dialog.rearrange.sortbyfilename=Metti in ordine di nome del file
422 dialog.rearrange.sortbyname=Metti in ordine di nome
423 dialog.rearrange.sortbytime=Metti in ordine di tempo
420424 dialog.compress.closepoints.title=Cancella punti vicini
421425 dialog.compress.closepoints.paramdesc=Fattore vicinanza
422426 dialog.compress.wackypoints.title=Cancella punti strani
474478 dialog.checkversion.releasedate2=.
475479 dialog.checkversion.download=Per scaricare la nuova versione vai a http://gpsprune.activityworkshop.net/download.html.
476480 dialog.keys.intro=Puoi utilizzare i seguenti tast di scelta rapida al posto del mouse
477 dialog.keys.keylist=<table><tr><td>Tasti freccia</td><td>Muovi mappa destra, sinistra, su, giu'</td></tr><tr><td>Ctrl + freccia destra, sinistra</td><td>Selezione punto successivo o precedente</td></tr><tr><td>Ctrl + freccia su, giu'</td><td>Zoom in o out</td></tr><tr><td>Del</td><td>Cancella punto attuale</td></tr></table>
481 dialog.keys.keylist=<table><tr><td>Tasti freccia</td><td>Muovi mappa destra, sinistra, su, giu'</td></tr><tr><td>Ctrl + freccia destra, sinistra</td><td>Selezione punto successivo o precedente</td></tr><tr><td>Ctrl + freccia su, giu'</td><td>Zoom in o out</td></tr><tr><td>Ctrl + pagina su, giu'</td><td>Segmento successivo o precedente</tr><tr><td>Ctrl + Home, End</td><td>Punto primo o ultimo</td></tr><tr><td>Del</td><td>Cancella punto attuale</td></tr></table>
478482 dialog.keys.normalmodifier=Ctrl
479483 dialog.keys.macmodifier=Comando
480484 dialog.saveconfig.desc=Le configurazioni seguenti possono essere salvati in un file di configurazione:
514518 dialog.colourchooser.red=Rosso
515519 dialog.colourchooser.green=Verde
516520 dialog.colourchooser.blue=Blu
521 dialog.colourer.type.byfile=Per file
522 dialog.colourer.type.bysegment=Per segmento
523 dialog.colourer.type.byaltitude=Per altitud
524 dialog.colourer.type.byspeed=Per velocit\u00e0
525 dialog.colourer.type.byvertspeed=Per velocit\u00e0 verticale
526 dialog.colourer.type.bygradient=Per gradiente
527 dialog.colourer.type.bydate=Per data
528 dialog.colourer.start=Colore iniziale
529 dialog.colourer.end=Colore finale
530 dialog.colourer.maxcolours=Numero massimo di colori
517531 dialog.setlanguage.firstintro=Puoi selezionare una delle lingue incluse,<p>oppure selezionare un file di testo.
518532 dialog.setlanguage.secondintro=Devi salvare le impostazioni e<p> riavviare GpsPrune per cambiare la lingua.
519533 dialog.setlanguage.language=Lingua
563577 dialog.weather.temp=Temp
564578 dialog.weather.humidity=Umidit\u00e0
565579 dialog.weather.creditnotice=Queste informazioni sono rese disponibili da openweathermap.org. Il loro sito web contiene ulteriori dettagli.
580 dialog.deletebydate.nodate=Senza dati temporali
581 dialog.deletebydate.column.keep=Tieni
582 dialog.deletebydate.column.delete=Cancella
566583
567584 # 3d window
568585 dialog.3d.title=Visione GpsPrune in 3D
717734 fieldname.altitude=Altitudine
718735 fieldname.timestamp=Dati temporali
719736 fieldname.time=Tempo
737 fieldname.date=Data
720738 fieldname.waypointname=Nome
721739 fieldname.waypointtype=Tipo
722740 fieldname.newsegment=Segmento
851869 error.cache.notthere=Directory del cache di tasselli non trovato
852870 error.cache.empty=Directory del cache di tasselli \u00e8 vuoto
853871 error.cache.cannotdelete=Impossibile cancellare tasselli
854 error.interpolate.invalidparameter=Il numero di punti deve essere tra 1 e 1000
855872 error.learnestimationparams.failed=Non \u00e8 possibile apprendere i parametri da questa traccia.\nProva a caricare pi\u00f9 tracce.
856873 error.tracksplit.nosplit=La traccia non pu\u00f2 essere divisa
857874 error.downloadsrtm.nocache=Non \u00e8 stato possibile salvare i file.\nControlla la cache del disco.
1111 menu.track.clearundo=\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3059\u308b
1212 menu.track.markrectangle=\u56db\u89d2\u306e\u4e2d\u306b\u5370\u3092\u3064\u3051\u308b
1313 menu.track.deletemarked=\u5370\u306e\u4ed8\u3044\u305f\u70b9\u3092\u524a\u9664
14 menu.track.rearrange=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u4e26\u3079\u66ff\u3048
15 menu.track.rearrange.start=\u5168\u3066\u3092\u30d5\u30a1\u30a4\u30eb\u306e\u59cb\u70b9\u306b
16 menu.track.rearrange.end=\u5168\u3066\u3092\u30d5\u30a1\u30a4\u30eb\u306e\u7d42\u70b9\u306b
17 menu.track.rearrange.nearest=\u305d\u308c\u305e\u308c\u3092\u6700\u8fd1\u306e\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306b
14 function.rearrangewaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u4e26\u3079\u66ff\u3048
1815 menu.range=\u7bc4\u56f2(R)
1916 menu.range.all=\u5168\u3066\u9078\u629e
2017 menu.range.none=\u9078\u629e\u89e3\u9664
316313 dialog.correlate.timestamp.end=\u7d42\u70b9
317314 dialog.correlate.select.audioname=\u30aa\u30fc\u30c7\u30a3\u30aa\u540d
318315 dialog.rearrangephotos.desc=\u5411\u304b\u3046\u5148\u3092\u9078\u629e\u3057\u3066\u3001\u5199\u771f\u306e\u70b9\u3092\u4e26\u3079\u76f4\u3059\u3002
319 dialog.rearrangephotos.tostart=\u79fb\u52d5\u958b\u59cb
320 dialog.rearrangephotos.toend=\u79fb\u52d5\u7d42\u4e86
321 dialog.rearrangephotos.nosort=\u4e26\u3079\u66ff\u3048\u306a\u3044
322 dialog.rearrangephotos.sortbyfilename=\u30d5\u30a1\u30a4\u30eb\u540d\u3067\u4e26\u3079\u66ff\u3048
323 dialog.rearrangephotos.sortbytime=\u6642\u9593\u3067\u4e26\u3079\u66ff\u3048
316 dialog.rearrange.tostart=\u79fb\u52d5\u958b\u59cb
317 dialog.rearrange.toend=\u79fb\u52d5\u7d42\u4e86
318 dialog.rearrange.tonearest=\u305d\u308c\u305e\u308c\u3092\u6700\u8fd1\u306e\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306b
319 dialog.rearrange.nosort=\u4e26\u3079\u66ff\u3048\u306a\u3044
320 dialog.rearrange.sortbyfilename=\u30d5\u30a1\u30a4\u30eb\u540d\u3067\u4e26\u3079\u66ff\u3048
321 dialog.rearrange.sortbytime=\u6642\u9593\u3067\u4e26\u3079\u66ff\u3048
324322 dialog.compress.closepoints.title=\u8fd1\u508d\u70b9\u3092\u524a\u9664
325323 dialog.compress.closepoints.paramdesc=\u8fd1\u508d\u4fc2\u6570
326324 dialog.compress.wackypoints.title=\u304a\u304b\u3057\u306a\u70b9\u306e\u524a\u9664
99 menu.track.undo=\uc2e4\ud589\ucde8\uc18c
1010 menu.track.clearundo=\uc2e4\ud589\ucde8\uc18c \ubaa9\ub85d \uc0ad\uc81c
1111 menu.track.deletemarked=\ud45c\uc2dc\ub41c \uc9c0\uc810 \uc9c0\uc6b0\uae30
12 menu.track.rearrange=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c
13 menu.track.rearrange.start=\uc2dc\uc791 \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
14 menu.track.rearrange.end=\ub05d \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
15 menu.track.rearrange.nearest=\uac00\uae4c\uc6b4 \ud2b8\ub799\uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
12 function.rearrangewaypoints=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c
1613 menu.range=\uc5f0\uacb0\uc120
1714 menu.range.all=\ubaa8\ub450 \uc120\ud0dd
1815 menu.range.none=\uc120\ud0dd\ud558\uc9c0 \uc54a\uae30
319316 dialog.correlate.select.audioname=\uc18c\ub9ac \uc774\ub984
320317 dialog.correlate.select.audiolater=\uc18c\ub9ac \ud6c4\uc5d0
321318 dialog.rearrangephotos.desc=\uc0ac\uc9c4 \uc9c0\uc810\ub4e4\uc744 \uc5b4\ub5bb\uac8c \uc815\ub82c\ud560 \uac74\uc9c0 \uc120\ud0dd\ud574\uc8fc\uc138\uc694.
322 dialog.rearrangephotos.tostart=\uc2dc\uc791\uc73c\ub85c
323 dialog.rearrangephotos.toend=\ub05d\uc73c\ub85c
324 dialog.rearrangephotos.nosort=\uc815\ub82c\ud558\uc9c0 \uc54a\uae30
325 dialog.rearrangephotos.sortbyfilename=\ud30c\uc77c \uc774\ub984\uc73c\ub85c \uc815\ub82c
326 dialog.rearrangephotos.sortbytime=\uc2dc\uac04\uc73c\ub85c \uc815\ub82c
319 dialog.rearrange.tostart=\uc2dc\uc791\uc73c\ub85c
320 dialog.rearrange.toend=\ub05d\uc73c\ub85c
321 dialog.rearrange.tonearest=\uac00\uae4c\uc6b4 \ud2b8\ub799\uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
322 dialog.rearrange.nosort=\uc815\ub82c\ud558\uc9c0 \uc54a\uae30
323 dialog.rearrange.sortbyfilename=\ud30c\uc77c \uc774\ub984\uc73c\ub85c \uc815\ub82c
324 dialog.rearrange.sortbytime=\uc2dc\uac04\uc73c\ub85c \uc815\ub82c
327325 dialog.deletemarked.nonefound=\uc9c0\uc810 \ub370\uc774\ud130\uac00 \uc81c\uac70\ub420 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.
328326 dialog.compress.closepoints.title=\uc8fc\ubcc0 \ud3ec\uc778\ud2b8 \uc81c\uac70
329327 dialog.compress.closepoints.paramdesc=\ud655\uc7a5 \uacc4\uc218
1212 menu.track.clearundo=Ongedaan-maken lijst wissen
1313 menu.track.markrectangle=Makeer alle punten in een vierkant
1414 menu.track.deletemarked=Verwijderen gemarkeerde punten
15 menu.track.rearrange=Rangschikken waypoints
16 menu.track.rearrange.start=Alles naar begin route
17 menu.track.rearrange.end=Alles naar einde route
18 menu.track.rearrange.nearest=Alles naar dichtstbijzijnde routepunt
1915 menu.range=Reeks
2016 menu.range.all=Selecteer alles
2117 menu.range.none=Selecteer geen
9187 function.deleterange=Verwijder reeks
9288 function.croptrack=Route bijknippen
9389 function.interpolate=Interpoleer punten
90 function.deletebydate=Verwijder punten op datum
9491 function.addtimeoffset=Tijdsverschil toevoegen
9592 function.addaltitudeoffset=Hoogteverschil toevoegen
93 function.rearrangewaypoints=Rangschikken waypoints
9694 function.convertnamestotimes=Converteer waypointnamen naar tijden
9795 function.deletefieldvalues=Verwijder veldwaarden
9896 function.findwaypoint=Zoek waypoint
105103 function.learnestimationparams=Parameters voor geschatte tijd
106104 function.setmapbg=Instellen kaart achtergrond
107105 function.setpaths=Instellen programmapaden
106 function.selectsegment=Selecteer huidige segment
108107 function.splitsegments=Splits route in segmenten
109108 function.sewsegments=Voeg segmenten samen
110109 function.getgpsies=Routes van Gpsies ophalen
140139 function.diskcache=Kaart opslaan op schijf
141140 function.managetilecache=Beheer tegelcache
142141 function.getweatherforecast=Ophalen weersvoorspelling
142 function.setaltitudetolerance=Instellen hoogtetolerantie
143143
144144 # Dialogs
145145 dialog.exit.confirm.title=GpsPrune afsluiten
378378 dialog.gpsies.activity.skating=Skating
379379 dialog.wikipedia.column.name=Artikelnaam
380380 dialog.wikipedia.column.distance=Afstand
381 dialog.wikipedia.nonefound=Geen punten gevonden
381382 dialog.correlate.notimestamps=Er zit geen tijdinformatie in de punten, dus kunnen ze niet aan foto's gekoppeld worden.
382383 dialog.correlate.nouncorrelatedphotos=Er zijn geen ongekoppelde foto's.\nWeet u zeker dat u wilt doorgaan?
383384 dialog.correlate.nouncorrelatedaudios=Er zijn geen ongekoppelde geluidsbestanden.\nWeet u zeker dat u wilt doorgaan?
409410 dialog.correlate.timestamp.middle=Midden
410411 dialog.correlate.timestamp.end=Einde
411412 dialog.correlate.audioselect.intro=Gebruk \u00e9\u00e9n van deze gecorreleerde audiobestanden als tijdsveschil
412 dialog.correlate.select.audioname=Naam audiobestsnd
413 dialog.correlate.select.audioname=Naam audiobestand
413414 dialog.correlate.select.audiolater=Audio later
415 dialog.rearrangewaypoints.desc=Selecteer doel en sorteervolgorde van de waypoints
414416 dialog.rearrangephotos.desc=Selecteer doel en sorteervolgorde van de foto punten
415 dialog.rearrangephotos.tostart=Naar begin
416 dialog.rearrangephotos.toend=Naar einde
417 dialog.rearrangephotos.nosort=Niet sorteren
418 dialog.rearrangephotos.sortbyfilename=Sorteren op bestandsnaam
419 dialog.rearrangephotos.sortbytime=Sorteren op tijd
417 dialog.rearrange.tostart=Naar begin
418 dialog.rearrange.toend=Naar einde
419 dialog.rearrange.tonearest=Naar dichtstbijzijnde routepunt
420 dialog.rearrange.nosort=Niet sorteren
421 dialog.rearrange.sortbyfilename=Sorteren op bestandsnaam
422 dialog.rearrange.sortbyname=Sorteren op naam
423 dialog.rearrange.sortbytime=Sorteren op tijd
420424 dialog.compress.closepoints.title=Verwijder nabijliggende punten
421425 dialog.compress.closepoints.paramdesc=Bereik
422426 dialog.compress.wackypoints.title=Vreemde punten verwijderen
514518 dialog.colourchooser.red=Rood
515519 dialog.colourchooser.green=Groen
516520 dialog.colourchooser.blue=Blauw
521 dialog.colourer.intro=Een puntinkleuring geeft routepunten verschillende kleuren
522 dialog.colourer.type=Type inkleuring
523 dialog.colourer.type.none=Geen
524 dialog.colourer.type.byfile=Op bestand
525 dialog.colourer.type.bysegment=Op segment
526 dialog.colourer.type.byaltitude=Op hoogte
527 dialog.colourer.type.byspeed=Op snelheid
528 dialog.colourer.type.byvertspeed=Op verticale snelheid
529 dialog.colourer.type.bygradient=Op stijgingspercentage
530 dialog.colourer.type.bydate=Op datum
531 dialog.colourer.start=Beginkleur
532 dialog.colourer.end=Eindkleur
533 dialog.colourer.maxcolours=Maximum aantal kleuren
517534 dialog.setlanguage.firstintro=U kunt kiezen uit \u00e9\u00e9n van de meegeleverde talen,<p>of selecteer een tekstbestand.
518535 dialog.setlanguage.secondintro=U dient uw instellingen op te slaan en<p>GpsPrune te herstarten om de taal te kunnen wijzigen
519536 dialog.setlanguage.language=Taal
559576 dialog.weather.day.friday=Vrijdag
560577 dialog.weather.day.saturday=Zaterdag
561578 dialog.weather.day.sunday=Zondag
579 dialog.weather.wind=Wind
580 dialog.weather.temp=Temp
562581 dialog.weather.humidity=Luchtvocht.
563582 dialog.weather.creditnotice=Deze gegevens worden beschikbaar gesteld door openweathermap.org. Hun website heeft meer details.
583 dialog.deletebydate.onlyonedate=Alle punten werden op dezelfde datum opgenomen.
584 dialog.deletebydate.intro=Je kan voor iedere datum in de route kiezen of je de punten wilt verwijderen of behouden
585 dialog.deletebydate.nodate=Geen tijden
586 dialog.deletebydate.column.keep=Behouden
587 dialog.deletebydate.column.delete=Verwijderen
588 dialog.setaltitudetolerance.text.metres=Grens (in meters) waaronder kleine klimmen en afdalingen worden genegeerd
589 dialog.setaltitudetolerance.text.feet=Grens (in feet) waaronder kleine klimmen en afdalingen worden genegeerd
564590
565591 # 3d window
566592 dialog.3d.title=GpsPrune in 3D
715741 fieldname.altitude=Hoogte
716742 fieldname.timestamp=Tijd
717743 fieldname.time=Tijd
744 fieldname.date=Datum
718745 fieldname.waypointname=Naam
719746 fieldname.waypointtype=Type
720747 fieldname.newsegment=Segment
756783 units.deg=Graden
757784 units.iso8601=ISO 8601
758785 units.degreescelsius=Celsius
786 units.degreescelsius.short=\u00baC
759787 units.degreesfahrenheit=Fahrenheit
788 units.degreesfahrenheit.short=\u00baF
760789
761790 # How to combine conditions, such as filters
762791 logic.and=en
847876 error.cache.notthere=De tegelcache map niet gevonden
848877 error.cache.empty=De tegelcache map is leeg
849878 error.cache.cannotdelete=Er konden geen tegels verwijderd worden
850 error.interpolate.invalidparameter=Aantal punten moet tussen 1 en 1000 liggen
851879 error.learnestimationparams.failed=Kan geen parameters bepalen van deze route.\nProbeer meer routes te laden.
852880 error.tracksplit.nosplit=Deze route kon niet opgedeeld worden
853881 error.downloadsrtm.nocache=De bestanden konden niet worden opgeslagen.\nControleer de schijfcache.
66 menu.file.recentfiles=Ostatnio u\u017cywane
77 menu.file.save=Zapisz
88 menu.file.exit=Zako\u0144cz
9 menu.online=Online
910 menu.track=\u015acie\u017cka
1011 menu.track.undo=Cofnij
1112 menu.track.clearundo=Wyczy\u015b\u0107 list\u0119 zmian
1213 menu.track.markrectangle=Zaznaczenie prostok\u0105tne
1314 menu.track.deletemarked=Usu\u0144 zaznaczone punkty
14 menu.track.rearrange=Zmie\u0144 kolejno\u015b\u0107 punkt\u00f3w po\u015brednich
15 menu.track.rearrange.start=Wszystkie na pocz\u0105tek \u015bcie\u017cki
16 menu.track.rearrange.end=Wszystkie na koniec \u015bcie\u017cki
17 menu.track.rearrange.nearest=Do najbli\u017cszego punktu
1815 menu.range=Zakres
1916 menu.range.all=Zaznacz wszystko
2017 menu.range.none=Usu\u0144 zaznaczenie
2219 menu.range.end=Zaznacz koniec zakresu
2320 menu.range.average=U\u015brednij zaznaczenie
2421 menu.range.reverse=Odwr\u00f3\u0107 zakres
25 menu.range.mergetracksegments=Po\u0142\u0105cz fragmenty \u015bcie\u017cek
22 menu.range.mergetracksegments=Scal fragmenty \u015bcie\u017cek
2623 menu.range.cutandmove=Wytnij i przesu\u0144 zaznaczenie
2724 menu.point=Punkt
2825 menu.point.editpoint=Edytuj punkt
5653
5754 # Alt keys for menus
5855 altkey.menu.file=P
56 altkey.menu.online=O
5957 altkey.menu.track=C
6058 altkey.menu.range=Z
6159 altkey.menu.point=U
8987 function.deleterange=Usu\u0144 zakres
9088 function.croptrack=Przytnij \u015bcie\u017ck\u0119
9189 function.interpolate=Wstaw pomi\u0119dzy punkty
90 function.deletebydate=Usu\u0144 punkty wed\u0142ug daty
9291 function.addtimeoffset=Dodaj przesuni\u0119cie czasu
9392 function.addaltitudeoffset=Dodaj przesuni\u0119cie wysoko\u015bci
93 function.rearrangewaypoints=Zmie\u0144 kolejno\u015b\u0107 punkt\u00f3w po\u015brednich
9494 function.convertnamestotimes=Zamie\u0144 nazwy punkt\u00f3w na czas
9595 function.deletefieldvalues=Usu\u0144 warto\u015bci
9696 function.findwaypoint=Znajd\u017a punkt po\u015bredni
103103 function.learnestimationparams=Skoryguj wsp\u00f3\u0142czynniki szacowania czasu
104104 function.setmapbg=Wybierz map\u0119 t\u0142a
105105 function.setpaths=Ustaw \u015bcie\u017cki do program\u00f3w
106 function.selectsegment=Wybierz bie\u017c\u0105cy fragment
107 function.splitsegments=Podziel \u015bcie\u017ck\u0119 na fragmenty
108 function.sewsegments=Po\u0142\u0105cz fragmenty
106109 function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies
107110 function.uploadgpsies=Wy\u015blij \u015bcie\u017cki do Gpsies
108111 function.lookupsrtm=Pobierz wysoko\u015bci z SRTM
135138 function.saveconfig=Zapisz ustawienia
136139 function.diskcache=Zapisz mapy na dysk
137140 function.managetilecache=Zarz\u0105dzaj keszem p\u0142ytek
138 function.getweatherforecast=Pobierz prognoza pogody
141 function.getweatherforecast=Pobierz prognoz\u0119 pogody
142 function.setaltitudetolerance=Ustaw tolerancj\u0119 wysoko\u015bci
139143
140144 # Dialogs
141145 dialog.exit.confirm.title=Zako\u0144cz GpsPrune
243247 dialog.exportpov.ballsandsticks=Kule i pa\u0142ki
244248 dialog.exportpov.tubesandwalls=Rurki i \u015bciany
245249 dialog.3d.warningtracksize=Ta \u015bcie\u017cka ma bardzo wiele punkt\u00f3w, kt\u00f3rych Java3D mo\u017ce nie wy\u015bwietli\u0107.\nCzy chcesz kontynuowa\u0107?
250 dialog.3d.useterrain=Poka\u017c teren
251 dialog.3d.terraingridsize=Rozmiar siatki
246252 dialog.exportpov.baseimage=Obraz podk\u0142adu
247253 dialog.exportpov.cannotmakebaseimage=Nie mo\u017cna zapisa\u0107 obrazu podk\u0142adu
248254 dialog.baseimage.title=Obrazu podk\u0142adu
372378 dialog.gpsies.activity.skating=Wrotki/rolki
373379 dialog.wikipedia.column.name=Tytu\u0142 artyku\u0142u
374380 dialog.wikipedia.column.distance=Odleg\u0142o\u015b\u0107
381 dialog.wikipedia.nonefound=Brak wpis\u00f3w w wikipedii
375382 dialog.correlate.notimestamps=Punkty nie maj\u0105 znacznik\u00f3w czasu, nie mo\u017cna ich powi\u0105za\u0107 ze zdj\u0119ciami.
376383 dialog.correlate.nouncorrelatedphotos=Nie ma nie powi\u0105zanych zdj\u0119\u0107.\nCzy na pewno chcesz kontynuowa\u0107?
377384 dialog.correlate.nouncorrelatedaudios=Nie ma nie powi\u0105zanych plik\u00f3w audio.\nCzy na pewno chcesz kontynuowa\u0107?
405412 dialog.correlate.audioselect.intro=Wybierz jeden z powi\u0105zanych plik\u00f3w audio i u\u017cyj go jako wzorca do przesuni\u0119cia czasu
406413 dialog.correlate.select.audioname=nazwa pliku audio
407414 dialog.correlate.select.audiolater=p\u00f3\u017aniejszy plik audio
415 dialog.rearrangewaypoints.desc=Wybierz przeznaczenie i porz\u0105dek sortowania punkt\u00f3w po\u015brednich
408416 dialog.rearrangephotos.desc=Wybierz przeznaczenie i porz\u0105dek sortowania punkt\u00f3w ze zdj\u0119ciami
409 dialog.rearrangephotos.tostart=Przesu\u0144 na pocz\u0105tek
410 dialog.rearrangephotos.toend=Przesu\u0144 na koniec
411 dialog.rearrangephotos.nosort=Nie sortuj
412 dialog.rearrangephotos.sortbyfilename=Sortuj po nazwie pliku
413 dialog.rearrangephotos.sortbytime=Sortuj wed\u0142ug czasu
417 dialog.rearrange.tostart=Przesu\u0144 na pocz\u0105tek
418 dialog.rearrange.toend=Przesu\u0144 na koniec
419 dialog.rearrange.tonearest=Do najbli\u017cszego punktu
420 dialog.rearrange.nosort=Nie sortuj
421 dialog.rearrange.sortbyfilename=Sortuj po nazwie pliku
422 dialog.rearrange.sortbyname=Sortuj po nazwie
423 dialog.rearrange.sortbytime=Sortuj wed\u0142ug czasu
414424 dialog.compress.closepoints.title=Usuwanie bliskich sobie punkt\u00f3w
415425 dialog.compress.closepoints.paramdesc=Wsp\u00f3\u0142czynnik rozpi\u0119to\u015bci
416426 dialog.compress.wackypoints.title=Usuwanie dziwacznych punkt\u00f3w
508518 dialog.colourchooser.red=Czerwony
509519 dialog.colourchooser.green=Zielony
510520 dialog.colourchooser.blue=Niebieski
521 dialog.colourer.intro=Koloryzer punkt\u00f3w, zmienia kolor punkt\u00f3w \u015bcie\u017cki
522 dialog.colourer.type=Tryb koloryzera
523 dialog.colourer.type.none=\u017baden
524 dialog.colourer.type.byfile=wed\u0142ug pliku
525 dialog.colourer.type.bysegment=wed\u0142ug segmentu
526 dialog.colourer.type.byaltitude=wed\u0142ug wysoko\u015bci
527 dialog.colourer.type.byspeed=wed\u0142ug pr\u0119dko\u015bci
528 dialog.colourer.type.byvertspeed=wed\u0142ug pr\u0119dko\u015bci pionowej
529 dialog.colourer.type.bygradient=wed\u0142ug nachylenia
530 dialog.colourer.type.bydate=wed\u0142ug daty
531 dialog.colourer.start=Kolor pocz\u0105tkowy
532 dialog.colourer.end=Kolor ko\u0144cowy
533 dialog.colourer.maxcolours=Maksymalna liczba kolor\u00f3w
511534 dialog.setlanguage.firstintro=Mo\u017cesz wybra\u0107 jeden z do\u0142\u0105czonych j\u0119zyk\u00f3w<p>Albo wybra\u0107 wybrany przez siebie plik tekstowy.
512535 dialog.setlanguage.secondintro=B\u0119dziesz musia\u0142 zapisa\u0107 ustawienia<p>i zrestartowa\u0107 GpsPrune by zmieni\u0107 j\u0119zyk.
513536 dialog.setlanguage.language=J\u0119zyk
535558 dialog.setlinewidth.text=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek
536559 dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM:
537560 dialog.searchwikipedianames.search=Szukaj
561 dialog.weather.location=Pozycja
562 dialog.weather.update=Prognoza zaktualizowana
563 dialog.weather.sunrise=Wsch\u00f3d s\u0142o\u0144ca
564 dialog.weather.sunset=Zach\u00f3d s\u0142o\u0144ca
565 dialog.weather.temperatureunits=Jednostki temperatury
566 dialog.weather.currentforecast=Bie\u017c\u0105ca pogoda
567 dialog.weather.dailyforecast=Prognoza dobowa
568 dialog.weather.3hourlyforecast=Prognoza na trzy godziny
538569 dialog.weather.day.now=Aktualny
539570 dialog.weather.day.today=Dzisiaj
540571 dialog.weather.day.tomorrow=Jutro
541572 dialog.weather.day.monday=Poniedzia\u0142ek
542573 dialog.weather.day.tuesday=Wtorek
543 dialog.weather.day.wednesday=\u015Aroda
574 dialog.weather.day.wednesday=\u015aroda
544575 dialog.weather.day.thursday=Czwartek
545576 dialog.weather.day.friday=Pi\u0105tek
546577 dialog.weather.day.saturday=Sobota
547578 dialog.weather.day.sunday=Niedziela
579 dialog.weather.wind=Wiatr
580 dialog.weather.temp=Temp
581 dialog.weather.humidity=Wilgotno\u015b\u0107
582 dialog.weather.creditnotice=Dane na podstawie openweathermap.org. Wi\u0119cej informacji na ich stronie.
583 dialog.deletebydate.onlyonedate=Te punkty zosta\u0142y zarejestrowane w tym samym czasie.
584 dialog.deletebydate.intro=Dla ka\u017cdej daty w \u015bcie\u017cce, mo\u017cesz wybra\u0107 czy usun\u0105\u0107 czy zostawi\u0107 punkty
585 dialog.deletebydate.nodate=Brak znacznika czasu.
586 dialog.deletebydate.column.keep=Zostaw
587 dialog.deletebydate.column.delete=Usu\u0144
588 dialog.setaltitudetolerance.text.metres=Limit (w metrach) poni\u017cej kt\u00f3rego, ma\u0142e spadki wzniosy b\u0119d\u0105 ignorowane
589 dialog.setaltitudetolerance.text.feet=Limit (w stopach) poni\u017cej kt\u00f3rego, ma\u0142e spadki wzniosy b\u0119d\u0105 ignorowane
548590
549591 # 3d window
550592 dialog.3d.title=GpsPrune widok tr\u00f3jwymiarowy
563605 confirm.addaltitudeoffset=Dodano przesuni\u0119cie wysoko\u015bci
564606 confirm.rearrangewaypoints=Przestawiono punkty po\u015brednie
565607 confirm.rearrangephotos=Zmieniono kolejno\u015b\u0107 zdj\u0119\u0107
608 confirm.splitsegments=
609 confirm.sewsegments=Po\u0142\u0105czono %d fragmenty/\u00f3w
566610 confirm.cutandmove=Przesuni\u0119to zaznaczenie
567611 confirm.interpolate=Dodano punkty
568612 confirm.convertnamestotimes=Zmieniono nazwy punkt\u00f3w po\u015brednich
581625 confirm.rotatephoto=obr\u00f3cono zdj\u0119cie
582626 confirm.running=Przetwarzam dane ...
583627 confirm.lookupsrtm=Znaleziono %d warto\u015bci wysoko\u015bci
628 confirm.downloadsrtm=Pobrano %d plik\u00f3w do kesza
629 confirm.downloadsrtm.1=Pobrano %d plik do kesza
630 confirm.downloadsrtm.none=Nie pobrano \u017cadnych plik\u00f3w, wszystkie by\u0142y ju\u017c w keszu
584631 confirm.deletefieldvalues=Warto\u015bci p\u00f3l usuni\u0119to
585632 confirm.audioload=dodano pliki audio
586633 confirm.correlateaudios.single=audio zosta\u0142o po\u0142\u0105czone
587634 confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone
588635
589 # Tips
636 # Tips, shown just once when appropriate
590637 tip.title=Porada
638 tip.useamapcache=Konfiguruj\u0105c kesz dyskowy (Ustawienia -> Zapisz mapy na dysk)\nprzyspieszasz wy\u015bwietlanie i ograniczasz ruch sieciowy
639 tip.learntimeparams=Resultat b\u0119dzie dok\u0142adniejszy je\u015bli u\u017cyjesz
640 tip.downloadsrtm=Mo\u017cesz przyspieszy\u0107 operacj\u0119 wywo\u0142uj\u0105c polecenie
641 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.
591642 tip.manuallycorrelateone=Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie.
592643
593644 # Buttons
690741 fieldname.altitude=Wysoko\u015b\u0107
691742 fieldname.timestamp=Czas
692743 fieldname.time=Czas
744 fieldname.date=Data
693745 fieldname.waypointname=Nazwa
694746 fieldname.waypointtype=Typ
695747 fieldname.newsegment=Odcinek
763815 undo.deletemarked=usu\u0144 punkty
764816 undo.insert=wstaw punkty
765817 undo.reverse=odwr\u00f3\u0107 zakres
766 undo.mergetracksegments=po\u0142\u0105cz fragmenty \u015bcie\u017cki
818 undo.mergetracksegments=scal fragmenty \u015bcie\u017cki
819 undo.splitsegments=podziel \u015bcie\u017ck\u0119 na fragmenty
820 undo.sewsegments=po\u0142\u0105cz fragmenty \u015bcie\u017cki
767821 undo.addtimeoffset=dodaj przesuni\u0119cie czasowe
768822 undo.addaltitudeoffset=dodaj przeuni\u0119cie wysoko\u015bci
769823 undo.rearrangewaypoints=przestaw punkty po\u015brednie
822876 error.cache.notthere=Nie znaleziono katalogu kesza
823877 error.cache.empty=Katalog kesza jest pusty
824878 error.cache.cannotdelete=\u017badne p\u0142ytki nie mog\u0142y zosta\u0107 usuni\u0119te
825 error.interpolate.invalidparameter=Ilo\u015b\u0107 punkt\u00f3w musi zawiera\u0107 si\u0119 w zakresie od 1 do 1000
826879 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.
880 error.tracksplit.nosplit=Nie mo\u017cna podzieli\u0107 \u015bcie\u017cki
881 error.downloadsrtm.nocache=Nie mo\u017cna zapisa\u0107 plik\u00f3w\nSprawd\u017a ustawienia kesza
882 error.sewsegments.nothingdone=Nie mo\u017cna po\u0142\u0105czy\u0107 fragment\u00f3w\nW \u015bcie\u017cce jest teraz %d fragment\u00f3w.
1212 menu.track.clearundo=Limpar lista de desfazer
1313 menu.track.markrectangle=Marcar pontos no ret\u00e2ngulo
1414 menu.track.deletemarked=Remover pontos marcados
15 menu.track.rearrange=Rearrumar pontos
16 menu.track.rearrange.start=Tudo para o in\u00edcio do arquivo
17 menu.track.rearrange.end=Tudo para o fim do arquivo
18 menu.track.rearrange.nearest=Cada um para o ponto da rota mais pr\u00f3ximo
15 function.rearrangewaypoints=Rearrumar pontos
1916 menu.range=Intervalo
2017 menu.range.all=Selecionar tudo
2118 menu.range.none=Desmarcar todas as sele\u00e7\u00f5es
9188 function.deleterange=Remover intervalo
9289 function.croptrack=Cortar rota
9390 function.interpolate=Interpolar pontos
91 function.deletebydate=Remover pontos de acordo com a data
9492 function.addtimeoffset=Adicionar diferen\u00e7a de tempo
9593 function.addaltitudeoffset=Adicionar diferen\u00e7a de altitude
9694 function.convertnamestotimes=Converter nomes dos pontos para tempos
105103 function.learnestimationparams=Aprender os par\u00e2metros para estimativa de tempo
106104 function.setmapbg=Definir como fundo do mapa
107105 function.setpaths=Definir caminhos do programa
106 function.selectsegment=Selecionar segmento atual
108107 function.splitsegments=Dividir rota em segmentos
109108 function.sewsegments=Reunir segmentos em rota
110109 function.getgpsies=Obter rotas Gpsies
412411 dialog.correlate.select.audioname=Nome do \u00e1udio
413412 dialog.correlate.select.audiolater=\u00c1udio posterior
414413 dialog.rearrangephotos.desc=Selecione o destino e a ordena\u00e7\u00e3o dos pontos das fotos
415 dialog.rearrangephotos.tostart=Mover para o in\u00edcio
416 dialog.rearrangephotos.toend=Mover para o fim
417 dialog.rearrangephotos.nosort=N\u00e3o ordenar
418 dialog.rearrangephotos.sortbyfilename=Ordenar pelo nome do arquivo
419 dialog.rearrangephotos.sortbytime=Ordenar pela hora
414 dialog.rearrange.tostart=Mover para o in\u00edcio
415 dialog.rearrange.toend=Mover para o fim
416 dialog.rearrange.tonearest=Cada um para o ponto da rota mais pr\u00f3ximo
417 dialog.rearrange.nosort=N\u00e3o ordenar
418 dialog.rearrange.sortbyfilename=Ordenar pelo nome do arquivo
419 dialog.rearrange.sortbyname=Ordenar pelo nome
420 dialog.rearrange.sortbytime=Ordenar pela hora
420421 dialog.compress.closepoints.title=Remo\u00e7\u00e3o de ponto pr\u00f3ximo
421422 dialog.compress.closepoints.paramdesc=Fator de deslocamento
422423 dialog.compress.wackypoints.title=Remo\u00e7\u00e3o de ponto exc\u00eantrica
851852 error.cache.notthere=A paste de cache de fundos n\u00e3o foi encontrada
852853 error.cache.empty=A pasta de cache de fundos est\u00e1 vazia
853854 error.cache.cannotdelete=Nenhum fundo pode ser removido
854 error.interpolate.invalidparameter=O n\u00famero de pontos deve estar entre 1 e 1000
855855 error.learnestimationparams.failed=N\u00e3o foi poss\u00edvel aprender par\u00e2metros desta rota.\nTente baixar mais rotas.
856856 error.tracksplit.nosplit=A rota n\u00e3o pode ser dividida.
857857 error.downloadsrtm.nocache=Os arquivos n\u00e3o puderam ser salvos.\nPor favor, verifique a cache do disco.
55 menu.file.addphotos=Adaug\u0103 foto
66 menu.file.recentfiles=Fi\u015fiere recente
77 menu.file.save=Salvare
8 menu.file.exit=Iesire
8 menu.file.exit=Ie\u015fire
9 menu.online=Internet
910 menu.track=Traseu
1011 menu.track.undo=Anulare
1112 menu.track.clearundo=\u015etergere lista de anulari
1213 menu.track.deletemarked=\u015etergere puncte marcate
13 menu.track.rearrange=Rearanjare waypoint
14 menu.track.rearrange.start=Toate la inceputul fi\u015fierului
15 menu.track.rearrange.end=Toate la sfarsitul fi\u015fierului
16 menu.track.rearrange.nearest=Fiecare la punctul cel mai apropiat al traseului
1714 menu.range=Interval
1815 menu.range.all=Selectare toate
1916 menu.range.none=Nu selecta niciun punct
2017 menu.range.start=Seteaza inceputul selectiei
2118 menu.range.end=Seteaza sfarsitul selectiei
22 menu.range.average=Mediere selectie
23 menu.range.reverse=Inversare selectie
19 menu.range.average=Mediere selec\u0163ie
20 menu.range.reverse=Inversare selec\u0163ie
2421 menu.range.mergetracksegments=Unire segmente traseu
25 menu.range.cutandmove=Taiere si mutare selectie
22 menu.range.cutandmove=Taiere si mutare selec\u0163ie
2623 menu.point=Punct
2724 menu.point.editpoint=Editare punct
2825 menu.point.deletepoint=\u015etergere punct
3734 menu.view.browser.yahoo=Harti Yahoo
3835 menu.view.browser.bing=Harti Bing
3936 menu.settings=Set\u0103ri
37 menu.settings.onlinemode=\u00cencarc\u0103 h\u0103r\u021bi
4038 menu.help=Ajutor
4139 # Popup menu for map
4240 menu.map.zoomin=Apropie
5250
5351 # Alt keys for menus
5452 altkey.menu.file=F
53 altkey.menu.online=N
5554 altkey.menu.track=T
5655 altkey.menu.range=I
5756 altkey.menu.point=P
8685 function.interpolate=Interpolare
8786 function.addtimeoffset=Adaug\u0103 decalaj timp
8887 function.addaltitudeoffset=Adaug\u0103 decalaj altitudine
88 function.rearrangewaypoints=Rearanjare waypoint
8989 function.findwaypoint=Gasire waypoint
9090 function.charts=Grafice
9191 function.show3d=Vizualizare arborescenta
9292 function.distances=Distan\u0163e
9393 function.fullrangedetails=Informa\u0163ie complet
94 function.loadaudio=Adaug\u0103 audio
95 function.setmapbg=Fundal
94 function.getgpsies=\u00cencarc\u0103 trassee Gpsies
95 function.uploadgpsies=Trimite date spre Gpsies
96 function.downloadsrtm=\u00cencarc\u0103 date SRTM
97 function.estimatetime=Estimare durat\u0103
98 function.setmapbg=Seteaza harta
99 function.selectsegment=Selectare segment curent
96100 function.setcolours=Selectare culorile
97101 function.setlanguage=Selectare limba
98102 function.connecttopoint=Conecteaza la punct
99103 function.disconnectfrompoint=Deconecteaza de la punct
100104 function.removephoto=Elimina foto
101105 function.correlatephotos=Corelare fotografii
106 function.rearrangephotos=Rearanjare fotografii
107 function.rotatephotoleft=Roti foto la st\u00e2nga
108 function.rotatephotoright=Roti foto la dreapta
109 function.photopopup=Arat\u0103 foto
110 function.loadaudio=Adaug\u0103 audio
111 function.removeaudio=Elimina audio
112 function.playaudio=Redare audio
102113 function.help=Ajutor
103114 function.showkeys=Arat\u0103 tastele scurt\u0103turi
104115 function.about=Despre GpsPrune
105116 function.checkversion=Verific\u0103 pentru o versiune noua
106117 function.saveconfig=Salvare set\u0103ri
118 function.diskcache=Salvare harti
107119 function.getweatherforecast=Prognoz\u0103 meteo
108120
109121 # Dialogs
113125 dialog.openappend.text=Adauga la datele deja incarcate?
114126 dialog.deletepoint.title=\u015eterge Punct
115127 dialog.deletepoint.deletephoto=\u015eterg fotografiile atasate acestui punct?
116 dialog.deletephoto.title=\u015eterge Foto
128 dialog.deletephoto.title=\u015eterge foto
117129 dialog.deletephoto.deletepoint=\u015eterg punct atasat acestei fotografii?
130 dialog.deleteaudio.deletepoint=\u015eterg punct atasat acestei audio?
118131 dialog.openoptions.title=Optiuni deschidere
119132 dialog.load.table.field=Cimp
120133 dialog.load.table.datatype=Tip data
128141 dialog.openoptions.deliminfo.records=inregistrari, cu
129142 dialog.openoptions.deliminfo.fields=cimpuri
130143 dialog.openoptions.deliminfo.norecords=Nu sunt inregistrari
144 dialog.openoptions.altitudeunits=Unit\u0103\u0163i de altitudini
145 dialog.openoptions.speedunits=Unit\u0103\u0163i de viteza
146 dialog.openoptions.vertspeedunits=Unit\u0103\u0163i de viteza vertical\u0103
131147 dialog.selecttracks.noname=F\u0103r\u0103 nume
132148 dialog.jpegload.subdirectories=Include subdirectori
133149 dialog.jpegload.loadjpegswithoutcoords=Include fotografii fara coordonate
134150 dialog.jpegload.loadjpegsoutsidearea=Include fotografii din afara zonei curente
135 dialog.jpegload.progress.title=Incarcare fotografii
151 dialog.jpegload.progress.title=\u00cenc\u0103rcare fotografii
136152 dialog.jpegload.progress=Va rog sa asteptati, caut fotografiile
137153 dialog.gpsload.nogpsbabel=Nu gasesc programul gpsbabel. Continui ?
138154 dialog.gpsload.device=Nume dispozitiv
139155 dialog.gpsload.format=Format
140 dialog.gpsload.getwaypoints=Incarcare waypoints
156 dialog.gpsload.getwaypoints=\u00cencarc\u0103 waypoints
157 dialog.gpsload.gettracks=\u00cencarc\u0103 trasee
158 dialog.gpsload.save=Salvare fi\u015fier
159 dialog.gpssend.sendwaypoints=Trimite waypoints
160 dialog.gpssend.sendtracks=Trimite trasee
141161 dialog.gpssend.trackname=Nume traseu
142162 dialog.gpsbabel.filters=Filtre
143 dialog.gpsbabel.filter.simplify=Simplifica
163 dialog.addfilter.title=Adaug\u0103 filtru
164 dialog.gpsbabel.filter.discard=Arunc\u0103
165 dialog.gpsbabel.filter.simplify=Simplific\u0103
144166 dialog.gpsbabel.filter.distance=Distan\u0163\u0103
167 dialog.gpsbabel.filter.interpolate=Interpolare
168 dialog.gpsbabel.filter.discard.intro=Arunc\u0103 puncte dac\u0103
169 dialog.gpsbabel.filter.discard.hdop=Hdop >
170 dialog.gpsbabel.filter.discard.vdop=Vdop >
145171 dialog.gpsbabel.filter.discard.numsats=Num\u0103r de sateli\u0163i <
172 dialog.gpsbabel.filter.simplify.maxpoints=Num\u0103r de puncte <
173 dialog.gpsbabel.filter.simplify.length=diferen\u0163\u0103 de lungime
174 dialog.gpsbabel.filter.distance.distance=Dac\u0103 distan\u0163\u0103 <
175 dialog.gpsbabel.filter.distance.time=\u0219i diferen\u0163\u0103 de timp <
176 dialog.gpsbabel.filter.interpolate.distance=Dac\u0103 distan\u0163\u0103 >
177 dialog.gpsbabel.filter.interpolate.time=sau diferen\u0163\u0103 de timp >
146178 dialog.saveoptions.title=Salvare fi\u015fier
147179 dialog.save.table.field=Cimp
180 dialog.save.table.hasdata=Date
148181 dialog.save.table.save=Salvare
182 dialog.save.coordinateunits=Format coordonate
183 dialog.save.altitudeunits=Unit\u0103\u0163i de altitudini
184 dialog.save.timestampformat=Format de timp
149185 dialog.save.overwrite.title=Fi\u015fierul exist\u0103
150186 dialog.save.overwrite.text=Fi\u015fierul exist\u0103. \u00cel suprascriu?
151187 dialog.exportkml.text=Titlu
188 dialog.exportkml.imagesize=Dimensiune imaginii
152189 dialog.exportkml.trackcolour=Culoarea liniei
153190 dialog.exportgpx.name=Nume
154191 dialog.exportgpx.desc=Descriere
192 dialog.exportgpx.encoding=Codare
155193 dialog.exportgpx.encoding.system=Sistem
156194 dialog.exportgpx.encoding.utf8=UTF-8
157195 dialog.exportpov.font=Fontul
163201 dialog.3d.terraingridsize=Dimensiune a grilei
164202 dialog.exportpov.baseimage=Imagine cartografice
165203 dialog.baseimage.title=Imagine cartografice
204 dialog.baseimage.zoom=Nivel de zoom
205 dialog.baseimage.incomplete=Imagine incomplet\u0103
166206 dialog.baseimage.tiles=Tigla
167 dialog.exportsvg.phi=Azimut \u03D5
168 dialog.exportsvg.theta=\u00cenclina\u0163ie \u03B8
207 dialog.exportsvg.phi=Azimut \u03d5
208 dialog.exportsvg.theta=\u00cenclina\u0163ie \u03b8
209 dialog.pointtype.track=Puncte de traseu
210 dialog.pointtype.waypoint=Waypoints
211 dialog.pointtype.photo=Puncte foto
212 dialog.pointtype.audio=Puncte audio
213 dialog.pointtype.selection=Doar interval
169214 dialog.undo.title=Anulare
215 dialog.pointedit.title=Editare punct
170216 dialog.pointedit.intro=V\u0103 rog selecta\u0163i r\u00e2ndul care va fi editat
171217 dialog.pointedit.table.field=Cimp
172218 dialog.pointedit.table.value=Valoare
219 dialog.pointnameedit.name=Nume de waypoint
173220 dialog.pointnameedit.uppercase=Litere MARI
174221 dialog.pointnameedit.lowercase=Litere mici
175222 dialog.addtimeoffset.days=Zile
176223 dialog.addtimeoffset.hours=Ore
177224 dialog.addtimeoffset.minutes=Minute
178225 dialog.findwaypoint.search=C\u0103utare
226 dialog.saveexif.table.photoname=Nume
227 dialog.saveexif.title=Salvare Exif
179228 dialog.saveexif.table.status=Stare
180229 dialog.saveexif.table.save=Salveaza
181230 dialog.saveexif.photostatus.connected=Conectat
184233 dialog.saveexif.overwrite=Suprascrie fi\u015fiere
185234 dialog.charts.xaxis=Axa X
186235 dialog.charts.yaxis=Axa Y
236 dialog.charts.output=Rezultat
237 dialog.charts.svgwidth=L\u0103\u021bime SVG
238 dialog.charts.svgheight=\u00cen\u0103l\u021bime SVG
239 dialog.distances.column.from=De punct
240 dialog.distances.column.to=Spre punct
187241 dialog.distances.currentpoint=Punct curent
242 dialog.estimatetime.details=Detalii
243 dialog.estimatetime.parameters=Parametrii
244 dialog.estimatetime.parameters.timefor=Durata pentru
245 dialog.estimatetime.results=Rezultate
246 dialog.estimatetime.results.estimatedtime=Durata estimat\u0103
247 dialog.estimatetime.results.actualtime=Durata (measured)
248 dialog.learnestimationparams.averageerror=Eroare estimat
249 dialog.learnestimationparams.combinedresults=Rezultate combinat
250 dialog.learnestimationparams.weight.current=curente
251 dialog.learnestimationparams.weight.calculated=calculate
252 dialog.addmapsource.sourcename=Nume
188253 dialog.addmapsource.noname=F\u0103r\u0103 nume
189254 dialog.gpsies.column.name=Nume
190255 dialog.gpsies.column.length=Lungime
191256 dialog.gpsies.description=Descriere
192257 dialog.gpsies.nodescription=F\u0103r\u0103 descriere
258 dialog.gpsies.nonefound=Nu a fost g\u0103sit
259 dialog.gpsies.username=Gpsies username
260 dialog.gpsies.password=Gpsies parol\u0103
261 dialog.gpsies.keepprivate=Traseu privat
262 dialog.gpsies.activities=Activit\u0103\u0163i
193263 dialog.wikipedia.column.name=Nume
194264 dialog.wikipedia.column.distance=Distan\u0163\u0103
265 dialog.wikipedia.nonefound=Nu a fost g\u0103sit
266 dialog.correlate.select.photoname=Nume
267 dialog.correlate.select.timediff=Diferenta de timp
195268 dialog.correlate.options.offset.hours=ore,
196269 dialog.correlate.options.offset.minutes=minute,
197270 dialog.correlate.options.offset.seconds=secunde
271 dialog.correlate.options.correlate=Corelare
272 dialog.correlate.timestamp.beginning=\u00cenceptutul
273 dialog.correlate.timestamp.middle=Mijlocul
274 dialog.correlate.timestamp.end=Sf\u00e2r\u015fitul
275 dialog.correlate.select.audioname=Nume
276 dialog.rearrange.tostart=Toate la inceputul fi\u015fierului
277 dialog.rearrange.toend=Toate la sfarsitul fi\u015fierului
278 dialog.rearrange.tonearest=Fiecare la punctul cel mai apropiat al traseului
279 dialog.rearrange.nosort=Nu sunt sortate
280 dialog.rearrange.sortbyfilename=Sorta dup\u0103 nume de fi\u015fier
281 dialog.rearrange.sortbyname=Sorta dup\u0103 nume
282 dialog.rearrange.sortbytime=Sorta dup\u0103 timp
198283 dialog.pastecoordinates.coords=Coordonate
199284 dialog.about.version=Versiunea
285 dialog.about.build=Construi
286 dialog.about.languages=Limbi
200287 dialog.about.systeminfo=Informa\u0163ii a sistemului
201288 dialog.about.systeminfo.os=Sistem de operare
289 dialog.about.systeminfo.java3d=Java3d instalat
290 dialog.about.systeminfo.povray=Povray instalat
291 dialog.about.systeminfo.exiftool=Exiftool instalat
292 dialog.about.systeminfo.gpsbabel=Gpsbabel instalat
293 dialog.about.systeminfo.gnuplot=Gnuplot instalat
202294 dialog.about.systeminfo.exiflib=Bibliotec\u0103 Exif
203295 dialog.about.systeminfo.exiflib.internal=Intern
204296 dialog.about.systeminfo.exiflib.internal.failed=Intern (absent)
209301 dialog.about.readme=Cite\u015fte-m\u0103
210302 dialog.checkversion.releasedate1=Aceasta versiune noua a fost lansapa pe
211303 dialog.checkversion.releasedate2=.
304 dialog.saveconfig.prune.languagecode=Limb\u0103 (RO)
305 dialog.saveconfig.prune.languagefile=Fi\u015fier de limba
306 dialog.saveconfig.prune.gpsdevice=Dispozitiv GPS
307 dialog.saveconfig.prune.gpsformat=Format GPS
212308 dialog.setcolours.background=Fund
213309 dialog.setcolours.lines=Linii
214310 dialog.setcolours.primary=Primar
215311 dialog.setcolours.secondary=Secundar
216312 dialog.setcolours.point=Puncte
313 dialog.setcolours.selection=Selec\u0163ie
217314 dialog.setcolours.text=Text
315 dialog.colourchooser.title=Selectare culoare
218316 dialog.colourchooser.red=Ro\u0219u
219317 dialog.colourchooser.green=Verde
220318 dialog.colourchooser.blue=Albastru
319 dialog.colourer.type.none=Nimic
320 dialog.setlanguage.language=Limb\u0103
321 dialog.setlanguage.languagefile=Fi\u015fier de limba
322 dialog.diskcache.table.tiles=Tigla
323 dialog.searchwikipedianames.search=C\u0103utare :
324 dialog.weather.location=Loca\u0163ie
325 dialog.weather.sunrise=R\u0103s\u0103rit
326 dialog.weather.sunset=Apus de soare
327 dialog.weather.currentforecast=Vremea curent\u0103
328 dialog.weather.day.now=Vremea curent\u0103
221329 dialog.weather.day.today=Ast\u0103zi
222330 dialog.weather.day.tomorrow=M\u00e2ine
223331 dialog.weather.day.monday=Luni
232340 confirm.loadfile=Date incarcate din fi\u015fier
233341 confirm.save.ok1=Salvat cu succes
234342 confirm.save.ok2=puncte \u00een
343 confirm.media.connect=foto/audio conectat
344 confirm.photo.disconnect=foto deconectat
345 confirm.audio.disconnect=audio deconectat
346 confirm.media.removed=\u0219ters
347 confirm.running=Executare ...
348 confirm.downloadsrtm=S-au desc\u0103rcat %d fi\u015fiere
349 confirm.downloadsrtm.1=S-au desc\u0103rcat %d fi\u015fier
235350
236351 # Tips
237352 tip.title=Indiciu
257372 button.selectall=Selecteaza tot
258373 button.selectnone=Deselecteaza tot
259374 button.load=Descarca
260 button.upload=Inc\u0103rca
375 button.upload=Trimite
261376 button.guessfields=Ghici cimpuri
262377 button.check=Verifica
263378 button.delete=\u015etergere
280395 details.trackdetails=Detalii traseul
281396 details.track.points=Puncte
282397 details.pointdetails=Detalii punctul
398 details.index.selected=Punct
399 details.index.of=de
400 details.photofile=Fi\u015fier
283401 details.rangedetails=Detalii intervalul
284402 details.range.selected=Selectat
285403 details.range.to=la
286404 details.altitude.to=la
405 details.range.climb=Urcare
406 details.range.descent=Cobor\u00e2re
287407 details.coordformat=Format coordonate
288 details.distanceunits=Unitati de distan\u0163e
408 details.distanceunits=Unit\u0103\u0163i de distan\u0163e
289409 display.range.time.secs=s
290410 display.range.time.mins=m
291411 display.range.time.hours=o
292412 display.range.time.days=z
293413 details.range.avespeed=Viteza medie
294414 details.range.maxspeed=Viteza maxim\u0103
415 details.range.numsegments=Num\u0103r de segmente
416 details.range.pace=Ritm
417 details.range.gradient=Gradient
418 details.lists.waypoints=Waypoints
295419 details.lists.photos=Foto-uri
296420 details.lists.audio=Audio
421 details.photodetails=Detalii foto
422 details.photo.loading=\u00cenc\u0103rcare
423 details.photo.bearing=Direc\u0163ie
424 details.media.connected=Conectat
297425 details.audiodetails=Detalii audio
426 details.audio.file=Fi\u015fier
298427
299428 # Field names
300429 fieldname.latitude=Latitudine
301430 fieldname.longitude=Longitudine
302431 fieldname.altitude=Altitudine
432 fieldname.timestamp=Timp
303433 fieldname.time=Timp
434 fieldname.date=Data
304435 fieldname.waypointname=Nume
305436 fieldname.waypointtype=Tip
306437 fieldname.newsegment=Segment
321452 units.kilometresperhour.short=km/o
322453 units.miles=Mil\u0103
323454 units.miles.short=mi
455 units.milesperhour=mil\u0103 pe or\u0103
456 units.milesperhour.short=mpo
324457 units.nauticalmiles=Mil\u0103 marin\u0103
325458 units.nauticalmiles.short=mm
326459 units.nauticalmilesperhour.short=kn
460 units.metrespersec=metri pe secund
461 units.metrespersec.short=m/s
327462 units.hours=ore
328463 units.minutes=minute
329464 units.seconds=secunde
465 units.degminsec=Grad-min-sec
466 units.degmin=Grad-min
467 units.deg=Grad
468 units.iso8601=ISO 8601
469 units.degreescelsius=Celsius
470 units.degreescelsius.short=\u00b0C
471 units.degreesfahrenheit=Fahrenheit
472 units.degreesfahrenheit.short=\u00b0F
330473
331474 # How to combine conditions, such as filters
332 logic.and=\ufeff\u0219i
475 logic.and=\u0219i
333476 logic.or=sau
477
478 # External urls
479 wikipedia.lang=ro
480 openweathermap.lang=ro
334481
335482 # Cardinals for 3d plots
336483 cardinal.n=N
338485 cardinal.e=E
339486 cardinal.w=V
340487
341 wikipedia.lang=ro
342 openweathermap.lang=ro
488 # Undo operations
489 undo.load=\u00cencarc\u0103 date
490 undo.loadphotos=\u00cencarc\u0103 fotografii
491 undo.loadaudios=\u00cencarc\u0103 audio
492 undo.editpoint=Editare punct
493 undo.deletepoint=\u015eterge punct
494 undo.removephoto=Elimina foto
495 undo.removeaudio=Elimina audio
496 undo.deleterange=\u015eterge interval
497 undo.deletemarked=\u015eterge puncte
498 undo.connect=conecteaza
499 undo.disconnect=deconecteaza
500 undo.rotatephoto=roti foto
501
502 # Error messages
503 error.function.notavailable.title=Func\u021bie indisponibil\u0103
1212 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
1313 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
1414 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
15 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
16 menu.track.rearrange.start=\u0412\u0441\u0435 \u0432 \u043d\u0430\u0447\u0430\u043b\u043e \u0444\u0430\u0439\u043b\u0430
17 menu.track.rearrange.end=\u0412\u0441\u0435 \u0432 \u043a\u043e\u043d\u0435\u0446 \u0444\u0430\u0439\u043b\u0430
18 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
15 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
1916 menu.range=\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b
2017 menu.range.all=\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435
2118 menu.range.none=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u044b\u0431\u043e\u0440\u043a\u0443
384381 dialog.correlate.select.audioname=\u0418\u043c\u044f \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438
385382 dialog.correlate.select.audiolater=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u0437\u0434\u043d\u0435\u0435
386383 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
387 dialog.rearrangephotos.tostart=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043d\u0430\u0447\u0430\u043b\u043e
388 dialog.rearrangephotos.toend=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0435\u0446
389 dialog.rearrangephotos.nosort=\u041d\u0435 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c
390 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
391 dialog.rearrangephotos.sortbytime=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438
384 dialog.rearrange.tostart=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043d\u0430\u0447\u0430\u043b\u043e
385 dialog.rearrange.toend=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0435\u0446
386 dialog.rearrange.tonearest=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u043a \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0439
387 dialog.rearrange.nosort=\u041d\u0435 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c
388 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
389 dialog.rearrange.sortbytime=\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438
392390 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
393391 dialog.compress.closepoints.paramdesc=\u0420\u0430\u0437\u043c\u0430\u0445
394392 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
793791 error.load.othererror=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438 \u0444\u0430\u0439\u043b\u0430:
794792 error.jpegload.dialogtitle=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0444\u043e\u0442\u043e
795793 error.jpegload.nofilesfound=\u0424\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
796 error.jpegload.nojpegsfound=JEPG-\u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
794 error.jpegload.nojpegsfound=JPEG-\u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
797795 error.jpegload.nogpsfound=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 GPS-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f
798796 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.
799797 error.audioload.nofilesfound=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b
819817 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
820818 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
821819 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
822 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
823820 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
1111 menu.track.clearundo=Rensa \u00e5ngra-historik
1212 menu.track.markrectangle=Markera punkter i rektangel
1313 menu.track.deletemarked=Radera markerade punkter
14 menu.track.rearrange=Ordna waypoints
15 menu.track.rearrange.start=Alla till b\u00f6rjan av fil
16 menu.track.rearrange.end=Alla till slut av fil
17 menu.track.rearrange.nearest=Varje till n\u00e4rmaste sp\u00e5rpunkt
14 function.rearrangewaypoints=Ordna waypoints
15 dialog.rearrange.tostart=Alla till b\u00f6rjan av fil
16 dialog.rearrange.toend=Alla till slut av fil
17 dialog.rearrange.tonearest=Varje till n\u00e4rmaste sp\u00e5rpunkt
1818 menu.range=Intervall
1919 menu.range.all=V\u00e4lj alla
2020 menu.range.none=V\u00e4lj ingen
1616 menu.range.average=Se\u00e7me ortala
1717 menu.range.reverse=S\u0131ra tersine \u00e7evir
1818 menu.range.mergetracksegments=\u0130z par\u00e7alar\u0131 birle\u015ftir
19 menu.track.rearrange=Yol noktalar\u0131 yeniden diz
20 menu.track.rearrange.start=Hepsini dosyan\u0131n ba\u015f\u0131na
21 menu.track.rearrange.end=Hepsini dosyan\u0131n sonuna
22 menu.track.rearrange.nearest=En yak\u0131n iz noktaya
19 function.rearrangewaypoints=Yol noktalar\u0131 yeniden diz
20 dialog.rearrange.tostart=Hepsini dosyan\u0131n ba\u015f\u0131na
21 dialog.rearrange.toend=Hepsini dosyan\u0131n sonuna
22 dialog.rearrange.tonearest=En yak\u0131n iz noktaya
2323 menu.range.cutandmove=Se\u00e7me kes ve ta\u015f\u0131
2424 menu.range=S\u0131ra
2525 menu.point=Nokta
1111 menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043c\u0456\u043d
1212 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
1313 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
14 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
15 menu.track.rearrange.start=\u0423\u0441\u0435 \u043d\u0430 \u043f\u043e\u0447\u0430\u0442\u043e\u043a \u0444\u0430\u0439\u043b\u0443
16 menu.track.rearrange.end=\u0423\u0441\u0435 \u043d\u0430 \u043a\u0456\u043d\u0435\u0446\u044c \u0444\u0430\u0439\u043b\u0443
17 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
14 function.rearrangewaypoints=\u041f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0438
15 dialog.rearrange.tostart=\u0423\u0441\u0435 \u043d\u0430 \u043f\u043e\u0447\u0430\u0442\u043e\u043a \u0444\u0430\u0439\u043b\u0443
16 dialog.rearrange.toend=\u0423\u0441\u0435 \u043d\u0430 \u043a\u0456\u043d\u0435\u0446\u044c \u0444\u0430\u0439\u043b\u0443
17 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
1818 menu.range=\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b
1919 menu.range.all=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u0443\u0441\u0456
2020 menu.range.none=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438 \u0432\u0438\u0431\u0456\u0440\u043a\u0443
1212 menu.track.clearundo=\u6e05\u9664\u64a4\u9500\u6e05\u5355
1313 menu.track.markrectangle=\u6807\u8bb0\u9009\u53d6\u533a\u57df\u5185\u7684\u70b9
1414 menu.track.deletemarked=\u5220\u9664\u5df2\u6807\u8bb0\u8f68\u8ff9\u70b9
15 menu.track.rearrange=\u91cd\u65b0\u6392\u5217\u822a\u70b9
16 menu.track.rearrange.start=\u81f3\u8d77\u59cb\u4f4d\u7f6e
17 menu.track.rearrange.end=\u81f3\u672b\u4f4d\u7f6e
18 menu.track.rearrange.nearest=\u81f3\u6700\u8fd1\u8f68\u8ff9\u70b9
1915 menu.range=\u822a\u6bb5
2016 menu.range.all=\u5168\u9009
2117 menu.range.none=\u64a4\u9500\u9009\u62e9
9187 function.deleterange=\u5220\u9664\u8f68\u8ff9\u70b9\u6bb5
9288 function.croptrack=\u4fee\u526a\u8f68\u8ff9
9389 function.interpolate=\u91cd\u53e0\u8f68\u8ff9\u70b9
90 function.deletebydate=\u6839\u636e\u65e5\u671f\u5220\u9664\u8f68\u8ff9\u70b9
9491 function.addtimeoffset=\u52a0\u5165\u65f6\u95f4\u5dee
9592 function.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb
93 function.rearrangewaypoints=\u91cd\u65b0\u6392\u5217\u822a\u70b9
9694 function.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4
9795 function.deletefieldvalues=\u5220\u9664\u5b57\u6bb5\u503c
9896 function.findwaypoint=\u67e5\u627e\u822a\u70b9
105103 function.learnestimationparams=\u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4
106104 function.setmapbg=\u80cc\u666f\u5730\u56fe
107105 function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84
106 function.selectsegment=\u9009\u4e2d\u5f53\u524d\u8f68\u8ff9\u6bb5
108107 function.splitsegments=\u5206\u5272\u8f68\u8ff9
109108 function.sewsegments=\u63a5\u5408\u8f68\u8ff9\u7247\u6bb5
110109 function.getgpsies=\u83b7\u53d6Gpsies\u8f68\u8ff9
140139 function.diskcache=\u4fdd\u5b58\u5730\u56fe
141140 function.managetilecache=\u7ba1\u7406\u5730\u56fe\u533a\u57df\u6570\u636e\u7f13\u5b58
142141 function.getweatherforecast=\u83b7\u53d6\u5929\u6c14\u9884\u62a5
142 function.setaltitudetolerance=\u8bbe\u7f6e\u9ad8\u5ea6\u516c\u5dee
143143
144144 # Dialogs
145145 dialog.exit.confirm.title=\u9000\u51fa
378378 dialog.gpsies.activity.skating=\u6ed1\u51b0
379379 dialog.wikipedia.column.name=\u6587\u7ae0\u9898\u76ee
380380 dialog.wikipedia.column.distance=\u8ddd\u79bb
381 dialog.wikipedia.nonefound=\u672a\u627e\u5230\u7ef4\u57fa\u767e\u79d1\u6761\u76ee
381382 dialog.correlate.notimestamps=\u6570\u636e\u70b9\u4e2d\u65e0\u65f6\u95f4\u4fe1\u606f\uff0c\u7167\u7247\u65e0\u6cd5\u94fe\u63a5
382383 dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u7167\u7247\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f
383384 dialog.correlate.nouncorrelatedaudios=\u6240\u6709\u97f3\u9891\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f
411412 dialog.correlate.audioselect.intro=\u9009\u62e9\u4ee5\u4e0b\u58f0\u97f3\u6587\u4ef6\u4f5c\u4e3a\u65f6\u95f4\u504f\u5dee
412413 dialog.correlate.select.audioname=\u58f0\u97f3\u6587\u4ef6\u540d\u5b57
413414 dialog.correlate.select.audiolater=\u58f0\u97f3\u5ef6\u8fdf
415 dialog.rearrangewaypoints.desc=\u9009\u62e9\u76ee\u7684\u5730\u5e76\u6392\u5217\u8def\u70b9
414416 dialog.rearrangephotos.desc=\u9009\u62e9\u76ee\u7684\u5730\u53ca\u7167\u7247\u70b9\u6392\u5217\u987a\u5e8f
415 dialog.rearrangephotos.tostart=\u79fb\u5230\u5f00\u59cb
416 dialog.rearrangephotos.toend=\u79fb\u5230\u672b\u5c3e
417 dialog.rearrangephotos.nosort=\u4e0d\u6392\u5e8f
418 dialog.rearrangephotos.sortbyfilename=\u6309\u540d\u79f0\u6392\u5e8f
419 dialog.rearrangephotos.sortbytime=\u6309\u65f6\u95f4\u6392\u5e8f
417 dialog.rearrange.tostart=\u79fb\u5230\u5f00\u59cb
418 dialog.rearrange.toend=\u79fb\u5230\u672b\u5c3e
419 dialog.rearrange.tonearest=\u81f3\u6700\u8fd1\u8f68\u8ff9\u70b9
420 dialog.rearrange.nosort=\u4e0d\u6392\u5e8f
421 dialog.rearrange.sortbyfilename=\u6309\u6587\u4ef6\u540d\u6392\u5e8f
422 dialog.rearrange.sortbyname=\u6309\u8def\u70b9\u540d\u6392\u5e8f
423 dialog.rearrange.sortbytime=\u6309\u65f6\u95f4\u6392\u5e8f
420424 dialog.compress.closepoints.title=\u90bb\u8fd1\u70b9\u5220\u9664
421425 dialog.compress.closepoints.paramdesc=\u8303\u56f4\u7cfb\u6570
422426 dialog.compress.wackypoints.title=\u5f02\u5e38\u70b9\u5220\u9664
514518 dialog.colourchooser.red=\u7ea2
515519 dialog.colourchooser.green=\u7eff
516520 dialog.colourchooser.blue=\u84dd
521 dialog.colourer.intro=\u53ef\u4ee5\u8d4b\u4e88\u8f68\u8ff9\u70b9\u4e0d\u540c\u7684\u989c\u8272
522 dialog.colourer.type=\u7740\u8272\u6a21\u5f0f
523 dialog.colourer.type.none=\u65e0
524 dialog.colourer.type.byfile=\u6309\u6587\u4ef6
525 dialog.colourer.type.bysegment=\u6309\u8f68\u8ff9\u6bb5
526 dialog.colourer.type.byaltitude=\u6309\u9ad8\u5ea6
527 dialog.colourer.type.byspeed=\u6309\u901f\u5ea6
528 dialog.colourer.type.byvertspeed=\u6309\u5782\u76f4\u901f\u5ea6
529 dialog.colourer.type.bygradient=\u6309\u5761\u5ea6
530 dialog.colourer.type.bydate=\u6309\u65e5\u671f
531 dialog.colourer.start=\u8d77\u59cb\u989c\u8272
532 dialog.colourer.end=\u7ed3\u675f\u989c\u8272
533 dialog.colourer.maxcolours=\u6700\u5927\u989c\u8272\u6570\u91cf
517534 dialog.setlanguage.firstintro=\u4f60\u53ef\u4ee5\u9009\u62e9\u5df2\u6709\u8bed\u8a00,<p>\u6216\u9009\u62e9\u5916\u6302\u8bed\u8a00\u5305
518535 dialog.setlanguage.secondintro=\u8bf7\u4fdd\u5b58\u8bbe\u7f6e<p>\u5e76\u91cd\u542fGpsPrune\u4f7f\u8bbe\u7f6e\u751f\u6548
519536 dialog.setlanguage.language=\u8bed\u8a00
559576 dialog.weather.day.friday=\u5468\u4e94
560577 dialog.weather.day.saturday=\u5468\u516d
561578 dialog.weather.day.sunday=\u5468\u65e5
579 dialog.weather.wind=\u98ce\u529b
580 dialog.weather.temp=\u6e29\u5ea6
581 dialog.weather.humidity=\u6e7f\u5ea6
562582 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
583 dialog.deletebydate.onlyonedate=\u6240\u6709\u8f68\u8ff9\u70b9\u90fd\u662f\u540c\u4e00\u5929\u7684
584 dialog.deletebydate.intro=\u4f60\u53ef\u4ee5\u9009\u62e9\u4fdd\u7559\u6216\u5220\u9664\u67d0\u4e00\u5929\u7684\u8f68\u8ff9\u70b9
585 dialog.deletebydate.nodate=\u6ca1\u6709\u65f6\u95f4\u6233
586 dialog.deletebydate.column.keep=\u4fdd\u7559
587 dialog.deletebydate.column.delete=\u5220\u9664
588 dialog.setaltitudetolerance.text.metres=\u4e0d\u8d85\u8fc7\u6b64\u6570\u503c(\u7c73)\u7684\u9ad8\u5ea6\u53d8\u5316\u5c06\u88ab\u5ffd\u7565
589 dialog.setaltitudetolerance.text.feet=
563590
564591 # 3d window
565592 dialog.3d.title=GpsPrune 3D \u663e\u793a
714741 fieldname.altitude=\u9ad8\u5ea6
715742 fieldname.timestamp=\u65f6\u95f4
716743 fieldname.time=\u65f6\u95f4
744 fieldname.date=\u65e5\u671f
717745 fieldname.waypointname=\u540d\u79f0
718746 fieldname.waypointtype=\u7c7b\u578b
719747 fieldname.newsegment=\u6bb5
848876 error.cache.notthere=\u672a\u627e\u5230\u533a\u57df\u6570\u636e\u7f13\u5b58\u6587\u4ef6\u5939
849877 error.cache.empty=\u533a\u57df\u6570\u636e\u6587\u4ef6\u5939\u7a7a
850878 error.cache.cannotdelete=\u65e0\u53ef\u5220\u9664\u533a\u57df\u6570\u636e
851 error.interpolate.invalidparameter=\u8f93\u5165\u70b9\u6570\u91cf\u5fc5\u987b\u57281\u52301000\u4e4b\u95f4
852879 error.learnestimationparams.failed=\u65e0\u6cd5\u4ece\u6b64\u8f68\u8ff9\u5f97\u5230\u53c2\u6570\n \u5c1d\u8bd5\u8f7d\u5165\u66f4\u591a\u8f68\u8ff9
853880 error.tracksplit.nosplit=\u6b64\u8f68\u8ff9\u65e0\u6cd5\u5206\u5272
854881 error.downloadsrtm.nocache=\u6587\u4ef6\u65e0\u6cd5\u4fdd\u5b58\n\u8bf7\u68c0\u67e5\u78c1\u76d8\u7f13\u5b58
257257 // Apply timestamp to photo and its point (if any)
258258 photo.setTimestamp(timestamp);
259259 if (photo.getDataPoint() != null) {
260 photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.FORMAT_ISO_8601), false);
260 photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.Format.ISO8601), false);
261261 }
262262 return photo;
263263 }
0 GpsPrune version 16.3
1 =====================
0 GpsPrune version 17
1 ===================
22
33 GpsPrune is an application for viewing, editing and managing coordinate data from GPS systems,
44 including format conversion, charting and photo correlation.
5 Full details can be found at http://activityworkshop.net/software/gpsprune/
5 Full details can be found at http://gpsprune.activityworkshop.net/
66
77 GpsPrune is copyright 2006-2014 activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
88 You may freely use the software, and may help others to freely use it too. For further information
1616 =======
1717
1818 To run GpsPrune from the jar file, simply call it from a command prompt or shell:
19 java -jar gpsprune_16.3.jar
19 java -jar gpsprune_17.jar
2020
2121 If the jar file is saved in a different directory, you will need to include the path.
2222 Depending on your system settings, you may be able to click or double-click on the jar file
2424 or other link can of course be made should you wish.
2525
2626 To specify a language other than the default, use an additional parameter, eg:
27 java -jar gpsprune_16.3.jar --lang=DE
28
29
30 New with version 16.3
31 =====================
32 The following fixes were added since version 16.2:
33 - Fix for gpx caching of points which failed to load
34 - Additional newlines / tabs in gpx export
35 - API key for openweathermap.org
36 - Improvements to 3d terrain reflections
37 - Additional translations
38
39 New with version 16.2
40 =====================
41 The following fixes were added since version 16.1:
42 - Fix for Gpx-slicing UTF8 files
43 - Conversion of sunrise/sunset times to local timezone
44 - Removal of Cloudmade maps
45 - Additional translations
46
47 New with version 16.1
48 =====================
49 The following fixes were added since version 16:
50 - Caching of terrain information for three-dimensional views
51 - Additional translations
52 - Improved void filling by interpolation
53 - Remembering file type of imported files
54
27 java -jar gpsprune_17.jar --lang=DE
28
29
30 New with version 17
31 ===================
32 The following features were added since version 16:
33 - Colouring the track points according to various criteria (such as altitude,
34 speed, segment, file) in both the regular map view and the image export
35 - Marking points for deletion according to their date
36 - Select the current segment
37 - Adding an altitude tolerance to the climb and descent calculations
38 - Sorting waypoints by name or by timestamp
39
5540 New with version 16
5641 ===================
5742 The following features were added since version 15:
6348 - Function to download and save SRTM tiles
6449
6550 New with version 15
66 =====================
51 ===================
6752 The following features were added since version 14:
6853 - Extend povray output using map image on base plane
6954 - Export an image of the map and track at a selected zoom level
70 - Estimation of hiking times and learining of parameter values
55 - Estimation of hiking times and learning of parameter values
7156 - Allow altitude / speed profile to show any arbitrary field
7257 - Accept files dragged and dropped onto the GpsPrune window
7358 - Take account of timezone if present in track timestamps
7762 - Allow loading of speeds and vertical speeds from text files
7863
7964 New with version 14
80 =====================
65 ===================
8166 The following features were added since version 13:
8267 - Dragging of existing points
8368 - Creation of new points by dragging the halfway point between two points
119104
120105 New with version 11
121106 ===================
122
123107 The following features were added since version 10:
124108 - Option to select which of the named tracks to load out of a gpx file or gps
125109 - Function to delete all values of a single field (eg all altitudes, all timestamps)
131115
132116 New with version 10
133117 ===================
134
135118 The following features were added since version 9:
136119 - Function to lookup altitudes using SRTM data from the Space Shuttle
137120 - Choice between altitude profile and speed profile in main view
143126
144127 New with version 9
145128 ==================
146
147129 The following features were added since version 8:
148130 - Ability to paste coordinates (eg from wikipedia or geocaching sites) to create new points
149131 - Configurable colour settings
157139
158140 New with version 8
159141 ==================
160
161142 The following features were added since version 7:
162143 - Loading of NMEA files (with suffix .nmea)
163144 - Loading of nearby tracks from gpsies.com
171152
172153 New with version 7
173154 ==================
174
175155 The following features were added since version 6:
176156 - Loading of KMZ files and zipped GPX
177157 - Improved compression functions with four configurable algorithms
183163
184164 New with version 6
185165 ==================
186
187166 The following features were added since version 5:
188167 - Map view using OpenStreetMap images is now integrated in the main window, with control for map transparency
189168 - Pov export has new option to use sphere sweeps for better appearance
196175
197176 New with version 5
198177 ==================
199
200 The following features were added since version 4.1:
178 The following features were added since version 4:
201179 - New map window in the View menu, showing points overlaid on OpenStreetMap images
202180 - New function to launch a browser showing the area in either Google Maps or OpenStreetMap
203181 - Handling of track segments, including loading, saving and exporting, and preservation during edits and undos
208186
209187 New with version 4
210188 ==================
211
212189 The following features were added since version 3:
213190 - Automatic correlation of photos with points based on timestamps
214191 - Manual disconnection of photos from points
221198
222199 New with version 3
223200 ==================
224
225201 The following features were added since version 2:
226202 - Loading of GPX and KML files
227203 - Loading of jpeg photos with or without coordinate data
233209
234210 New with version 2
235211 ==================
236
237212 The following features were added since version 1:
238213 - Display of data in 3d view using Java3D library
239214 - Export of 3d model to POV format for rendering by povray
243218
244219 Features of version 1
245220 =====================
246
247221 The following features were included in version 1:
248222 - Loading of text files, display in overhead and profile views
249223 - Display of track details such as distances, speeds
7373 private static final int[] FORMAT_COORDS = {Coordinate.FORMAT_NONE, Coordinate.FORMAT_DEG_MIN_SEC,
7474 Coordinate.FORMAT_DEG_MIN, Coordinate.FORMAT_DEG};
7575 private static final Unit[] UNIT_ALTS = {null, UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_FEET};
76 private static final int[] FORMAT_TIMES = {Timestamp.FORMAT_ORIGINAL, Timestamp.FORMAT_LOCALE, Timestamp.FORMAT_ISO_8601};
76 private static final Timestamp.Format[] FORMAT_TIMES = {Timestamp.Format.ORIGINAL, Timestamp.Format.LOCALE, Timestamp.Format.ISO8601};
7777
7878
7979 /**
436436 }
437437 }
438438 // Get timestamp format
439 int timestampFormat = Timestamp.FORMAT_ORIGINAL;
439 Timestamp.Format timestampFormat = Timestamp.Format.ORIGINAL;
440440 for (int i=0; i<_timestampUnitsRadios.length; i++)
441441 {
442442 if (_timestampUnitsRadios[i].isSelected()) {
571571 * @param inTimestampFormat timestamp format
572572 */
573573 private void saveField(StringBuffer inBuffer, DataPoint inPoint, Field inField,
574 int inCoordFormat, Unit inAltitudeUnit, int inTimestampFormat)
574 int inCoordFormat, Unit inAltitudeUnit, Timestamp.Format inTimestampFormat)
575575 {
576576 // Output field according to type
577577 if (inField == Field.LATITUDE)
594594 {
595595 if (inPoint.hasTimestamp())
596596 {
597 if (inTimestampFormat == Timestamp.FORMAT_ORIGINAL) {
598 // output original string
599 inBuffer.append(inPoint.getTimestamp().getText(Timestamp.FORMAT_ORIGINAL));
600 }
601 else {
602 // format value accordingly
603 inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat));
604 }
597 // format value accordingly
598 inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat));
605599 }
606600 }
607601 else
534534 source = replaceGpxTags(source, "lat=\"", "\"", inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
535535 source = replaceGpxTags(source, "lon=\"", "\"", inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
536536 source = replaceGpxTags(source, "<ele>", "</ele>", inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES));
537 source = replaceGpxTags(source, "<time>", "</time>", inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
537 source = replaceGpxTags(source, "<time>", "</time>", inPoint.getTimestamp().getText(Timestamp.Format.ISO8601));
538538 if (inPoint.isWaypoint())
539539 {
540540 source = replaceGpxTags(source, "<name>", "</name>", inPoint.getWaypointName());
684684 if (inPoint.hasTimestamp() && inTimestamps)
685685 {
686686 inWriter.write("\t\t<time>");
687 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
687 inWriter.write(inPoint.getTimestamp().getText(Timestamp.Format.ISO8601));
688688 inWriter.write("</time>\n");
689689 }
690690 // write waypoint name after elevation and time
756756 if (inPoint.hasTimestamp() && inTimestamps)
757757 {
758758 inWriter.write("\t\t\t\t<time>");
759 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
759 inWriter.write(inPoint.getTimestamp().getText(Timestamp.Format.ISO8601));
760760 inWriter.write("</time>\n");
761761 }
762762 // photo, audio
3535 import tim.prune.gui.BaseImageDefinitionPanel;
3636 import tim.prune.gui.GuiGridLayout;
3737 import tim.prune.gui.WholeNumberField;
38 import tim.prune.gui.colour.PointColourer;
3839 import tim.prune.gui.map.MapSource;
3940 import tim.prune.gui.map.MapSourceLibrary;
4041 import tim.prune.gui.map.MapUtils;
288289 final int zoomFactor = 1 << _baseImagePanel.getImageDefinition().getZoom();
289290 Graphics g = inImage.getImage().getGraphics();
290291 // TODO: Set line width, style etc
291 g.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT));
292
293 // Loop over points
292 final PointColourer pointColourer = _app.getPointColourer();
293 final Color defaultPointColour = Config.getColourScheme().getColour(ColourScheme.IDX_POINT);
294 g.setColor(defaultPointColour);
295
296 // Loop to draw all track points
294297 final Track track = _app.getTrackInfo().getTrack();
295298 final int numPoints = track.getNumPoints();
296299 int prevX = 0, prevY = 0;
299302 DataPoint point = track.getPoint(i);
300303 if (!point.isWaypoint())
301304 {
305 // Determine what colour to use to draw the track point
306 if (pointColourer != null)
307 {
308 Color c = pointColourer.getColour(i);
309 g.setColor(c == null ? defaultPointColour : c);
310 }
302311 double x = track.getX(i) - xRange.getMinimum();
303312 double y = track.getY(i) - yRange.getMinimum();
304313 // use zoom level to calculate pixel coords on image
317326 prevX = px; prevY = py;
318327 }
319328 }
320 // Draw waypoints
329
330 // Now the waypoints
321331 final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT);
322332 g.setColor(textColour);
323 // Loop over points
333 // Loop again to draw waypoints
324334 for (int i=0; i<numPoints; i++)
325335 {
326336 DataPoint point = track.getPoint(i);
5050 import tim.prune.data.Track;
5151 import tim.prune.data.TrackInfo;
5252 import tim.prune.data.UnitSetLibrary;
53 import tim.prune.gui.ColourChooser;
54 import tim.prune.gui.ColourPatch;
5553 import tim.prune.gui.DialogCloser;
5654 import tim.prune.gui.ImageUtils;
5755 import tim.prune.gui.WholeNumberField;
56 import tim.prune.gui.colour.ColourChooser;
57 import tim.prune.gui.colour.ColourPatch;
5858 import tim.prune.load.GenericFileFilter;
5959 import tim.prune.save.xml.XmlUtils;
6060
683683 // Add timestamp (if any) to the list
684684 whenList.append("<when>");
685685 if (point.hasTimestamp()) {
686 whenList.append(point.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
686 whenList.append(point.getTimestamp().getText(Timestamp.Format.ISO8601));
687687 }
688688 whenList.append("</when>\n");
689689 // Add coordinates to the list
320320 {
321321 // file saved - store directory in config for later
322322 Config.setConfigString(Config.KEY_TRACK_DIR, povFile.getParentFile().getAbsolutePath());
323 // also store exaggeration
323 // also store exaggeration and grid size
324324 Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_altFactor * 100));
325 if (_terrainPanel.getUseTerrain() && _terrainPanel.getGridSize() > 20) {
326 Config.setConfigInt(Config.KEY_TERRAIN_GRID_SIZE, _terrainPanel.getGridSize());
327 }
325328 }
326329 else
327330 {
77 */
88 public abstract class UndoDeleteOperation implements UndoOperation
99 {
10 /** Flag to remember whether the deleted point was at the beginning or end of the selected range */
11 private boolean _isAtBoundaryOfSelectedRange = false;
12
13 /**
14 * @param inAtBoundary true if deleted point was at the beginning or end of the selected range
15 */
16 public void setAtBoundaryOfSelectedRange(boolean inAtBoundary)
17 {
18 _isAtBoundaryOfSelectedRange = inAtBoundary;
19 }
20
1021 /**
1122 * Modify the current point/range selection after the delete operation is undone
1223 * @param inTrackInfo track info object
1324 * @param inStartIndex start index of reinserted range
1425 * @param inEndIndex end index of reinserted range
1526 */
16 protected static void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex)
27 protected void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex)
1728 {
1829 final int numPointsInserted = inEndIndex - inStartIndex + 1;
1930 // See if there is a currently selected point, if so does it need to be modified
2536 // Same for currently selected range
2637 int rangeStart = inTrackInfo.getSelection().getStart();
2738 int rangeEnd = inTrackInfo.getSelection().getEnd();
28 if (rangeEnd >= inStartIndex && rangeEnd > rangeStart)
39 // Was the deleted point at the start or end of the selected range?
40 final boolean wasAtStart = numPointsInserted == 1 && inStartIndex == rangeStart && _isAtBoundaryOfSelectedRange;
41 final boolean wasAtEnd = numPointsInserted == 1 && inStartIndex == (rangeEnd+1) && _isAtBoundaryOfSelectedRange;
42 if (rangeEnd >= inStartIndex && rangeEnd > rangeStart || wasAtStart || wasAtEnd)
2943 {
3044 rangeEnd += numPointsInserted;
3145 if (rangeStart >= inStartIndex) {
3246 rangeStart += numPointsInserted;
3347 }
48 // Extend selection if the deleted point was at the start or end
49 if (wasAtStart) {
50 rangeStart--;
51 }
3452 inTrackInfo.getSelection().selectRange(rangeStart, rangeEnd);
3553 }
3654 }