Imported Upstream version 17
Mònica Ramírez Arceda
9 years ago
0 | 0 | # Build script using external exif library |
1 | 1 | # Version number |
2 | PRUNENAME=gpsprune_16.3 | |
2 | PRUNENAME=gpsprune_17 | |
3 | 3 | # remove compile directory |
4 | 4 | rm -rf compile |
5 | 5 | # remove dist directory |
34 | 34 | import tim.prune.gui.SidebarController; |
35 | 35 | import tim.prune.gui.UndoManager; |
36 | 36 | import tim.prune.gui.Viewport; |
37 | import tim.prune.gui.colour.ColourerCaretaker; | |
38 | import tim.prune.gui.colour.PointColourer; | |
37 | 39 | import tim.prune.load.FileLoader; |
38 | 40 | import tim.prune.load.JpegLoader; |
39 | 41 | import tim.prune.load.MediaLinkInfo; |
60 | 62 | private JpegLoader _jpegLoader = null; |
61 | 63 | private FileSaver _fileSaver = null; |
62 | 64 | private UndoStack _undoStack = null; |
65 | private ColourerCaretaker _colCaretaker = null; | |
63 | 66 | private boolean _mangleTimestampsConfirmed = false; |
64 | 67 | private Viewport _viewport = null; |
65 | 68 | private ArrayList<File> _dataFiles = null; |
82 | 85 | _track = new Track(); |
83 | 86 | _trackInfo = new TrackInfo(_track); |
84 | 87 | FunctionLibrary.initialise(this); |
88 | _colCaretaker = new ColourerCaretaker(this); | |
89 | UpdateMessageBroker.addSubscriber(_colCaretaker); | |
90 | _colCaretaker.setColourer(Config.getPointColourer()); | |
85 | 91 | } |
86 | 92 | |
87 | 93 | |
119 | 125 | return _undoStack; |
120 | 126 | } |
121 | 127 | |
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 | } | |
122 | 146 | |
123 | 147 | /** |
124 | 148 | * Show the specified tip if appropriate |
318 | 342 | int audioIndex = _trackInfo.getAudioList().getAudioIndex(currentPoint.getAudio()); |
319 | 343 | DataPoint nextTrackPoint = _trackInfo.getTrack().getNextTrackPoint(pointIndex + 1); |
320 | 344 | // Construct Undo object |
321 | UndoOperation undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex, | |
345 | UndoDeletePoint undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex, | |
322 | 346 | audioIndex, nextTrackPoint != null && nextTrackPoint.getSegmentStart()); |
347 | undo.setAtBoundaryOfSelectedRange(pointIndex == _trackInfo.getSelection().getStart() || | |
348 | pointIndex == _trackInfo.getSelection().getEnd()); | |
323 | 349 | // call track to delete point |
324 | 350 | if (_trackInfo.deletePoint()) |
325 | 351 | { |
498 | 524 | */ |
499 | 525 | public void createPoint(DataPoint inPoint) |
500 | 526 | { |
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 | { | |
501 | 537 | // create undo object |
502 | 538 | UndoCreatePoint undo = new UndoCreatePoint(); |
503 | 539 | _undoStack.add(undo); |
504 | 540 | // add point to track |
505 | inPoint.setSegmentStart(true); | |
541 | inPoint.setSegmentStart(inNewSegment); | |
506 | 542 | _track.appendPoints(new DataPoint[] {inPoint}); |
507 | 543 | // ensure track's field list contains point's fields |
508 | 544 | _track.extendFieldList(inPoint.getFieldList()); |
5 | 5 | import tim.prune.function.charts.Charter; |
6 | 6 | import tim.prune.function.compress.CompressTrackFunction; |
7 | 7 | import tim.prune.function.compress.MarkPointsInRectangleFunction; |
8 | import tim.prune.function.deletebydate.DeleteByDateFunction; | |
8 | 9 | import tim.prune.function.distance.DistanceFunction; |
9 | 10 | import tim.prune.function.edit.PointNameEditor; |
10 | 11 | import tim.prune.function.estimate.EstimateTime; |
41 | 42 | public static GenericFunction FUNCTION_IMPORTBABEL = null; |
42 | 43 | public static GenericFunction FUNCTION_SAVECONFIG = null; |
43 | 44 | 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; | |
45 | 47 | public static GenericFunction FUNCTION_SPLIT_SEGMENTS = null; |
46 | 48 | public static GenericFunction FUNCTION_SEW_SEGMENTS = null; |
47 | 49 | public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null; |
49 | 51 | public static GenericFunction FUNCTION_DELETE_RANGE = null; |
50 | 52 | public static GenericFunction FUNCTION_CROP_TRACK = null; |
51 | 53 | 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; | |
53 | 56 | public static GenericFunction FUNCTION_LOOKUP_SRTM = null; |
54 | 57 | public static GenericFunction FUNCTION_DOWNLOAD_SRTM = null; |
55 | 58 | public static GenericFunction FUNCTION_LOOKUP_WIKIPEDIA = null; |
89 | 92 | public static GenericFunction FUNCTION_SET_DISK_CACHE = null; |
90 | 93 | public static GenericFunction FUNCTION_SET_PATHS = null; |
91 | 94 | 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; | |
93 | 96 | public static GenericFunction FUNCTION_SET_LANGUAGE = null; |
97 | public static SingleNumericParameterFunction FUNCTION_SET_ALTITUDE_TOLERANCE = null; | |
94 | 98 | public static GenericFunction FUNCTION_HELP = null; |
95 | 99 | public static GenericFunction FUNCTION_SHOW_KEYS = null; |
96 | 100 | public static GenericFunction FUNCTION_ABOUT = null; |
114 | 118 | FUNCTION_SAVECONFIG = new SaveConfig(inApp); |
115 | 119 | FUNCTION_EDIT_WAYPOINT_NAME = new PointNameEditor(inApp); |
116 | 120 | FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp); |
121 | FUNCTION_SELECT_SEGMENT = new SelectSegmentFunction(inApp); | |
117 | 122 | FUNCTION_SPLIT_SEGMENTS = new SplitSegmentsFunction(inApp); |
118 | 123 | FUNCTION_SEW_SEGMENTS = new SewTrackSegmentsFunction(inApp); |
119 | 124 | FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp); |
121 | 126 | FUNCTION_DELETE_RANGE = new DeleteSelectedRangeFunction(inApp); |
122 | 127 | FUNCTION_CROP_TRACK = new CropToSelection(inApp); |
123 | 128 | FUNCTION_MARK_IN_RECTANGLE = new MarkPointsInRectangleFunction(inApp); |
129 | FUNCTION_DELETE_BY_DATE = new DeleteByDateFunction(inApp); | |
124 | 130 | FUNCTION_INTERPOLATE = new InterpolateFunction(inApp); |
125 | 131 | FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp); |
126 | 132 | FUNCTION_DOWNLOAD_SRTM = new DownloadSrtmFunction(inApp); |
163 | 169 | FUNCTION_SET_COLOURS = new SetColours(inApp); |
164 | 170 | FUNCTION_SET_LINE_WIDTH = new SetLineWidth(inApp); |
165 | 171 | FUNCTION_SET_LANGUAGE = new SetLanguage(inApp); |
172 | FUNCTION_SET_ALTITUDE_TOLERANCE = new SetAltitudeTolerance(inApp); | |
166 | 173 | FUNCTION_HELP = new HelpScreen(inApp); |
167 | 174 | FUNCTION_SHOW_KEYS = new ShowKeysScreen(inApp); |
168 | 175 | FUNCTION_ABOUT = new AboutScreen(inApp); |
2 | 2 | import java.awt.event.WindowAdapter; |
3 | 3 | import java.awt.BorderLayout; |
4 | 4 | import java.awt.Component; |
5 | import java.awt.Image; | |
5 | 6 | import java.awt.event.WindowEvent; |
6 | 7 | import java.io.File; |
7 | 8 | import java.io.FileNotFoundException; |
34 | 35 | public class GpsPrune |
35 | 36 | { |
36 | 37 | /** 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"; | |
38 | 39 | /** Build number, just used for about screen */ |
39 | public static final String BUILD_NUMBER = "303c"; | |
40 | public static final String BUILD_NUMBER = "320"; | |
40 | 41 | /** Static reference to App object */ |
41 | 42 | private static App APP = null; |
42 | 43 | |
227 | 228 | // Avoid automatically shutting down if window closed |
228 | 229 | frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); |
229 | 230 | |
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 | } | |
235 | 251 | |
236 | 252 | // Set up drag-and-drop handler to accept dropped files |
237 | 253 | frame.setTransferHandler(new FileDropHandler(APP)); |
5 | 5 | */ |
6 | 6 | public abstract class UpdateMessageBroker |
7 | 7 | { |
8 | private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 6; | |
8 | private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 7; | |
9 | 9 | /** Array of all subscribers */ |
10 | 10 | private static DataSubscriber[] _subscribers = new DataSubscriber[MAXIMUM_NUMBER_SUBSCRIBERS]; |
11 | 11 | /** Counter of the number of subscribers added so far */ |
6 | 6 | import tim.prune.data.RecentFileList; |
7 | 7 | import tim.prune.data.UnitSet; |
8 | 8 | import tim.prune.data.UnitSetLibrary; |
9 | import tim.prune.gui.colour.ColourerFactory; | |
10 | import tim.prune.gui.colour.PointColourer; | |
9 | 11 | import tim.prune.gui.map.MapSourceLibrary; |
10 | 12 | |
11 | 13 | |
21 | 23 | private static Properties _configValues = null; |
22 | 24 | /** Colour scheme object is also part of config */ |
23 | 25 | private static ColourScheme _colourScheme = new ColourScheme(); |
26 | /** Point colourer object, if any */ | |
27 | private static PointColourer _pointColourer = null; | |
24 | 28 | /** Recently-used file list */ |
25 | 29 | private static RecentFileList _recentFiles = new RecentFileList(); |
26 | 30 | /** Current unit set */ |
72 | 76 | public static final String KEY_EXIFTOOL_PATH = "prune.exiftoolpath"; |
73 | 77 | /** Key for colour scheme */ |
74 | 78 | public static final String KEY_COLOUR_SCHEME = "prune.colourscheme"; |
79 | /** Key for point colourer */ | |
80 | public static final String KEY_POINT_COLOURER = "prune.pointcolourer"; | |
75 | 81 | /** Key for line width used for drawing */ |
76 | 82 | public static final String KEY_LINE_WIDTH = "prune.linewidth"; |
77 | 83 | /** Key for kml track colour */ |
84 | 90 | public static final String KEY_ESTIMATION_PARAMS = "prune.estimationparams"; |
85 | 91 | /** Key for 3D exaggeration factor */ |
86 | 92 | 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"; | |
87 | 97 | |
88 | 98 | |
89 | 99 | /** Initialise the default properties */ |
144 | 154 | // Save all properties from file |
145 | 155 | _configValues.putAll(props); |
146 | 156 | _colourScheme.loadFromHex(_configValues.getProperty(KEY_COLOUR_SCHEME)); |
157 | _pointColourer = ColourerFactory.createColourer(_configValues.getProperty(KEY_POINT_COLOURER)); | |
147 | 158 | _recentFiles = new RecentFileList(_configValues.getProperty(KEY_RECENT_FILES)); |
148 | 159 | _unitSet = UnitSetLibrary.getUnitSet(_configValues.getProperty(KEY_UNITSET_KEY)); |
149 | 160 | // Adjust map source index if necessary |
175 | 186 | props.put(KEY_AUTOSAVE_SETTINGS, "0"); // autosave false by default |
176 | 187 | props.put(KEY_UNITSET_KEY, "unitset.kilometres"); // metric by default |
177 | 188 | 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 | |
178 | 191 | return props; |
179 | 192 | } |
180 | 193 | |
237 | 250 | } |
238 | 251 | |
239 | 252 | /** |
253 | * @return the current point colourer, if any | |
254 | */ | |
255 | public static PointColourer getPointColourer() | |
256 | { | |
257 | return _pointColourer; | |
258 | } | |
259 | ||
260 | /** | |
240 | 261 | * @return list of recently used files |
241 | 262 | */ |
242 | 263 | public static RecentFileList getRecentFileList() |
337 | 358 | } |
338 | 359 | |
339 | 360 | /** |
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 | /** | |
340 | 371 | * @return the current unit set |
341 | 372 | */ |
342 | 373 | public static UnitSet getUnitSet() { |
343 | 374 | return _unitSet; |
344 | 375 | } |
345 | 376 | |
377 | /** | |
378 | * @param inIndex index of unit set to select | |
379 | */ | |
346 | 380 | public static void selectUnitSet(int inIndex) |
347 | 381 | { |
348 | 382 | _unitSet = UnitSetLibrary.getUnitSet(inIndex); |
0 | 0 | package tim.prune.data; |
1 | ||
2 | import tim.prune.config.Config; | |
1 | 3 | |
2 | 4 | /** |
3 | 5 | * Represents a range of altitudes, taking units into account. |
7 | 9 | { |
8 | 10 | /** Range of altitudes in metres */ |
9 | 11 | 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; | |
12 | 14 | /** Previous metric value */ |
13 | private int _prevValue; | |
15 | private int _previousValue; | |
14 | 16 | /** Total climb in metres */ |
15 | private double _climb; | |
17 | private int _climb; | |
16 | 18 | /** 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; | |
18 | 24 | |
19 | 25 | |
20 | 26 | /** |
30 | 36 | public void clear() |
31 | 37 | { |
32 | 38 | _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; | |
37 | 44 | } |
38 | 45 | |
39 | 46 | |
43 | 50 | */ |
44 | 51 | public void addValue(Altitude inAltitude) |
45 | 52 | { |
53 | final int wiggleLimit = Config.getConfigInt(Config.KEY_ALTITUDE_TOLERANCE) / 100; | |
54 | ||
46 | 55 | if (inAltitude != null && inAltitude.isValid()) |
47 | 56 | { |
48 | 57 | int altValue = (int) inAltitude.getMetricValue(); |
49 | 58 | _range.addValue(altValue); |
50 | 59 | // Compare with previous value if any |
51 | if (!_empty) | |
60 | if (_gotPreviousValue) | |
52 | 61 | { |
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 | } | |
57 | 121 | } |
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; | |
60 | 138 | } |
61 | 139 | } |
62 | 140 | |
66 | 144 | */ |
67 | 145 | public void ignoreValue(Altitude inAltitude) |
68 | 146 | { |
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 | } | |
72 | 165 | } |
73 | 166 | |
74 | 167 | /** |
106 | 199 | */ |
107 | 200 | public int getClimb(Unit inUnit) |
108 | 201 | { |
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()); | |
110 | 208 | } |
111 | 209 | |
112 | 210 | /** |
115 | 213 | */ |
116 | 214 | public int getDescent(Unit inUnit) |
117 | 215 | { |
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()); | |
119 | 222 | } |
120 | 223 | |
121 | 224 | /** |
123 | 226 | */ |
124 | 227 | public double getMetricHeightDiff() |
125 | 228 | { |
126 | return _climb - _descent; | |
229 | return getClimb(UnitSetLibrary.UNITS_METRES) - getDescent(UnitSetLibrary.UNITS_METRES); | |
127 | 230 | } |
128 | 231 | } |
43 | 43 | { |
44 | 44 | int i = inIndex + 1; |
45 | 45 | DataPoint point = null; |
46 | while ((point=inTrack.getPoint(i)) != null && !point.getSegmentStart()) { | |
46 | while ((point=inTrack.getPoint(i)) != null && (point.isWaypoint() || !point.getSegmentStart())) { | |
47 | 47 | i++; |
48 | 48 | } |
49 | 49 | return Math.min(i, inTrack.getNumPoints()-1); |
59 | 59 | { |
60 | 60 | int i = inIndex - 1; |
61 | 61 | DataPoint point = null; |
62 | while ((point=inTrack.getPoint(i)) != null && !point.getSegmentStart()) { | |
62 | while ((point=inTrack.getPoint(i)) != null && (point.isWaypoint() || !point.getSegmentStart())) { | |
63 | 63 | i--; |
64 | 64 | } |
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; | |
66 | 116 | } |
67 | 117 | } |
125 | 125 | // parse fields according to number found |
126 | 126 | _degrees = (int) fields[0]; |
127 | 127 | _asDouble = _degrees; |
128 | _originalFormat = hasCardinal?FORMAT_DEG:FORMAT_DEG_WITHOUT_CARDINAL; | |
128 | _originalFormat = hasCardinal ? FORMAT_DEG : FORMAT_DEG_WITHOUT_CARDINAL; | |
129 | 129 | _fracDenom = 10; |
130 | 130 | if (numFields == 2) |
131 | 131 | { |
148 | 148 | _fracs = 0; |
149 | 149 | _asDouble = 1.0 * _degrees + (_minutes / 60.0); |
150 | 150 | } |
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); | |
151 | 164 | } |
152 | 165 | // Differentiate between d-m.f and d-m-s using . or , |
153 | 166 | else if (numFields == 3 && !otherDelims[2]) |
183 | 196 | |
184 | 197 | /** |
185 | 198 | * 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) | |
190 | 203 | { |
191 | 204 | // Try leading character first |
192 | 205 | int cardinal = getCardinal(inFirstChar); |
81 | 81 | } |
82 | 82 | |
83 | 83 | /** |
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 | /** | |
84 | 99 | * Clone contents of file info |
85 | 100 | */ |
86 | 101 | @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 | } |
121 | 121 | public int getIndex(DataPoint inPoint) |
122 | 122 | { |
123 | 123 | 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 | } | |
126 | 130 | } |
127 | 131 | if (idx == -1) {return idx;} // point not found |
128 | 132 | if (_pointIndices == null) {return idx;} // All points loaded |
15 | 15 | */ |
16 | 16 | public static void calculateSpeed(Track inTrack, int inIndex, SpeedValue inValue) |
17 | 17 | { |
18 | if (inTrack == null || inIndex < 0 || inValue == null) { | |
18 | inValue.setInvalid(); | |
19 | if (inTrack == null || inIndex < 0 || inValue == null) | |
20 | { | |
19 | 21 | System.err.println("Cannot calculate speed for index " + inIndex); |
20 | 22 | return; |
21 | 23 | } |
22 | inValue.setInvalid(); | |
23 | 24 | |
24 | 25 | DataPoint point = inTrack.getPoint(inIndex); |
25 | 26 | if (point == null) {return;} |
17 | 17 | private boolean _valid = false; |
18 | 18 | private long _milliseconds = 0L; |
19 | 19 | 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(); | |
23 | 23 | private static final DateFormat DEFAULT_TIME_FORMAT = DateFormat.getTimeInstance(); |
24 | private static boolean MillisAddedToTimeFormat = false; | |
24 | 25 | 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'"); | |
25 | 27 | private static final DateFormat ISO_8601_FORMAT_NOZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); |
26 | 28 | private static DateFormat[] ALL_DATE_FORMATS = null; |
27 | 29 | private static Calendar CALENDAR = null; |
37 | 39 | private static long TWENTY_YEARS_IN_SECS = 0L; |
38 | 40 | private static final long GARTRIP_OFFSET = 631065600L; |
39 | 41 | |
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 | } | |
46 | 49 | |
47 | 50 | /** Identifier for the parsing strategy to use */ |
48 | 51 | private enum ParseType |
57 | 60 | FIXED_FORMAT4, |
58 | 61 | FIXED_FORMAT5, |
59 | 62 | FIXED_FORMAT6, |
63 | FIXED_FORMAT7, | |
60 | 64 | GENERAL_STRING |
61 | 65 | } |
62 | 66 | |
63 | 67 | /** Array of parse types to loop through (first one is changed to last successful type) */ |
64 | 68 | private static ParseType[] ALL_PARSE_TYPES = {ParseType.NONE, ParseType.ISO8601_FRACTIONAL, ParseType.LONG, |
65 | 69 | 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}; | |
67 | 72 | |
68 | 73 | // Static block to initialise offsets |
69 | 74 | static |
79 | 84 | TWENTY_YEARS_IN_SECS = (MSECS_SINCE_1970 - MSECS_SINCE_1990) / 1000L; |
80 | 85 | // Set timezone for output |
81 | 86 | 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); | |
83 | 89 | // Date formats |
84 | 90 | ALL_DATE_FORMATS = new DateFormat[] { |
85 | DEFAULT_DATE_FORMAT, | |
91 | DEFAULT_DATETIME_FORMAT, | |
86 | 92 | new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy"), |
87 | 93 | new SimpleDateFormat("HH:mm:ss dd MMM yyyy"), |
88 | 94 | new SimpleDateFormat("dd MMM yyyy HH:mm:ss"), |
95 | new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss"), | |
89 | 96 | new SimpleDateFormat("yyyy MMM dd HH:mm:ss"), |
90 | 97 | ISO_8601_FORMAT, ISO_8601_FORMAT_NOZ |
91 | 98 | }; |
99 | for (DateFormat df : ALL_DATE_FORMATS) { | |
100 | df.setLenient(false); | |
101 | } | |
92 | 102 | } |
93 | 103 | |
94 | 104 | |
99 | 109 | public Timestamp(String inString) |
100 | 110 | { |
101 | 111 | _valid = false; |
112 | _text = null; | |
102 | 113 | if (inString != null && !inString.equals("")) |
103 | 114 | { |
104 | 115 | // Try each of the parse types in turn |
108 | 119 | { |
109 | 120 | ALL_PARSE_TYPES[0] = type; |
110 | 121 | _valid = true; |
122 | _text = inString; | |
111 | 123 | return; |
112 | 124 | } |
113 | 125 | } |
166 | 178 | case FIXED_FORMAT4: return parseString(inString, ALL_DATE_FORMATS[4]); |
167 | 179 | case FIXED_FORMAT5: return parseString(inString, ALL_DATE_FORMATS[5]); |
168 | 180 | case FIXED_FORMAT6: return parseString(inString, ALL_DATE_FORMATS[6]); |
181 | case FIXED_FORMAT7: return parseString(inString, ALL_DATE_FORMATS[7]); | |
169 | 182 | |
170 | 183 | case GENERAL_STRING: |
171 | 184 | if (inString.length() == 19) |
200 | 213 | */ |
201 | 214 | private boolean parseString(String inString, DateFormat inDateFormat) |
202 | 215 | { |
203 | inDateFormat.setLenient(false); | |
204 | 216 | ParsePosition pPos = new ParsePosition(0); |
205 | 217 | Date date = inDateFormat.parse(inString, pPos); |
206 | 218 | if (date != null && inString.length() == pPos.getIndex()) // require use of _all_ the string, not just the beginning |
338 | 350 | } |
339 | 351 | |
340 | 352 | /** |
353 | * @return true if the timestamp has non-zero milliseconds | |
354 | */ | |
355 | public boolean hasMilliseconds() | |
356 | { | |
357 | return isValid() && (_milliseconds % 1000L) > 0; | |
358 | } | |
359 | /** | |
341 | 360 | * @param inOther other Timestamp |
342 | 361 | * @return true if this one is at least a second after the other |
343 | 362 | */ |
431 | 450 | */ |
432 | 451 | public String getText() |
433 | 452 | { |
434 | return getText(FORMAT_LOCALE); | |
453 | return getText(Format.LOCALE); | |
435 | 454 | } |
436 | 455 | |
437 | 456 | /** |
438 | 457 | * @param inFormat format of timestamp |
439 | 458 | * @return Description of timestamp in required format |
440 | 459 | */ |
441 | public String getText(int inFormat) | |
460 | public String getText(Format inFormat) | |
442 | 461 | { |
443 | 462 | 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); | |
449 | 473 | } |
450 | 474 | return _text; |
451 | 475 | } |
452 | 476 | |
453 | 477 | /** |
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 | /** | |
454 | 487 | * @return Description of time part of timestamp in locale-specific format |
455 | 488 | */ |
456 | 489 | public String getTimeText() |
457 | 490 | { |
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 | } | |
462 | 504 | } |
463 | else _timeText = ""; | |
464 | } | |
465 | return _timeText; | |
505 | catch (ClassCastException cce) {} | |
506 | } | |
507 | return format(DEFAULT_TIME_FORMAT); | |
466 | 508 | } |
467 | 509 | |
468 | 510 | /** |
86 | 86 | _dataPoints[pointIndex] = point; |
87 | 87 | pointIndex++; |
88 | 88 | } |
89 | else | |
90 | { | |
91 | // TODO: Maybe report this somehow? | |
92 | // System.out.println("point is not valid!"); | |
93 | } | |
89 | 94 | } |
90 | 95 | _numPoints = pointIndex; |
91 | 96 | // Set first track point to be start of segment |
359 | 364 | // needs to be scaled again |
360 | 365 | _scaled = false; |
361 | 366 | 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; | |
417 | 367 | } |
418 | 368 | |
419 | 369 |
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 | } |
90 | 90 | |
91 | 91 | // Set status label according to error or "none found", leave blank if ok |
92 | 92 | if (_errorMessage == null && _trackListModel.isEmpty()) { |
93 | _errorMessage = I18nManager.getText("dialog.gpsies.nonefound"); | |
93 | _errorMessage = I18nManager.getText("dialog.wikipedia.nonefound"); | |
94 | 94 | } |
95 | 95 | _statusLabel.setText(_errorMessage == null ? "" : _errorMessage); |
96 | 96 | } |
2 | 2 | import javax.swing.JOptionPane; |
3 | 3 | |
4 | 4 | import tim.prune.App; |
5 | import tim.prune.GenericFunction; | |
6 | 5 | import tim.prune.I18nManager; |
7 | 6 | import tim.prune.data.DataPoint; |
8 | 7 | import tim.prune.data.Track; |
11 | 10 | /** |
12 | 11 | * Function to interpolate between the points in a range |
13 | 12 | */ |
14 | public class InterpolateFunction extends GenericFunction | |
13 | public class InterpolateFunction extends SingleNumericParameterFunction | |
15 | 14 | { |
16 | 15 | /** |
17 | 16 | * Constructor |
18 | 17 | * @param inApp app object |
19 | 18 | */ |
20 | 19 | public InterpolateFunction(App inApp) { |
21 | super(inApp); | |
20 | super(inApp, 1, 1000); | |
22 | 21 | } |
23 | 22 | |
24 | 23 | /** @return name key */ |
26 | 25 | return "function.interpolate"; |
27 | 26 | } |
28 | 27 | |
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 | ||
29 | 38 | /** |
30 | 39 | * Perform the operation |
31 | 40 | */ |
32 | 41 | 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) | |
33 | 50 | { |
34 | 51 | // Firstly, work out whether the selected range only contains waypoints or not |
35 | 52 | final int startIndex = _app.getTrackInfo().getSelection().getStart(); |
48 | 65 | betweenWaypoints = true; |
49 | 66 | } |
50 | 67 | |
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 | ||
64 | 68 | if (startIndex < 0 || endIndex < 0 || endIndex <= startIndex) { |
65 | 69 | return; |
66 | 70 | } |
67 | 71 | |
68 | 72 | // construct new point array with the interpolated points |
73 | final int numToAdd = inParam; | |
69 | 74 | final Track track = _app.getTrackInfo().getTrack(); |
70 | 75 | final int maxToAdd = (endIndex-startIndex) * numToAdd; |
71 | 76 | final int extendedSize = track.getNumPoints() + maxToAdd; |
140 | 145 | } |
141 | 146 | return false; |
142 | 147 | } |
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 | } | |
163 | 148 | } |
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 | } |
0 | 0 | package tim.prune.function; |
1 | 1 | |
2 | import java.awt.BorderLayout; | |
3 | import java.awt.FlowLayout; | |
4 | import java.awt.event.ActionEvent; | |
5 | import java.awt.event.ActionListener; | |
6 | 2 | 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; | |
13 | 3 | import javax.swing.JOptionPane; |
14 | import javax.swing.JPanel; | |
15 | import javax.swing.JRadioButton; | |
16 | ||
17 | 4 | import tim.prune.App; |
18 | import tim.prune.GenericFunction; | |
19 | 5 | import tim.prune.I18nManager; |
20 | 6 | import tim.prune.data.DataPoint; |
21 | 7 | import tim.prune.data.Track; |
8 | import tim.prune.data.sort.PhotoComparer; | |
9 | import tim.prune.data.sort.SortMode; | |
22 | 10 | import tim.prune.undo.UndoRearrangePhotos; |
23 | 11 | |
24 | 12 | /** |
25 | 13 | * Class to provide the function for rearranging photo points |
26 | 14 | */ |
27 | public class RearrangePhotosFunction extends GenericFunction | |
15 | public class RearrangePhotosFunction extends RearrangeFunction | |
28 | 16 | { |
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 | ||
37 | 17 | /** |
38 | 18 | * Constructor |
39 | 19 | * @param inApp app object |
40 | 20 | */ |
41 | 21 | public RearrangePhotosFunction(App inApp) |
42 | 22 | { |
43 | super(inApp); | |
23 | super(inApp, false); | |
44 | 24 | } |
45 | 25 | |
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 */ | |
63 | 27 | public String getNameKey() { |
64 | 28 | return "function.rearrangephotos"; |
65 | 29 | } |
66 | 30 | |
31 | /** Get the description key */ | |
32 | public String getDescriptionKey() { | |
33 | return "dialog.rearrangephotos.desc"; | |
34 | } | |
67 | 35 | |
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"; | |
129 | 39 | } |
130 | 40 | |
131 | 41 | /** |
132 | 42 | * Perform the rearrange |
133 | 43 | */ |
134 | private void finish() | |
44 | protected void finish() | |
135 | 45 | { |
136 | 46 | Track track = _app.getTrackInfo().getTrack(); |
137 | 47 | UndoRearrangePhotos undo = new UndoRearrangePhotos(track); |
144 | 54 | for (int i=0; i<numPoints; i++) |
145 | 55 | { |
146 | 56 | DataPoint point = track.getPoint(i); |
147 | if (point.getPhoto() != null) { | |
57 | if (point.getPhoto() != null) | |
58 | { | |
148 | 59 | photos[numPhotos] = point; |
149 | 60 | numPhotos++; |
150 | 61 | } |
151 | else { | |
62 | else | |
63 | { | |
152 | 64 | nonPhotos[numNonPhotos] = point; |
153 | 65 | numNonPhotos++; |
154 | 66 | } |
155 | 67 | } |
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); | |
159 | 94 | } |
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) | |
174 | 97 | { |
175 | 98 | _app.getTrackInfo().getSelection().clearAll(); |
176 | 99 | _app.completeFunction(undo, I18nManager.getText("confirm.rearrangephotos")); |
186 | 109 | /** |
187 | 110 | * Sort the given photo list either by filename or by time |
188 | 111 | * @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 | |
190 | 113 | * @return sorted array |
191 | 114 | */ |
192 | private static void sortPhotos(DataPoint[] inPhotos, boolean inSortByFile) | |
115 | private static void sortPhotos(DataPoint[] inPhotos, SortMode inSortMode) | |
193 | 116 | { |
194 | PhotoComparer comparer = new PhotoComparer(inSortByFile ? PhotoComparer.SortMode.SORTBY_NAME : PhotoComparer.SortMode.SORTBY_TIME); | |
117 | PhotoComparer comparer = new PhotoComparer(inSortMode); | |
195 | 118 | Arrays.sort(inPhotos, comparer); |
196 | 119 | } |
197 | 120 | } |
0 | 0 | package tim.prune.function; |
1 | ||
2 | import java.util.Arrays; | |
1 | 3 | |
2 | 4 | import javax.swing.JOptionPane; |
3 | 5 | |
4 | 6 | import tim.prune.App; |
5 | import tim.prune.GenericFunction; | |
6 | 7 | import tim.prune.I18nManager; |
8 | import tim.prune.data.Checker; | |
9 | import tim.prune.data.DataPoint; | |
7 | 10 | import tim.prune.data.Track; |
11 | import tim.prune.data.sort.SortMode; | |
12 | import tim.prune.data.sort.WaypointComparer; | |
8 | 13 | import tim.prune.undo.UndoRearrangeWaypoints; |
9 | 14 | |
10 | 15 | /** |
11 | 16 | * Class to provide the function for rearranging waypoints |
12 | 17 | */ |
13 | public class RearrangeWaypointsFunction extends GenericFunction | |
18 | public class RearrangeWaypointsFunction extends RearrangeFunction | |
14 | 19 | { |
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 | } | |
26 | 20 | |
27 | 21 | /** |
28 | 22 | * Constructor |
30 | 24 | */ |
31 | 25 | public RearrangeWaypointsFunction(App inApp) |
32 | 26 | { |
33 | super(inApp); | |
27 | super(inApp, true); | |
34 | 28 | } |
35 | 29 | |
36 | /** Begin the rearrange (not needed) */ | |
37 | public void begin() { | |
30 | /** Get the name key */ | |
31 | public String getNameKey() { | |
32 | return "function.rearrangewaypoints"; | |
38 | 33 | } |
39 | 34 | |
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"; | |
43 | 48 | } |
44 | 49 | |
45 | 50 | /** |
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 | |
48 | 52 | */ |
49 | public void rearrangeWaypoints(Rearrange inFunction) | |
53 | protected void finish() | |
50 | 54 | { |
51 | 55 | Track track = _app.getTrackInfo().getTrack(); |
56 | // Figure out what is required from the radio buttons | |
57 | Rearrange rearrangeOption = getRearrangeOption(); | |
58 | SortMode sortOption = getSortMode(); | |
59 | ||
52 | 60 | UndoRearrangeWaypoints undo = new UndoRearrangeWaypoints(track); |
53 | 61 | boolean success = false; |
54 | if (inFunction == Rearrange.TO_START || inFunction == Rearrange.TO_END) | |
62 | if (rearrangeOption == Rearrange.TO_START || rearrangeOption == Rearrange.TO_END) | |
55 | 63 | { |
56 | 64 | // Collect the waypoints to the start or end of the track |
57 | success = track.collectWaypoints(inFunction == Rearrange.TO_START); | |
65 | success = collectWaypoints(rearrangeOption, sortOption); | |
58 | 66 | } |
59 | 67 | else |
60 | 68 | { |
73 | 81 | } |
74 | 82 | } |
75 | 83 | |
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 | } | |
76 | 188 | } |
47 | 47 | else |
48 | 48 | { |
49 | 49 | // point is attached, so need to confirm point deletion |
50 | final int pointIndex = _app.getTrackInfo().getTrack().getPointIndex(currentAudio.getDataPoint()); | |
50 | 51 | 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()); | |
52 | 55 | int response = JOptionPane.showConfirmDialog(_app.getFrame(), |
53 | 56 | I18nManager.getText("dialog.deleteaudio.deletepoint"), |
54 | 57 | I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION); |
47 | 47 | else |
48 | 48 | { |
49 | 49 | // point is attached, so need to confirm point deletion |
50 | final int pointIndex = _app.getTrackInfo().getTrack().getPointIndex(currentPhoto.getDataPoint()); | |
50 | 51 | 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()); | |
52 | 55 | int response = JOptionPane.showConfirmDialog(_app.getFrame(), |
53 | 56 | I18nManager.getText("dialog.deletephoto.deletepoint"), |
54 | 57 | I18nManager.getText("dialog.deletephoto.title"), |
157 | 157 | */ |
158 | 158 | private void saveConfig(File inSaveFile) |
159 | 159 | { |
160 | // TODO: Check for null inSaveFile, then just call finish() ? | |
160 | 161 | FileOutputStream outStream = null; |
161 | 162 | try |
162 | 163 | { |
97 | 97 | |
98 | 98 | // Set status label according to error or "none found", leave blank if ok |
99 | 99 | if (_errorMessage == null && _trackListModel.isEmpty()) { |
100 | _errorMessage = I18nManager.getText("dialog.gpsies.nonefound"); | |
100 | _errorMessage = I18nManager.getText("dialog.wikipedia.nonefound"); | |
101 | 101 | } |
102 | 102 | _statusLabel.setText(_errorMessage == null ? "" : _errorMessage); |
103 | 103 | } |
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 | } |
0 | 0 | package tim.prune.function; |
1 | 1 | |
2 | 2 | import java.awt.BorderLayout; |
3 | import java.awt.Color; | |
4 | 3 | import java.awt.Component; |
5 | 4 | import java.awt.Dimension; |
6 | 5 | import java.awt.GridLayout; |
7 | 6 | import java.awt.event.ActionEvent; |
8 | 7 | import java.awt.event.ActionListener; |
9 | import java.awt.event.MouseAdapter; | |
10 | import java.awt.event.MouseEvent; | |
11 | ||
12 | 8 | import javax.swing.BorderFactory; |
13 | 9 | import javax.swing.Box; |
14 | 10 | import javax.swing.BoxLayout; |
23 | 19 | import tim.prune.UpdateMessageBroker; |
24 | 20 | import tim.prune.config.ColourScheme; |
25 | 21 | 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; | |
28 | 27 | |
29 | 28 | /** |
30 | 29 | * Class to show the popup window for setting the colours |
35 | 34 | private JButton _okButton = null; |
36 | 35 | /** Array of 8 colour patches */ |
37 | 36 | private ColourPatch[] _patches = null; |
37 | /** colourer selection panel */ | |
38 | private ColourerSelectorPanel _colourerSelector = null; | |
38 | 39 | /** Single colour chooser */ |
39 | 40 | private ColourChooser _colourChooser = null; |
40 | 41 | |
48 | 49 | ColourScheme.IDX_TEXT, ColourScheme.IDX_LINES |
49 | 50 | }; |
50 | 51 | |
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 | } | |
70 | 52 | |
71 | 53 | /** |
72 | 54 | * Constructor |
93 | 75 | JPanel mainPanel = new JPanel(); |
94 | 76 | mainPanel.setLayout(new BorderLayout(0, 10)); |
95 | 77 | |
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?) | |
99 | 83 | 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()); | |
101 | 89 | _patches = new ColourPatch[8]; |
102 | 90 | |
103 | 91 | ColourScheme scheme = Config.getColourScheme(); |
110 | 98 | // Top label and patch |
111 | 99 | colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2]))); |
112 | 100 | patch = new ColourPatch(scheme.getColour(INDICES[i*2])); |
113 | patch.addMouseListener(new PatchListener(patch)); | |
101 | patch.addMouseListener(new PatchListener(patch, _colourChooser)); | |
114 | 102 | colPanel.add(patch); |
115 | 103 | _patches[i*2] = patch; |
116 | 104 | // separator |
118 | 106 | // Bottom label and patch |
119 | 107 | colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2+1]))); |
120 | 108 | patch = new ColourPatch(scheme.getColour(INDICES[i*2+1])); |
121 | patch.addMouseListener(new PatchListener(patch)); | |
109 | patch.addMouseListener(new PatchListener(patch, _colourChooser)); | |
122 | 110 | colPanel.add(patch); |
123 | 111 | _patches[i*2+1] = patch; |
124 | 112 | |
125 | 113 | // Add column to panel |
126 | 114 | 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 | |
129 | 126 | mainPanel.add(centralPanel, BorderLayout.CENTER); |
130 | 127 | |
131 | 128 | // Buttons at the bottom |
175 | 172 | { |
176 | 173 | _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey())); |
177 | 174 | _dialog.setLocationRelativeTo(_parentFrame); |
175 | _colourChooser = new ColourChooser(_dialog); | |
178 | 176 | _dialog.getContentPane().add(makeContents()); |
179 | 177 | _dialog.pack(); |
180 | _colourChooser = new ColourChooser(_dialog); | |
181 | 178 | } |
182 | 179 | // Reset colours to current ones |
183 | 180 | ColourScheme scheme = Config.getColourScheme(); |
184 | 181 | for (int i=0; i<8; i++) { |
185 | 182 | _patches[i].setColour(scheme.getColour(INDICES[i])); |
186 | 183 | } |
184 | PointColourer colourer = Config.getPointColourer(); | |
185 | _colourerSelector.init(colourer, scheme.getColour(ColourScheme.IDX_POINT)); | |
187 | 186 | _dialog.setVisible(true); |
188 | 187 | _okButton.requestFocus(); |
189 | 188 | } |
199 | 198 | scheme.setColour(INDICES[i], _patches[i].getBackground()); |
200 | 199 | } |
201 | 200 | Config.updateColourScheme(); |
201 | PointColourer colourer = _colourerSelector.getSelectedColourer(); | |
202 | Config.updatePointColourer(colourer); | |
203 | _app.updatePointColourer(); | |
202 | 204 | UpdateMessageBroker.informSubscribers(); |
203 | 205 | } |
204 | 206 | } |
0 | 0 | package tim.prune.function; |
1 | ||
2 | import javax.swing.JOptionPane; | |
3 | 1 | |
4 | 2 | import tim.prune.App; |
5 | 3 | import tim.prune.DataSubscriber; |
6 | import tim.prune.GenericFunction; | |
7 | import tim.prune.I18nManager; | |
8 | 4 | import tim.prune.UpdateMessageBroker; |
9 | 5 | import tim.prune.config.Config; |
10 | 6 | |
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 | |
12 | 11 | { |
13 | 12 | |
14 | 13 | /** |
16 | 15 | * @param inApp App object |
17 | 16 | */ |
18 | 17 | public SetLineWidth(App inApp) { |
19 | super(inApp); | |
18 | super(inApp, 1, 4); | |
20 | 19 | } |
21 | 20 | |
22 | 21 | /** @return name key */ |
24 | 23 | return "function.setlinewidth"; |
25 | 24 | } |
26 | 25 | |
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 | } | |
27 | 35 | |
28 | 36 | /** |
29 | 37 | * Run function |
30 | 38 | */ |
31 | 39 | public void begin() |
32 | 40 | { |
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) | |
42 | 52 | { |
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); | |
53 | 55 | } |
54 | 56 | } |
55 | 57 | } |
155 | 155 | */ |
156 | 156 | private void finish() |
157 | 157 | { |
158 | // Store exaggeration factor in config | |
158 | // Store exaggeration factor and grid size in config | |
159 | 159 | 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 | ||
160 | 164 | ThreeDWindow window = WindowFactory.getWindow(_parentFrame); |
161 | 165 | if (window != null) |
162 | 166 | { |
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 | } |
283 | 283 | + getSvgValue(_svgHeightField, DEFAULT_SVG_HEIGHT) + "\n"); |
284 | 284 | writer.write("set out '" + svgFile.getAbsolutePath() + "'\n"); |
285 | 285 | } |
286 | else { | |
287 | // For screen output, gnuplot should use the default terminal (windows or x11 or wxt or something) | |
288 | } | |
286 | 289 | if (numCharts > 1) { |
287 | 290 | writer.write("set multiplot layout " + numCharts + ",1\n"); |
288 | 291 | } |
373 | 376 | break; |
374 | 377 | } |
375 | 378 | // 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); | |
377 | 380 | tempFile.deleteOnExit(); |
378 | 381 | // write out values for x and y to temporary file |
379 | 382 | FileWriter tempFileWriter = null; |
14 | 14 | import javax.swing.JPanel; |
15 | 15 | |
16 | 16 | import tim.prune.App; |
17 | import tim.prune.GenericFunction; | |
18 | 17 | import tim.prune.I18nManager; |
19 | 18 | import tim.prune.UpdateMessageBroker; |
20 | 19 | import tim.prune.data.DataPoint; |
23 | 22 | /** |
24 | 23 | * Class to provide the function for track compression |
25 | 24 | */ |
26 | public class CompressTrackFunction extends GenericFunction | |
25 | public class CompressTrackFunction extends MarkAndDeleteFunction | |
27 | 26 | { |
28 | 27 | private Track _track = null; |
29 | 28 | private JDialog _dialog = null; |
30 | 29 | private JButton _okButton = null; |
31 | 30 | private CompressionAlgorithm[] _algorithms = null; |
32 | 31 | private SummaryLabel _summaryLabel = null; |
33 | /** flag to remember whether the automatic deletion has been set to always */ | |
34 | private boolean _automaticallyDelete = false; | |
35 | 32 | |
36 | 33 | |
37 | 34 | /** |
186 | 183 | // Show confirmation dialog with OK button (not status bar message) |
187 | 184 | if (numMarked > 0) |
188 | 185 | { |
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); | |
206 | 187 | } |
207 | 188 | else |
208 | 189 | { |
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 | } |
0 | 0 | package tim.prune.function.compress; |
1 | 1 | |
2 | import javax.swing.JOptionPane; | |
3 | ||
4 | 2 | import tim.prune.App; |
5 | import tim.prune.GenericFunction; | |
6 | import tim.prune.I18nManager; | |
7 | 3 | import tim.prune.UpdateMessageBroker; |
8 | 4 | import tim.prune.data.DataPoint; |
9 | 5 | |
10 | 6 | /** |
11 | 7 | * Function to mark all the points in the selected rectangle |
12 | 8 | */ |
13 | public class MarkPointsInRectangleFunction extends GenericFunction | |
9 | public class MarkPointsInRectangleFunction extends MarkAndDeleteFunction | |
14 | 10 | { |
15 | 11 | /** Minimum and maximum latitude values of rectangle */ |
16 | 12 | private double _minLat = 0.0, _maxLat = 0.0; |
17 | 13 | /** Minimum and maximum longitude values of rectangle */ |
18 | 14 | 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; | |
21 | 15 | |
22 | 16 | |
23 | 17 | /** |
85 | 79 | final double pointLat = point.getLatitude().getDouble(); |
86 | 80 | final boolean insideRect = (pointLon >= _minLon && pointLon <= _maxLon |
87 | 81 | && pointLat >= _minLat && pointLat <= _maxLat); |
88 | // If so, then mark it | |
82 | // Mark it accordingly (also resetting points outside the rect to false) | |
89 | 83 | point.setMarkedForDeletion(insideRect); |
90 | 84 | if (insideRect) { |
91 | 85 | numMarked++; |
97 | 91 | // Confirm message showing how many marked |
98 | 92 | if (numMarked > 0) |
99 | 93 | { |
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); | |
117 | 95 | } |
118 | 96 | } |
119 | 97 | } |
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 | } |
16 | 16 | private static final String _toColLabel = I18nManager.getText("dialog.distances.column.to"); |
17 | 17 | /** Column heading (depends on metric/imperial settings) */ |
18 | 18 | private String _distanceLabel = null; |
19 | /** Previous distance units */ | |
20 | private Unit _previousDistUnit = null; | |
19 | 21 | |
20 | 22 | /** |
21 | 23 | * @return column count |
68 | 70 | Unit distUnit = Config.getUnitSet().getDistanceUnit(); |
69 | 71 | _distanceLabel = I18nManager.getText("fieldname.distance") + " (" + |
70 | 72 | I18nManager.getText(distUnit.getShortnameKey()) + ")"; |
73 | final boolean distUnitsChanged = (distUnit != _previousDistUnit); | |
74 | _previousDistUnit = distUnit; | |
75 | ||
71 | 76 | // Initialize array of distances |
72 | 77 | int numRows = getRowCount(); |
73 | 78 | if (_distances == null || _distances.length != numRows) { |
83 | 88 | _distances[i] = Distance.convertRadiansToDistance(rads); |
84 | 89 | } |
85 | 90 | } |
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 | } | |
88 | 98 | } |
89 | 99 | } |
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 | 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 | } |
31 | 31 | import tim.prune.data.Field; |
32 | 32 | import tim.prune.data.Photo; |
33 | 33 | import tim.prune.data.Selection; |
34 | import tim.prune.data.SourceInfo; | |
34 | 35 | import tim.prune.data.SpeedCalculator; |
35 | 36 | import tim.prune.data.SpeedValue; |
36 | 37 | import tim.prune.data.TrackInfo; |
48 | 49 | private JLabel _indexLabel = null; |
49 | 50 | private JLabel _latLabel = null, _longLabel = null; |
50 | 51 | private JLabel _altLabel = null; |
51 | private JLabel _timeLabel = null; | |
52 | private JLabel _ptDateLabel = null, _ptTimeLabel = null; | |
52 | 53 | private JLabel _descLabel = null; |
53 | 54 | private JLabel _speedLabel = null, _vSpeedLabel = null; |
54 | 55 | private JLabel _nameLabel = null, _typeLabel = null; |
56 | private JLabel _filenameLabel = null; | |
55 | 57 | |
56 | 58 | // Range details |
57 | 59 | private JLabel _rangeLabel = null; |
89 | 91 | private static final String LABEL_POINT_LATITUDE = I18nManager.getText("fieldname.latitude") + ": "; |
90 | 92 | private static final String LABEL_POINT_LONGITUDE = I18nManager.getText("fieldname.longitude") + ": "; |
91 | 93 | 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") + ": "; | |
93 | 96 | private static final String LABEL_POINT_WAYPOINTNAME = I18nManager.getText("fieldname.waypointname") + ": "; |
94 | 97 | private static final String LABEL_POINT_WAYPOINTTYPE = I18nManager.getText("fieldname.waypointtype") + ": "; |
95 | 98 | private static final String LABEL_POINT_DESCRIPTION = I18nManager.getText("fieldname.description") + ": "; |
96 | 99 | private static final String LABEL_POINT_SPEED = I18nManager.getText("fieldname.speed") + ": "; |
97 | 100 | private static final String LABEL_POINT_VERTSPEED = I18nManager.getText("fieldname.verticalspeed") + ": "; |
101 | private static final String LABEL_POINT_FILENAME = I18nManager.getText("details.track.file") + ": "; | |
98 | 102 | private static final String LABEL_RANGE_SELECTED = I18nManager.getText("details.range.selected") + ": "; |
99 | 103 | private static final String LABEL_RANGE_DURATION = I18nManager.getText("fieldname.duration") + ": "; |
100 | 104 | private static final String LABEL_RANGE_DISTANCE = I18nManager.getText("fieldname.distance") + ": "; |
130 | 134 | pointDetailsPanel.add(_longLabel); |
131 | 135 | _altLabel = new JLabel(""); |
132 | 136 | 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); | |
136 | 143 | _descLabel = new JLabel(""); |
137 | 144 | pointDetailsPanel.add(_descLabel); |
138 | 145 | _speedLabel = new JLabel(""); |
143 | 150 | pointDetailsPanel.add(_nameLabel); |
144 | 151 | _typeLabel = new JLabel(""); |
145 | 152 | pointDetailsPanel.add(_typeLabel); |
153 | _filenameLabel = new JLabel(""); | |
154 | pointDetailsPanel.add(_filenameLabel); | |
146 | 155 | pointDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); |
147 | 156 | |
148 | 157 | // range details panel |
295 | 304 | _latLabel.setText(""); |
296 | 305 | _longLabel.setText(""); |
297 | 306 | _altLabel.setText(""); |
298 | _timeLabel.setText(""); | |
307 | _ptDateLabel.setText(""); | |
308 | _ptTimeLabel.setText(""); | |
299 | 309 | _descLabel.setText(""); |
300 | 310 | _nameLabel.setText(""); |
301 | 311 | _typeLabel.setText(""); |
302 | 312 | _speedLabel.setText(""); |
303 | 313 | _vSpeedLabel.setText(""); |
314 | _filenameLabel.setText(""); | |
304 | 315 | } |
305 | 316 | else |
306 | 317 | { |
315 | 326 | I18nManager.getText(altUnit.getShortnameKey())) |
316 | 327 | : ""); |
317 | 328 | 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()); | |
320 | 331 | } |
321 | 332 | else { |
322 | _timeLabel.setText(""); | |
323 | _timeLabel.setToolTipText(""); | |
333 | _ptDateLabel.setText(""); | |
334 | _ptTimeLabel.setText(""); | |
324 | 335 | } |
325 | 336 | // Maybe the point has a description? |
326 | 337 | String pointDesc = currentPoint.getFieldValue(Field.DESCRIPTION); |
376 | 387 | _typeLabel.setText(LABEL_POINT_WAYPOINTTYPE + type); |
377 | 388 | } |
378 | 389 | 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 | } | |
379 | 409 | } |
380 | 410 | |
381 | 411 | // Update range details |
446 | 476 | String shortPath = shortenPath(fullPath); |
447 | 477 | _photoPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath); |
448 | 478 | _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()):""); | |
450 | 480 | _photoConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": " |
451 | 481 | + (currentPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED ? |
452 | 482 | I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes"))); |
482 | 512 | String shortPath = shortenPath(fullPath); |
483 | 513 | _audioPathLabel.setText(fullPath == null ? "" : LABEL_FULL_PATH + shortPath); |
484 | 514 | _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()):""); | |
486 | 516 | int audioLength = currentAudio.getLengthInSeconds(); |
487 | 517 | _audioLengthLabel.setText(audioLength < 0?"":LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(audioLength)); |
488 | 518 | _audioConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": " |
531 | 561 | { |
532 | 562 | final int DECIMAL_PLACES = 7; |
533 | 563 | if (inCoord == null) return ""; |
564 | String result = inCoord; | |
534 | 565 | final int dotPos = Math.max(inCoord.lastIndexOf('.'), inCoord.lastIndexOf(',')); |
535 | if (dotPos >= 0) { | |
566 | if (dotPos >= 0) | |
567 | { | |
536 | 568 | 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; | |
542 | 581 | } |
543 | 582 | |
544 | 583 | /** |
9 | 9 | { |
10 | 10 | |
11 | 11 | /** Icon for window */ |
12 | public static final String WINDOW_ICON = "window_icon.png"; | |
12 | public static final String WINDOW_ICON = "window_icon"; | |
13 | 13 | |
14 | 14 | /** Icon for scalebar button on main map display */ |
15 | 15 | public static final String SCALEBAR_BUTTON = "scalebar.gif"; |
20 | 20 | import tim.prune.UpdateMessageBroker; |
21 | 21 | import tim.prune.config.Config; |
22 | 22 | import tim.prune.data.AudioClip; |
23 | import tim.prune.data.DataPoint; | |
23 | 24 | import tim.prune.data.Field; |
24 | 25 | import tim.prune.data.Photo; |
25 | 26 | import tim.prune.data.RecentFile; |
27 | 28 | import tim.prune.data.Selection; |
28 | 29 | import tim.prune.data.Track; |
29 | 30 | import tim.prune.data.TrackInfo; |
30 | import tim.prune.function.RearrangeWaypointsFunction.Rearrange; | |
31 | import tim.prune.function.ChooseSingleParameter; | |
31 | 32 | import tim.prune.function.browser.UrlGenerator; |
32 | 33 | |
33 | 34 | /** |
59 | 60 | private JMenuItem _compressItem = null; |
60 | 61 | private JMenuItem _markRectangleItem = null; |
61 | 62 | private JMenuItem _deleteMarkedPointsItem = null; |
63 | private JMenuItem _deleteByDateItem = null; | |
62 | 64 | private JMenuItem _interpolateItem = null; |
63 | 65 | private JMenuItem _averageItem = null; |
64 | 66 | private JMenuItem _selectAllItem = null; |
65 | 67 | private JMenuItem _selectNoneItem = null; |
68 | private JMenuItem _selectSegmentItem = null; | |
66 | 69 | private JMenuItem _selectStartItem = null; |
67 | 70 | private JMenuItem _selectEndItem = null; |
68 | 71 | private JMenuItem _findWaypointItem = null; |
71 | 74 | private JMenuItem _addTimeOffsetItem = null; |
72 | 75 | private JMenuItem _addAltitudeOffsetItem = null; |
73 | 76 | private JMenuItem _mergeSegmentsItem = null; |
74 | private JMenu _rearrangeMenu = null; | |
77 | private JMenuItem _rearrangeWaypointsItem = null; | |
75 | 78 | private JMenuItem _splitSegmentsItem = null; |
76 | 79 | private JMenuItem _sewSegmentsItem = null; |
77 | 80 | private JMenuItem _cutAndMoveItem = null; |
308 | 311 | }); |
309 | 312 | _deleteMarkedPointsItem.setEnabled(false); |
310 | 313 | trackMenu.add(_deleteMarkedPointsItem); |
314 | _deleteByDateItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_BY_DATE, false); | |
315 | trackMenu.add(_deleteByDateItem); | |
311 | 316 | trackMenu.addSeparator(); |
312 | 317 | // 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); | |
340 | 320 | // Split track segments |
341 | 321 | _splitSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SPLIT_SEGMENTS, false); |
342 | 322 | trackMenu.add(_splitSegmentsItem); |
368 | 348 | } |
369 | 349 | }); |
370 | 350 | rangeMenu.add(_selectNoneItem); |
351 | _selectSegmentItem = makeMenuItem(FunctionLibrary.FUNCTION_SELECT_SEGMENT); | |
352 | rangeMenu.add(_selectSegmentItem); | |
371 | 353 | rangeMenu.addSeparator(); |
372 | 354 | _selectStartItem = new JMenuItem(I18nManager.getText("menu.range.start")); |
373 | 355 | _selectStartItem.setEnabled(false); |
415 | 397 | _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES, false); |
416 | 398 | rangeMenu.add(_deleteFieldValuesItem); |
417 | 399 | rangeMenu.addSeparator(); |
418 | _interpolateItem = makeMenuItem(FunctionLibrary.FUNCTION_INTERPOLATE, false); | |
400 | _interpolateItem = makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_INTERPOLATE), false); | |
419 | 401 | rangeMenu.add(_interpolateItem); |
420 | 402 | _averageItem = new JMenuItem(I18nManager.getText("menu.range.average")); |
421 | 403 | _averageItem.addActionListener(new ActionListener() { |
656 | 638 | // Set colours |
657 | 639 | settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS)); |
658 | 640 | // 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))); | |
660 | 642 | // Set language |
661 | 643 | settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE)); |
644 | // Set altitude tolerance | |
645 | settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_ALTITUDE_TOLERANCE))); | |
662 | 646 | settingsMenu.addSeparator(); |
663 | 647 | // Save configuration |
664 | 648 | settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SAVECONFIG)); |
876 | 860 | _compressItem.setEnabled(hasData); |
877 | 861 | _markRectangleItem.setEnabled(hasData); |
878 | 862 | _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints()); |
879 | _rearrangeMenu.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints()); | |
863 | _rearrangeWaypointsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints()); | |
880 | 864 | _splitSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3); |
881 | 865 | _sewSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3); |
882 | 866 | _selectAllItem.setEnabled(hasData); |
894 | 878 | _findWaypointItem.setEnabled(hasData && _track.hasWaypoints()); |
895 | 879 | // have we got a cache? |
896 | 880 | _downloadSrtmItem.setEnabled(hasData && Config.getConfigString(Config.KEY_DISK_CACHE) != null); |
881 | // have we got any timestamps? | |
882 | _deleteByDateItem.setEnabled(hasData && _track.hasData(Field.TIMESTAMP)); | |
897 | 883 | |
898 | 884 | // is undo available? |
899 | 885 | boolean hasUndo = !_app.getUndoStack().isEmpty(); |
901 | 887 | _undoButton.setEnabled(hasUndo); |
902 | 888 | _clearUndoItem.setEnabled(hasUndo); |
903 | 889 | // is there a current point? |
904 | boolean hasPoint = (hasData && _selection.getCurrentPointIndex() >= 0); | |
890 | DataPoint currPoint = _app.getTrackInfo().getCurrentPoint(); | |
891 | boolean hasPoint = (currPoint != null); | |
905 | 892 | _editPointItem.setEnabled(hasPoint); |
906 | 893 | _editPointButton.setEnabled(hasPoint); |
907 | 894 | _editWaypointNameItem.setEnabled(hasPoint); |
912 | 899 | _selectEndItem.setEnabled(hasPoint); |
913 | 900 | _selectEndButton.setEnabled(hasPoint); |
914 | 901 | _duplicatePointItem.setEnabled(hasPoint); |
902 | // is it a waypoint? | |
903 | _selectSegmentItem.setEnabled(hasPoint && !currPoint.isWaypoint()); | |
915 | 904 | // are there any photos? |
916 | 905 | boolean anyPhotos = _app.getTrackInfo().getPhotoList().getNumPhotos() > 0; |
917 | 906 | _saveExifItem.setEnabled(anyPhotos && _app.getTrackInfo().getPhotoList().hasMediaWithFile()); |
938 | 927 | _selectNoAudioItem.setEnabled(hasAudio); |
939 | 928 | _removeAudioItem.setEnabled(hasAudio); |
940 | 929 | _connectAudioItem.setEnabled(hasAudio && hasPoint && currentAudio.getDataPoint() == null); |
941 | _disconnectAudioItem.setEnabled(hasAudio && _app.getTrackInfo().getCurrentAudio().getDataPoint() != null); | |
930 | _disconnectAudioItem.setEnabled(hasAudio && currentAudio.getDataPoint() != null); | |
942 | 931 | _correlateAudiosItem.setEnabled(anyAudios && hasData); |
943 | 932 | // is there a current range? |
944 | 933 | boolean hasRange = (hasData && _selection.hasRangeSelected()); |
982 | 971 | for (int i=0; i<numRecentFiles; i++) |
983 | 972 | { |
984 | 973 | 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()); | |
987 | 977 | } |
988 | 978 | } |
989 | 979 | else |
10 | 10 | import javax.swing.JPanel; |
11 | 11 | |
12 | 12 | import tim.prune.I18nManager; |
13 | import tim.prune.config.Config; | |
13 | 14 | import tim.prune.threedee.TerrainDefinition; |
14 | 15 | |
15 | 16 | /** |
43 | 44 | JLabel label = new JLabel(I18nManager.getText("dialog.3d.terraingridsize") + ": "); |
44 | 45 | add(label); |
45 | 46 | _gridSizeField = new WholeNumberField(4); |
46 | _gridSizeField.setValue(50); // default grid size | |
47 | _gridSizeField.setValue(Config.getConfigInt(Config.KEY_TERRAIN_GRID_SIZE)); // default grid size | |
47 | 48 | _gridSizeField.setMaximumSize(new Dimension(100, 50)); |
48 | 49 | _gridSizeField.setEnabled(false); |
49 | 50 | add(_gridSizeField); |
0 | 0 | package tim.prune.gui; |
1 | 1 | |
2 | 2 | import javax.swing.JTextField; |
3 | import javax.swing.event.DocumentEvent; | |
4 | import javax.swing.event.DocumentListener; | |
3 | 5 | import javax.swing.text.AttributeSet; |
4 | 6 | import javax.swing.text.BadLocationException; |
5 | 7 | import javax.swing.text.PlainDocument; |
58 | 60 | { |
59 | 61 | super(inMaxDigits); |
60 | 62 | 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 | }); | |
61 | 68 | } |
62 | 69 | |
63 | 70 | /** |
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 | } |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | 0 | package tim.prune.gui.map; |
1 | 1 | |
2 | import java.awt.AlphaComposite; | |
2 | 3 | import java.awt.BasicStroke; |
3 | 4 | import java.awt.BorderLayout; |
4 | 5 | import java.awt.Color; |
57 | 58 | import tim.prune.function.edit.FieldEdit; |
58 | 59 | import tim.prune.function.edit.FieldEditList; |
59 | 60 | import tim.prune.gui.IconManager; |
61 | import tim.prune.gui.colour.PointColourer; | |
60 | 62 | import tim.prune.tips.TipManager; |
61 | 63 | |
62 | 64 | /** |
83 | 85 | private MapTileManager _tileManager = new MapTileManager(this); |
84 | 86 | /** Image to display */ |
85 | 87 | private BufferedImage _mapImage = null; |
88 | /** Second image for drawing track (only needed for alpha blending) */ | |
89 | private BufferedImage _trackImage = null; | |
86 | 90 | /** Slider for transparency */ |
87 | 91 | private JSlider _transparencySlider = null; |
88 | 92 | /** Checkbox for scale bar */ |
483 | 487 | else if (px > (getWidth()-PAN_DISTANCE)) { |
484 | 488 | panX = AUTOPAN_DISTANCE + px - getWidth(); |
485 | 489 | } |
486 | if (py < PAN_DISTANCE) { | |
490 | if (py < (2*PAN_DISTANCE)) { | |
487 | 491 | panY = py - AUTOPAN_DISTANCE; |
488 | 492 | } |
489 | 493 | if (py > (getHeight()-PAN_DISTANCE)) { |
579 | 583 | } |
580 | 584 | } |
581 | 585 | |
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 | } | |
590 | 634 | |
591 | 635 | // free g |
592 | 636 | g.dispose(); |
593 | 637 | |
594 | // Zoom to fit if no points found | |
595 | if (pointsPainted <= 0 && _checkBounds) { | |
596 | zoomToFit(); | |
597 | _recalculate = true; | |
598 | repaint(); | |
599 | } | |
600 | 638 | _checkBounds = false; |
601 | 639 | // enable / disable transparency slider |
602 | 640 | _transparencySlider.setEnabled(showMap); |
612 | 650 | { |
613 | 651 | // Set up colours |
614 | 652 | 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(); | |
624 | 659 | |
625 | 660 | final int winWidth = getWidth(); |
626 | 661 | final int winHeight = getHeight(); |
658 | 693 | currPointVisible = px >= 0 && px < winWidth && py >= 0 && py < winHeight; |
659 | 694 | isWaypoint = _track.getPoint(i).isWaypoint(); |
660 | 695 | anyWaypoints = anyWaypoints || isWaypoint; |
661 | if (currPointVisible) | |
662 | { | |
663 | if (!isWaypoint) | |
696 | if (!isWaypoint) | |
697 | { | |
698 | if (currPointVisible || prevPointVisible) | |
664 | 699 | { |
665 | // Draw rectangle for track point | |
700 | // For track points, work out which colour to use | |
666 | 701 | if (_track.getPoint(i).getDeleteFlag()) { |
667 | 702 | inG.setColor(currentColour); |
668 | 703 | } |
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 | { | |
670 | 711 | inG.setColor(pointColour); |
671 | 712 | } |
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 | ||
678 | 722 | // 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) | |
681 | 724 | && !_track.getPoint(i).getSegmentStart()) |
682 | 725 | { |
683 | 726 | inG.drawLine(prevX, prevY, px, py); |
859 | 902 | } |
860 | 903 | |
861 | 904 | /** |
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 | /** | |
874 | 905 | * Inform that tiles have been updated and the map can be repainted |
875 | 906 | * @param inIsOk true if data loaded ok, false for error |
876 | 907 | */ |
1049 | 1080 | else if (_drawMode == MODE_DRAW_POINTS_CONT) |
1050 | 1081 | { |
1051 | 1082 | DataPoint point = createPointFromClick(inE.getX(), inE.getY()); |
1052 | _app.createPoint(point); | |
1053 | point.setSegmentStart(false); | |
1083 | _app.createPoint(point, false); // not a new segment | |
1054 | 1084 | } |
1055 | 1085 | } |
1056 | 1086 | else if (inE.getClickCount() == 2) |
86 | 86 | // check prefix |
87 | 87 | try { |
88 | 88 | 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. | |
89 | 91 | } |
90 | 92 | catch (MalformedURLException e) |
91 | 93 | { |
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 | } |
38 | 38 | { |
39 | 39 | double speedValue = speed.getValue(); |
40 | 40 | _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;} | |
43 | 43 | _hasData = true; |
44 | 44 | } |
45 | 45 | _pointHasData[i] = speed.isValid(); |
39 | 39 | // Store the value and maintain max and min values |
40 | 40 | double speedValue = speed.getValue(); |
41 | 41 | _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;} | |
44 | 44 | _hasData = true; |
45 | 45 | } |
46 | 46 | _pointHasData[i] = speed.isValid(); |
9 | 9 | menu.track.undo=Herroep |
10 | 10 | menu.track.clearundo=Herroep Lys Skoonmaak |
11 | 11 | 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 | |
16 | 13 | menu.range=Reeks |
17 | 14 | menu.range.all=Selekteer Alles |
18 | 15 | menu.range.none=Selekteer Niks |
274 | 271 | dialog.correlate.timestamp.beginning=Begin |
275 | 272 | dialog.correlate.timestamp.middle=Middel |
276 | 273 | 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 | |
282 | 281 | dialog.compress.closepoints.title=Naby punt verwydering |
283 | 282 | dialog.compress.closepoints.paramdesc=Span faktor |
284 | 283 | dialog.compress.wackypoints.title=Gekkige punt verwydering |
12 | 12 | menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo |
13 | 13 | menu.track.markrectangle=Ozna\u010dit body v obd\u00e9ln\u00edku |
14 | 14 | 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 | |
19 | 16 | menu.range=Rozmez\u00ed |
20 | 17 | menu.range.all=Vybrat v\u0161e |
21 | 18 | menu.range.none=Zru\u0161it v\u00fdb\u011br |
412 | 409 | dialog.correlate.select.audioname=N\u00e1zev audionahr\u00e1vky |
413 | 410 | dialog.correlate.select.audiolater=Audio pozd\u011bj\u0161\u00ed |
414 | 411 | 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 | |
420 | 419 | dialog.compress.closepoints.title=Odstran\u011bn\u00ed bl\u00edzk\u00fdch bod\u016f |
421 | 420 | dialog.compress.closepoints.paramdesc=Koeficient bl\u00edzkosti |
422 | 421 | dialog.compress.wackypoints.title=Odstran\u011bn\u00ed ust\u0159elen\u00fdch bod\u016f |
848 | 847 | error.cache.notthere=Nepoda\u0159ilo se nal\u00e9zt adres\u00e1\u0159 s cache map. |
849 | 848 | error.cache.empty=Adres\u00e1\u0159 s cache map je pr\u00e1zdn\u00fd. |
850 | 849 | error.cache.cannotdelete=Nelze smazat soubory map. |
851 | error.interpolate.invalidparameter=Po\u010det bod\u016f mus\u00ed b\u00fdt mezi 1 a 1000 | |
852 | 850 | error.learnestimationparams.failed=Na z\u00e1klad\u011b t\u00e9to stopy nelze vypo\u010d\u00edtat parametr.\nZkuste jin\u00e9 stopy. |
853 | 851 | error.tracksplit.nosplit=Stopu nen\u00ed mo\u017en\u00e9 rozd\u011blit |
854 | 852 | error.downloadsrtm.nocache=Nepoda\u0159ilo se ulo\u017eit soubory.\nPros\u00edm ov\u011b\u0159te diskovou cache. |
9 | 9 | menu.track.undo=Fortryd |
10 | 10 | menu.track.clearundo=Nulstil fortrydelsesliste |
11 | 11 | 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 | |
14 | 14 | menu.range=Omr\u00e5de |
15 | 15 | menu.range.all=V\u00e6lg alle |
16 | 16 | menu.range.none=Ingen valgt |
12 | 12 | menu.track.clearundo=Liste der letzten \u00c4nderungen l\u00f6schen |
13 | 13 | menu.track.markrectangle=Punkte im Viereck markieren |
14 | 14 | 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 | |
19 | 16 | menu.range=Bereich |
20 | 17 | menu.range.all=Alles markieren |
21 | 18 | menu.range.none=Nichts markieren |
86 | 83 | function.exportpov=POV exportieren |
87 | 84 | function.exportsvg=SVG exportieren |
88 | 85 | function.exportimage=Bild exportieren |
89 | function.editwaypointname=Name des Punkts bearbeiten | |
86 | function.editwaypointname=Namen des Punkts bearbeiten | |
90 | 87 | function.compress=Track komprimieren |
91 | 88 | function.deleterange=Bereich l\u00f6schen |
92 | 89 | function.croptrack=Track zuschneiden |
93 | 90 | function.interpolate=Punkte interpolieren |
91 | function.deletebydate=Punkte nach Datum l\u00f6schen | |
94 | 92 | function.addtimeoffset=Zeitverschiebung aufrechnen |
95 | 93 | function.addaltitudeoffset=H\u00f6henverschiebung aufrechnen |
96 | 94 | function.convertnamestotimes=Namen der Wegpunkte in Zeitstempel umwandeln |
105 | 103 | function.learnestimationparams=Zeitparameter erlernen |
106 | 104 | function.setmapbg=Karte Hintergrund setzen |
107 | 105 | function.setpaths=Programmpfade setzen |
106 | function.selectsegment=Aktuellen Abschnitt markieren | |
108 | 107 | function.splitsegments=In Trackabschnitte schneiden |
109 | 108 | function.sewsegments=Trackabschnitte zusammenf\u00fcgen |
110 | 109 | function.getgpsies=Tracks bei GPSies.com herunterladen |
140 | 139 | function.diskcache=Karten auf Festplatte speichern |
141 | 140 | function.managetilecache=Kartenkacheln verwalten |
142 | 141 | function.getweatherforecast=Wettervorhersage herunterladen |
142 | function.setaltitudetolerance=Altitudtoleranz einstellen | |
143 | 143 | |
144 | 144 | # Dialogs |
145 | 145 | dialog.exit.confirm.title=GpsPrune beenden |
378 | 378 | dialog.gpsies.activity.skating=Inline-Skating |
379 | 379 | dialog.wikipedia.column.name=Artikelname |
380 | 380 | dialog.wikipedia.column.distance=Entfernung |
381 | dialog.wikipedia.nonefound=Keine Punkte gefunden | |
381 | 382 | dialog.correlate.notimestamps=Die Punkte enthalten keine Zeitangaben, deshalb k\u00f6nnen die Fotos nicht zugeordnet werden. |
382 | 383 | dialog.correlate.nouncorrelatedphotos=Alle Fotos sind schon zugeordnet.\nWollen Sie trotzdem fortfahren? |
383 | 384 | dialog.correlate.nouncorrelatedaudios=Alle Audiodateien sind schon zugeordnet.\nWollen Sie trotzdem fortfahren? |
411 | 412 | dialog.correlate.audioselect.intro=W\u00e4hlen Sie eines dieser Audiodateien aus, um die Zeitdifferenz zu berechnen |
412 | 413 | dialog.correlate.select.audioname=Audio Name |
413 | 414 | dialog.correlate.select.audiolater=Audio sp\u00e4ter |
415 | dialog.rearrangewaypoints.desc=Setzen Sie das Ziel und die Reihenfolge der Wegpunkte | |
414 | 416 | 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 | |
420 | 424 | dialog.compress.closepoints.title=Nahegelegene Punkte entfernen |
421 | 425 | dialog.compress.closepoints.paramdesc=Span-Faktor |
422 | 426 | dialog.compress.wackypoints.title=Ungew\u00f6hnliche Punkte entfernen |
474 | 478 | dialog.checkversion.releasedate2=ver\u00f6ffentlicht worden. |
475 | 479 | dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://gpsprune.activityworkshop.net/download.html. |
476 | 480 | 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> | |
478 | 482 | dialog.keys.normalmodifier=Strg |
479 | 483 | dialog.keys.macmodifier=Kommando |
480 | 484 | dialog.saveconfig.desc=Die folgende Einstellungen k\u00f6nnen gespeichert werden: |
514 | 518 | dialog.colourchooser.red=Rot |
515 | 519 | dialog.colourchooser.green=Gr\u00fcn |
516 | 520 | 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 | |
517 | 534 | dialog.setlanguage.firstintro=Sie k\u00f6nnen entweder eine von den mitgelieferten Sprachen<p>oder eine Text-Datei ausw\u00e4hlen. |
518 | 535 | dialog.setlanguage.secondintro=Sie m\u00fcssen Ihre Einstellungen speichern und dann<p>GpsPrune neu starten, um die Sprache zu \u00e4ndern. |
519 | 536 | dialog.setlanguage.language=Sprache |
563 | 580 | dialog.weather.temp=Temp |
564 | 581 | dialog.weather.humidity=R.L. |
565 | 582 | 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 | |
566 | 590 | |
567 | 591 | # 3d window |
568 | 592 | dialog.3d.title=GpsPrune-3D-Ansicht |
717 | 741 | fieldname.altitude=H\u00f6he |
718 | 742 | fieldname.timestamp=Zeitstempel |
719 | 743 | fieldname.time=Zeit |
744 | fieldname.date=Datum | |
720 | 745 | fieldname.waypointname=Name |
721 | 746 | fieldname.waypointtype=Typ |
722 | fieldname.newsegment=Segment | |
747 | fieldname.newsegment=Abschnitt | |
723 | 748 | fieldname.custom=Custom |
724 | 749 | fieldname.prefix=Feld |
725 | 750 | fieldname.distance=L\u00e4nge |
845 | 870 | error.cache.notthere=Der Ordner wurde nicht gefunden |
846 | 871 | error.cache.empty=Der Ordner ist leer |
847 | 872 | error.cache.cannotdelete=Es konnte keine Kacheln gel\u00f6scht werden |
848 | error.interpolate.invalidparameter=Die Anzahl der Punkte muss zwischen 1 und 1000 liegen | |
849 | 873 | error.learnestimationparams.failed=Mit diesem Track k\u00f6nnen die Parameter nicht berechnet werden.\nVersuchen Sie mit mehreren Tracks. |
850 | 874 | error.tracksplit.nosplit=Der Track konnte nicht aufgesplittet werden. |
851 | 875 | error.downloadsrtm.nocache=Die Dateien konnten nicht gespeichert werden.\nBitte pr\u00fcfen Sie den Kartenordner nach. |
12 | 12 | menu.track.clearundo=Undo-Liste l\u00f6sche |
13 | 13 | menu.track.markrectangle=P\u00fcnkte inem Viereck markiere |
14 | 14 | 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 | |
19 | 16 | menu.range=Beriich |
20 | 17 | menu.range.all=Alles selektiere |
21 | 18 | menu.range.none=N\u00fc\u00fct selektiere |
85 | 82 | function.exportpov=POV exportier\u00e4 |
86 | 83 | function.exportsvg=SVG exportier\u00e4 |
87 | 84 | function.exportimage=Bild exportier\u00e4 |
88 | function.editwaypointname=Waypoint Name editiere | |
85 | function.editwaypointname=Waypoint Namen editiere | |
89 | 86 | function.compress=Track komprimier\u00e4 |
90 | 87 | function.deleterange=Beriich l\u00f6sche |
91 | 88 | function.croptrack=Track zuschniide |
89 | function.deletebydate=P\u00fcnkte na Datum l\u00f6sche | |
92 | 90 | function.addtimeoffset=Ziitverschiebig zutue |
93 | 91 | function.addaltitudeoffset=H\u00f6chiverschiebig zutue |
94 | 92 | function.findwaypoint=Waypoint suech\u00e4 |
102 | 100 | function.estimatetime=Ziit absch\u00e4tze |
103 | 101 | function.learnestimationparams=Ziitparameter erlerne |
104 | 102 | function.setmapbg=Karte Hintegrund setz\u00e4 |
103 | function.selectsegment=Aktuelli Segm\u00e4nt selektiere | |
105 | 104 | function.splitsegments=In Tracksegm\u00e4nte schniide |
106 | 105 | function.sewsegments=Tracksegm\u00e4nte z\u00e4mef\u00fcge |
107 | 106 | function.getgpsies=Gpsies Tracks hol\u00e4 |
135 | 134 | function.diskcache=Karten uufem Disk speichere |
136 | 135 | function.managetilecache=Kartebildli verwolte |
137 | 136 | function.getweatherforecast=W\u00e4tterprognose abalade |
137 | function.setaltitudetolerance=H\u00f6chitoleranz iistelle | |
138 | 138 | |
139 | 139 | # Dialogs |
140 | 140 | dialog.exit.confirm.title=GpsPrune be\u00e4nde |
161 | 161 | dialog.openoptions.deliminfo.fields=F\u00e4ldere |
162 | 162 | dialog.openoptions.deliminfo.norecords=Kei Rekords |
163 | 163 | 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 | |
166 | 166 | dialog.openoptions.vspeed.positiveup=Positive bed\u00fc\u00fctet uufe |
167 | 167 | dialog.openoptions.vspeed.positivedown=Positive bed\u00fc\u00fctet abe |
168 | 168 | dialog.open.contentsdoubled=Dieses File h\u00e4t zwei Kopien von j\u00e4dem Punkt,\neimol als Waypoint und eimol als Trackpunkt. |
373 | 373 | dialog.gpsies.activity.skating=Inline-Skate |
374 | 374 | dialog.wikipedia.column.name=Artikelname |
375 | 375 | dialog.wikipedia.column.distance=Entf\u00e4rnig |
376 | dialog.wikipedia.nonefound=Kei Wiki-Iitr\u00e4ge gfunde | |
376 | 377 | 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. |
377 | 378 | dialog.correlate.nouncorrelatedphotos=Alle F\u00f6telis sin scho korreliert.\nWend Sie trotzdem fortsetz\u00e4? |
378 | 379 | dialog.correlate.nouncorrelatedaudios=Alle Audios sin scho korreliert.\nWend Sie trotzdem fortsetz\u00e4? |
406 | 407 | dialog.correlate.audioselect.intro=W\u00e4hlet Sie eini vo deren Audios uus, um die Ziitdiffer\u00e4nz zu ber\u00e4chn\u00e4 |
407 | 408 | dialog.correlate.select.audioname=Audio Name |
408 | 409 | 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 | |
415 | 419 | dialog.compress.duplicates.title=Duplikate entf\u00e4rn\u00e4 |
416 | 420 | dialog.compress.closepoints.title=N\u00f6chigl\u00e4geni P\u00fcnkte entf\u00e4rn\u00e4 |
417 | 421 | dialog.compress.closepoints.paramdesc=Span Faktor |
509 | 513 | dialog.colourchooser.red=Rot |
510 | 514 | dialog.colourchooser.green=Gr\u00fcen |
511 | 515 | 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 | |
512 | 529 | dialog.setlanguage.firstintro=Sie k\u00f6nnet entweder eini vo den iigebouti Sproche<p>oder ne Text-Datei uusw\u00e4hle. |
513 | 530 | dialog.setlanguage.secondintro=Sie m\u00fcnt Ihri Iistellige speichere und dann<p>GpsPrune wieder neustarte um die Sproch z'\u00e4ndere. |
514 | 531 | dialog.setlanguage.language=Sproch |
558 | 575 | dialog.weather.temp=Temp |
559 | 576 | dialog.weather.humidity=R.L. |
560 | 577 | 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 | |
561 | 585 | |
562 | 586 | # 3d window |
563 | 587 | dialog.3d.title=GpsPrune Dr\u00fc\u00fc-d Aasicht |
611 | 635 | tip.learntimeparams=Wenn Sie Track -> Ziitparameter erlerne mit Ihren Tracks benutze\ndann werdet d ber\u00e4chneti Werte gnauer. |
612 | 636 | tip.downloadsrtm=Sie k\u00f6nnet d Funktion beschleunige indem Sie\nOnline -> SRTM Files abalade uufrufe\num d Date lokal z'speichere. |
613 | 637 | 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. | |
615 | 639 | |
616 | 640 | # Buttons |
617 | 641 | button.ok=OK |
712 | 736 | fieldname.altitude=H\u00f6chi |
713 | 737 | fieldname.timestamp=Ziitst\u00e4mpel |
714 | 738 | fieldname.time=Ziit |
739 | fieldname.date=Tag | |
715 | 740 | fieldname.waypointname=Name |
716 | 741 | fieldname.waypointtype=Typ |
717 | 742 | fieldname.newsegment=Segm\u00e4nt |
840 | 865 | error.cache.notthere=D Ordner isch n\u00f6d gfunde worde |
841 | 866 | error.cache.empty=D Ordner h\u00e4t n\u00fc\u00fct drinne |
842 | 867 | 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 | |
844 | 868 | error.learnestimationparams.failed=Mit dere Track k\u00f6nnet die Parameter n\u00f6d br\u00e4chnet werde.\nVersuechet Sie mit mehreri Tracks. |
845 | 869 | error.tracksplit.nosplit=Es isch n\u00f6d m\u00f6glech gsi, den Track uufz'schniide. |
846 | 870 | error.downloadsrtm.nocache=Die Files k\u00f6nnet n\u00f6d gspeicheret werde.\nBitte pr\u00fcefet Sie den Kartenordner na. |
12 | 12 | menu.track.clearundo=Clear undo list |
13 | 13 | menu.track.markrectangle=Mark points in rectangle |
14 | 14 | 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 | |
19 | 15 | menu.range=Range |
20 | 16 | menu.range.all=Select all |
21 | 17 | menu.range.none=Select none |
91 | 87 | function.deleterange=Delete range |
92 | 88 | function.croptrack=Crop track |
93 | 89 | function.interpolate=Interpolate points |
90 | function.deletebydate=Delete points by date | |
94 | 91 | function.addtimeoffset=Add time offset |
95 | 92 | function.addaltitudeoffset=Add altitude offset |
96 | 93 | function.findwaypoint=Find waypoint |
94 | function.rearrangewaypoints=Rearrange waypoints | |
97 | 95 | function.convertnamestotimes=Convert waypoint names to times |
98 | 96 | function.deletefieldvalues=Delete field values |
99 | 97 | function.pastecoordinates=Enter new coordinates |
103 | 101 | function.fullrangedetails=Full range details |
104 | 102 | function.estimatetime=Estimate time |
105 | 103 | function.learnestimationparams=Learn time estimation parameters |
104 | function.selectsegment=Select current segment | |
106 | 105 | function.splitsegments=Split track into segments |
107 | 106 | function.sewsegments=Sew track segments together |
108 | 107 | function.getgpsies=Get Gpsies tracks |
140 | 139 | function.diskcache=Save maps to disk |
141 | 140 | function.managetilecache=Manage tile cache |
142 | 141 | function.getweatherforecast=Get weather forecast |
142 | function.setaltitudetolerance=Set altitude tolerance | |
143 | 143 | |
144 | 144 | # Dialogs |
145 | 145 | dialog.exit.confirm.title=Exit GpsPrune |
217 | 217 | dialog.save.table.hasdata=Has data |
218 | 218 | dialog.save.table.save=Save |
219 | 219 | dialog.save.headerrow=Output header row |
220 | dialog.save.coordinateunits=Coordinate units | |
220 | dialog.save.coordinateunits=Coordinate format | |
221 | 221 | dialog.save.altitudeunits=Altitude units |
222 | 222 | dialog.save.timestampformat=Timestamp format |
223 | 223 | dialog.save.overwrite.title=File already exists |
378 | 378 | dialog.gpsies.activity.skating=Skating |
379 | 379 | dialog.wikipedia.column.name=Article name |
380 | 380 | dialog.wikipedia.column.distance=Distance |
381 | dialog.wikipedia.nonefound=No wikipedia entries found | |
381 | 382 | dialog.correlate.notimestamps=There are no timestamps in the data points, so there is nothing to correlate with the photos. |
382 | 383 | dialog.correlate.nouncorrelatedphotos=There are no uncorrelated photos.\nAre you sure you want to continue? |
383 | 384 | dialog.correlate.nouncorrelatedaudios=There are no uncorrelated audios.\nAre you sure you want to continue? |
411 | 412 | dialog.correlate.audioselect.intro=Select one of these correlated audios to use as the time offset |
412 | 413 | dialog.correlate.select.audioname=Audio name |
413 | 414 | dialog.correlate.select.audiolater=Audio later |
415 | dialog.rearrangewaypoints.desc=Select the destination and sort order of the waypoints | |
414 | 416 | 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 | |
420 | 424 | dialog.compress.duplicates.title=Duplicate removal |
421 | 425 | dialog.compress.closepoints.title=Nearby point removal |
422 | 426 | dialog.compress.closepoints.paramdesc=Span factor |
514 | 518 | dialog.colourchooser.red=Red |
515 | 519 | dialog.colourchooser.green=Green |
516 | 520 | 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 | |
517 | 534 | dialog.setlanguage.firstintro=You can either select one of the included languages,<p>or select a text file to use instead. |
518 | 535 | dialog.setlanguage.secondintro=You need to save your settings and then<p>restart GpsPrune to change the language. |
519 | 536 | dialog.setlanguage.language=Language |
563 | 580 | dialog.weather.temp=Temp |
564 | 581 | dialog.weather.humidity=Humidity |
565 | 582 | 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 | |
566 | 590 | |
567 | 591 | # 3d window |
568 | 592 | dialog.3d.title=GpsPrune Three-d view |
717 | 741 | fieldname.altitude=Altitude |
718 | 742 | fieldname.timestamp=Time |
719 | 743 | fieldname.time=Time |
744 | fieldname.date=Date | |
720 | 745 | fieldname.waypointname=Name |
721 | 746 | fieldname.waypointtype=Type |
722 | 747 | fieldname.newsegment=Segment |
851 | 876 | error.cache.notthere=The tile cache directory was not found |
852 | 877 | error.cache.empty=The tile cache directory is empty |
853 | 878 | error.cache.cannotdelete=No tiles could be deleted |
854 | error.interpolate.invalidparameter=The number of points must be between 1 and 1000 | |
855 | 879 | error.learnestimationparams.failed=Cannot learn the parameters from this track.\nTry loading more tracks. |
856 | 880 | error.tracksplit.nosplit=The track could not be split |
857 | 881 | error.downloadsrtm.nocache=The files could not be saved.\nPlease check the disk cache. |
6 | 6 | # Dialogs |
7 | 7 | dialog.exportkml.trackcolour=Track color |
8 | 8 | dialog.saveconfig.prune.languagecode=Language code (EN_US) |
9 | dialog.saveconfig.prune.colourscheme=Color scheme | |
10 | dialog.saveconfig.prune.kmltrackcolour=KML track color | |
9 | 11 | dialog.setcolours.intro=Click on a color patch to change the color |
10 | 12 | 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 | |
11 | 19 | |
12 | 20 | # Measurement units |
13 | 21 | units.metres=Meters |
12 | 12 | menu.track.clearundo=Despejar la lista de deshacer |
13 | 13 | menu.track.markrectangle=Marcar puntos dentro de un rect\u00e1ngulo |
14 | 14 | 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 | |
19 | 16 | menu.range=Rango |
20 | 17 | menu.range.all=Seleccionar todo |
21 | 18 | menu.range.none=No seleccionar nada |
91 | 88 | function.deleterange=Eliminar rango |
92 | 89 | function.croptrack=Truncar track |
93 | 90 | function.interpolate=Interpolar puntos |
91 | function.deletebydate=Eliminar puntos de acuerdo a la fecha | |
94 | 92 | function.addtimeoffset=A\u00f1adir compensar tiempo |
95 | 93 | function.addaltitudeoffset=A\u00f1adir compensar altitud |
96 | 94 | function.convertnamestotimes=Convertir los nombres de los "waypoints" a tiempo |
104 | 102 | function.estimatetime=Estimar duraci\u00f3n |
105 | 103 | function.setmapbg=Configurar fondo de mapa |
106 | 104 | function.setpaths=Configurar rutas del programas |
105 | function.selectsegment=Seleccionar segmento actual | |
107 | 106 | function.splitsegments=Segmentar el track |
108 | 107 | function.sewsegments=Ensamblar los segmentos |
109 | 108 | function.getgpsies=Bajar ruta de Gpsies |
362 | 361 | dialog.gpsies.activity.skating=Patinaje |
363 | 362 | dialog.wikipedia.column.name=Nombre del art\u00edculo |
364 | 363 | dialog.wikipedia.column.distance=Distancia |
364 | dialog.wikipedia.nonefound=No se encontraron puntos | |
365 | 365 | dialog.correlate.notimestamps=No hay informaci\u00f3n de tiempo para los puntos, as\u00ed que no hay nada que correlacionar con las fotos. |
366 | 366 | dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar? |
367 | 367 | dialog.correlate.nouncorrelatedaudios=No hay audios no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar? |
396 | 396 | dialog.correlate.select.audioname=Nombre del audio |
397 | 397 | dialog.correlate.select.audiolater=Audio m\u00e1s adelante |
398 | 398 | 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 | |
404 | 406 | dialog.compress.closepoints.title=remover puntos cercanos |
405 | 407 | dialog.compress.closepoints.paramdesc=Factor de extensi\u00f3n |
406 | 408 | dialog.compress.wackypoints.title=Eliminar puntos an\u00f3malos |
498 | 500 | dialog.colourchooser.red=Rojo |
499 | 501 | dialog.colourchooser.green=Verde |
500 | 502 | dialog.colourchooser.blue=Azul |
503 | dialog.colourer.start=Color de inicio | |
504 | dialog.colourer.end=Color final | |
501 | 505 | dialog.setlanguage.firstintro=Puede usted seleccionar algunos de los lenguajes incluidos,<p>o puede en lugar de esto seleccionar un archivo de texto |
502 | 506 | dialog.setlanguage.secondintro=Usted necesita guardar sus preferencias y luego<p>reiniciar GpsPrune para cambiar el lenguaje |
503 | 507 | dialog.setlanguage.language=Lenguaje |
545 | 549 | dialog.weather.wind=Viento |
546 | 550 | dialog.weather.temp=Temp |
547 | 551 | dialog.weather.humidity=Humedad |
552 | dialog.deletebydate.nodate=Sin marcas de tiempo | |
553 | dialog.deletebydate.column.keep=Mantener | |
554 | dialog.deletebydate.column.delete=Eliminar | |
548 | 555 | |
549 | 556 | # 3d window |
550 | 557 | dialog.3d.title=GpsPrune vista 3-D |
692 | 699 | fieldname.altitude=Altitud |
693 | 700 | fieldname.timestamp=Informaci\u00f3n de tiempo |
694 | 701 | fieldname.time=Tiempo |
702 | fieldname.date=Data | |
695 | 703 | fieldname.waypointname=Nombre |
696 | 704 | fieldname.waypointtype=Tipo |
697 | 705 | fieldname.newsegment=Segmento |
826 | 834 | error.cache.notthere=No se encontr\u00f3 la carpeta del cache de recuadros |
827 | 835 | error.cache.empty=La carpeta del cache de recuadros esta vac\u00edo |
828 | 836 | error.cache.cannotdelete=No se pudieron borrar recuadros |
829 | error.interpolate.invalidparameter=El n\u00famero de puntos necesita ser entre 1 y 1000 | |
830 | 837 | error.tracksplit.nosplit=Imposible segmentar el track |
831 | 838 | error.downloadsrtm.nocache=Imposible guardar los archivos.\nPor favor, compruebe el cache. |
832 | 839 | error.sewsegments.nothingdone=Imposible ensamblar los segmentos.\nEl track tiene ahora %d segmentos. |
9 | 9 | menu.track.undo=\u0628\u0627\u0637\u0644 \u06a9\u0631\u062f\u0646 \u06a9\u0627\u0631 \u0642\u0628\u0644\u064a |
10 | 10 | 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 |
11 | 11 | 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 | |
16 | 16 | menu.range=\u0686\u064a\u0646\u0634 |
17 | 17 | menu.range.all=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u0645\u0647 \u0646\u0642\u0627\u0637 |
18 | 18 | menu.range.none=\u0627\u0646\u062a\u062e\u0627\u0628 \u0647\u064a\u0686 \u064a\u06a9 \u0627\u0632 \u0646\u0642\u0627\u0637 |
12 | 12 | menu.track.clearundo=Purger la liste d'annulation |
13 | 13 | menu.track.markrectangle=S\u00e9lectionner les points dans un rectangle |
14 | 14 | 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 | |
19 | 16 | menu.range=\u00c9tendue |
20 | 17 | menu.range.all=Tout s\u00e9lectionner |
21 | 18 | menu.range.none=Rien s\u00e9lectionner |
102 | 99 | function.distances=Distances |
103 | 100 | function.fullrangedetails=Montrer tous les d\u00e9tails |
104 | 101 | function.estimatetime=Temps estim\u00e9 |
102 | function.learnestimationparams=Apprendre les param\u00e8tres pour l'estimation | |
105 | 103 | function.setmapbg=D\u00e9finir le fond de carte |
106 | 104 | function.setpaths=D\u00e9finir les chemins des programmes |
107 | 105 | function.splitsegments=S\u00e9pare les segments |
359 | 357 | dialog.gpsies.activity.skating=Skating |
360 | 358 | dialog.wikipedia.column.name=Nom de l'article |
361 | 359 | dialog.wikipedia.column.distance=Distance |
360 | dialog.wikipedia.nonefound=Aucune points trouv\u00e9e | |
362 | 361 | dialog.correlate.notimestamps=Les points n'ont pas d'indication de temps, il n'est pas possible de les corr\u00e9ler. |
363 | 362 | dialog.correlate.nouncorrelatedphotos=Il n'y a pas de photos non-corr\u00e9l\u00e9es.\nVoulez-vous continuer ? |
364 | 363 | dialog.correlate.nouncorrelatedaudios=Il n'y a pas d'audios non-corr\u00e9l\u00e9s.\nVoulez-vous continuer ? |
393 | 392 | dialog.correlate.select.audioname=Nom du fichier audio |
394 | 393 | dialog.correlate.select.audiolater=Audio apr\u00e8s |
395 | 394 | 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 | |
401 | 402 | dialog.compress.closepoints.title=Suppression des points voisins |
402 | 403 | dialog.compress.closepoints.paramdesc=Taille du voisinage |
403 | 404 | dialog.compress.wackypoints.title=Suppression des points anormaux |
544 | 545 | dialog.weather.temp=Temp |
545 | 546 | dialog.weather.humidity=Humidit\u00e9 |
546 | 547 | 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 | |
547 | 550 | |
548 | 551 | # 3d window |
549 | 552 | dialog.3d.title=Vue 3D de GpsPrune |
830 | 833 | error.cache.notthere=Le dossier du cache n'a pas \u00e9t\u00e9 trouv\u00e9 |
831 | 834 | error.cache.empty=Le dossier du cache est vide |
832 | 835 | error.cache.cannotdelete=Effacement des dalles impossible |
833 | error.interpolate.invalidparameter=Le nombre de points doit \u00eatre compris entre 1 et 1000 | |
834 | 836 | error.tracksplit.nosplit=Impossible de s\u00e9parer les segments |
12 | 12 | menu.track.clearundo=Visszavon\u00e1si lista t\u00f6rl\u00e9se |
13 | 13 | menu.track.markrectangle=N\u00e9gyzeten bel\u00fcli pontok megjel\u00f6l\u00e9se |
14 | 14 | 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 | |
19 | 17 | menu.range=Tartom\u00e1ny |
20 | 18 | menu.range.all=Mindet kijel\u00f6l |
21 | 19 | menu.range.none=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se |
412 | 410 | dialog.correlate.select.audioname=Hang neve |
413 | 411 | dialog.correlate.select.audiolater=Hang k\u00e9s\u0151bb |
414 | 412 | 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 | |
420 | 418 | dialog.compress.closepoints.title=K\u00f6zeli pontok elt\u00e1vol\u00edt\u00e1sa |
421 | 419 | dialog.compress.closepoints.paramdesc=Hat\u00f3t\u00e1vols\u00e1g |
422 | 420 | dialog.compress.wackypoints.title=Kisz\u00e1m\u00edthatatlan pontok elt\u00e1vol\u00edt\u00e1sa |
847 | 845 | error.cache.notthere=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem tal\u00e1lhat\u00f3 |
848 | 846 | error.cache.empty=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra \u00fcres |
849 | 847 | 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 | |
851 | 848 | error.learnestimationparams.failed=Nem lehet param\u00e9tereket tanulni ebb\u0151l a nyomvonalb\u00f3l.\nT\u00f6lts be t\u00f6bb nyomvonalat. |
852 | 849 | error.tracksplit.nosplit=A nyomvonal nem elv\u00e1ghat\u00f3 |
853 | 850 | error.downloadsrtm.nocache=A f\u00e1jlokat nem siker\u00fclt meneni.\nK\u00e9rlek, ellen\u0151rizd a gyors\u00edt\u00f3t\u00e1rat. |
12 | 12 | menu.track.clearundo=Cancella lista annulla |
13 | 13 | menu.track.markrectangle=Segnare i punti nel rettangolo |
14 | 14 | 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 | |
19 | 16 | menu.range=Serie |
20 | 17 | menu.range.all=Seleziona tutto |
21 | 18 | menu.range.none=Deseleziona tutto |
91 | 88 | function.deleterange=Cancella la serie |
92 | 89 | function.croptrack=Cima la traccia |
93 | 90 | function.interpolate=Interpola i punti |
91 | function.deletebydate=Cancella punti secondo la data | |
94 | 92 | function.addtimeoffset=Aggiungi uno scarto temporale |
95 | 93 | function.addaltitudeoffset=Aggiungi uno scarto di altitudine |
96 | 94 | function.convertnamestotimes=Converti nomi dei waypoint in orari |
105 | 103 | function.learnestimationparams=Apprendi parametri di stima |
106 | 104 | function.setmapbg=Configura sfondo mappa |
107 | 105 | function.setpaths=Configura percorsi programmi |
106 | function.selectsegment=Seleziona segmento corrente | |
108 | 107 | function.splitsegments=Dividi traccia in segmenti |
109 | 108 | function.sewsegments=Riorganizza segmenti insieme |
110 | 109 | function.getgpsies=Ottieni tracce da Gpsies |
140 | 139 | function.diskcache=Salva mappe su disco |
141 | 140 | function.managetilecache=Gestione del cache di tasselli |
142 | 141 | function.getweatherforecast=Ottieni previsioni del tempo |
142 | function.setaltitudetolerance=Configura tolleranza di altitudini | |
143 | 143 | |
144 | 144 | # Dialogs |
145 | 145 | dialog.exit.confirm.title=Esci da GpsPrune |
378 | 378 | dialog.gpsies.activity.skating=Pattinaggio |
379 | 379 | dialog.wikipedia.column.name=Titolo articolo |
380 | 380 | dialog.wikipedia.column.distance=Distanza |
381 | dialog.wikipedia.nonefound=Nessuna punti trovata | |
381 | 382 | dialog.correlate.notimestamps=Non ci sono informazioni temporali tra i dati dei punti, non c'\u00e8 niente per collegarli con le foto. |
382 | 383 | dialog.correlate.nouncorrelatedphotos=Non ci sono foto non correlate.\nSei sicuro di voler continuare? |
383 | 384 | dialog.correlate.nouncorrelatedaudios=Non ci sono audio non correlati. \nSei sicuro di voler continuare? |
412 | 413 | dialog.correlate.select.audioname=Nome ripresa audio |
413 | 414 | dialog.correlate.select.audiolater=Ripresa audio successiva |
414 | 415 | 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 | |
420 | 424 | dialog.compress.closepoints.title=Cancella punti vicini |
421 | 425 | dialog.compress.closepoints.paramdesc=Fattore vicinanza |
422 | 426 | dialog.compress.wackypoints.title=Cancella punti strani |
474 | 478 | dialog.checkversion.releasedate2=. |
475 | 479 | dialog.checkversion.download=Per scaricare la nuova versione vai a http://gpsprune.activityworkshop.net/download.html. |
476 | 480 | 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> | |
478 | 482 | dialog.keys.normalmodifier=Ctrl |
479 | 483 | dialog.keys.macmodifier=Comando |
480 | 484 | dialog.saveconfig.desc=Le configurazioni seguenti possono essere salvati in un file di configurazione: |
514 | 518 | dialog.colourchooser.red=Rosso |
515 | 519 | dialog.colourchooser.green=Verde |
516 | 520 | 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 | |
517 | 531 | dialog.setlanguage.firstintro=Puoi selezionare una delle lingue incluse,<p>oppure selezionare un file di testo. |
518 | 532 | dialog.setlanguage.secondintro=Devi salvare le impostazioni e<p> riavviare GpsPrune per cambiare la lingua. |
519 | 533 | dialog.setlanguage.language=Lingua |
563 | 577 | dialog.weather.temp=Temp |
564 | 578 | dialog.weather.humidity=Umidit\u00e0 |
565 | 579 | 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 | |
566 | 583 | |
567 | 584 | # 3d window |
568 | 585 | dialog.3d.title=Visione GpsPrune in 3D |
717 | 734 | fieldname.altitude=Altitudine |
718 | 735 | fieldname.timestamp=Dati temporali |
719 | 736 | fieldname.time=Tempo |
737 | fieldname.date=Data | |
720 | 738 | fieldname.waypointname=Nome |
721 | 739 | fieldname.waypointtype=Tipo |
722 | 740 | fieldname.newsegment=Segmento |
851 | 869 | error.cache.notthere=Directory del cache di tasselli non trovato |
852 | 870 | error.cache.empty=Directory del cache di tasselli \u00e8 vuoto |
853 | 871 | error.cache.cannotdelete=Impossibile cancellare tasselli |
854 | error.interpolate.invalidparameter=Il numero di punti deve essere tra 1 e 1000 | |
855 | 872 | error.learnestimationparams.failed=Non \u00e8 possibile apprendere i parametri da questa traccia.\nProva a caricare pi\u00f9 tracce. |
856 | 873 | error.tracksplit.nosplit=La traccia non pu\u00f2 essere divisa |
857 | 874 | error.downloadsrtm.nocache=Non \u00e8 stato possibile salvare i file.\nControlla la cache del disco. |
11 | 11 | menu.track.clearundo=\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3059\u308b |
12 | 12 | menu.track.markrectangle=\u56db\u89d2\u306e\u4e2d\u306b\u5370\u3092\u3064\u3051\u308b |
13 | 13 | 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 | |
18 | 15 | menu.range=\u7bc4\u56f2(R) |
19 | 16 | menu.range.all=\u5168\u3066\u9078\u629e |
20 | 17 | menu.range.none=\u9078\u629e\u89e3\u9664 |
316 | 313 | dialog.correlate.timestamp.end=\u7d42\u70b9 |
317 | 314 | dialog.correlate.select.audioname=\u30aa\u30fc\u30c7\u30a3\u30aa\u540d |
318 | 315 | 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 | |
324 | 322 | dialog.compress.closepoints.title=\u8fd1\u508d\u70b9\u3092\u524a\u9664 |
325 | 323 | dialog.compress.closepoints.paramdesc=\u8fd1\u508d\u4fc2\u6570 |
326 | 324 | dialog.compress.wackypoints.title=\u304a\u304b\u3057\u306a\u70b9\u306e\u524a\u9664 |
9 | 9 | menu.track.undo=\uc2e4\ud589\ucde8\uc18c |
10 | 10 | menu.track.clearundo=\uc2e4\ud589\ucde8\uc18c \ubaa9\ub85d \uc0ad\uc81c |
11 | 11 | 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 | |
16 | 13 | menu.range=\uc5f0\uacb0\uc120 |
17 | 14 | menu.range.all=\ubaa8\ub450 \uc120\ud0dd |
18 | 15 | menu.range.none=\uc120\ud0dd\ud558\uc9c0 \uc54a\uae30 |
319 | 316 | dialog.correlate.select.audioname=\uc18c\ub9ac \uc774\ub984 |
320 | 317 | dialog.correlate.select.audiolater=\uc18c\ub9ac \ud6c4\uc5d0 |
321 | 318 | 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 | |
327 | 325 | dialog.deletemarked.nonefound=\uc9c0\uc810 \ub370\uc774\ud130\uac00 \uc81c\uac70\ub420 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. |
328 | 326 | dialog.compress.closepoints.title=\uc8fc\ubcc0 \ud3ec\uc778\ud2b8 \uc81c\uac70 |
329 | 327 | dialog.compress.closepoints.paramdesc=\ud655\uc7a5 \uacc4\uc218 |
12 | 12 | menu.track.clearundo=Ongedaan-maken lijst wissen |
13 | 13 | menu.track.markrectangle=Makeer alle punten in een vierkant |
14 | 14 | 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 | |
19 | 15 | menu.range=Reeks |
20 | 16 | menu.range.all=Selecteer alles |
21 | 17 | menu.range.none=Selecteer geen |
91 | 87 | function.deleterange=Verwijder reeks |
92 | 88 | function.croptrack=Route bijknippen |
93 | 89 | function.interpolate=Interpoleer punten |
90 | function.deletebydate=Verwijder punten op datum | |
94 | 91 | function.addtimeoffset=Tijdsverschil toevoegen |
95 | 92 | function.addaltitudeoffset=Hoogteverschil toevoegen |
93 | function.rearrangewaypoints=Rangschikken waypoints | |
96 | 94 | function.convertnamestotimes=Converteer waypointnamen naar tijden |
97 | 95 | function.deletefieldvalues=Verwijder veldwaarden |
98 | 96 | function.findwaypoint=Zoek waypoint |
105 | 103 | function.learnestimationparams=Parameters voor geschatte tijd |
106 | 104 | function.setmapbg=Instellen kaart achtergrond |
107 | 105 | function.setpaths=Instellen programmapaden |
106 | function.selectsegment=Selecteer huidige segment | |
108 | 107 | function.splitsegments=Splits route in segmenten |
109 | 108 | function.sewsegments=Voeg segmenten samen |
110 | 109 | function.getgpsies=Routes van Gpsies ophalen |
140 | 139 | function.diskcache=Kaart opslaan op schijf |
141 | 140 | function.managetilecache=Beheer tegelcache |
142 | 141 | function.getweatherforecast=Ophalen weersvoorspelling |
142 | function.setaltitudetolerance=Instellen hoogtetolerantie | |
143 | 143 | |
144 | 144 | # Dialogs |
145 | 145 | dialog.exit.confirm.title=GpsPrune afsluiten |
378 | 378 | dialog.gpsies.activity.skating=Skating |
379 | 379 | dialog.wikipedia.column.name=Artikelnaam |
380 | 380 | dialog.wikipedia.column.distance=Afstand |
381 | dialog.wikipedia.nonefound=Geen punten gevonden | |
381 | 382 | dialog.correlate.notimestamps=Er zit geen tijdinformatie in de punten, dus kunnen ze niet aan foto's gekoppeld worden. |
382 | 383 | dialog.correlate.nouncorrelatedphotos=Er zijn geen ongekoppelde foto's.\nWeet u zeker dat u wilt doorgaan? |
383 | 384 | dialog.correlate.nouncorrelatedaudios=Er zijn geen ongekoppelde geluidsbestanden.\nWeet u zeker dat u wilt doorgaan? |
409 | 410 | dialog.correlate.timestamp.middle=Midden |
410 | 411 | dialog.correlate.timestamp.end=Einde |
411 | 412 | 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 | |
413 | 414 | dialog.correlate.select.audiolater=Audio later |
415 | dialog.rearrangewaypoints.desc=Selecteer doel en sorteervolgorde van de waypoints | |
414 | 416 | 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 | |
420 | 424 | dialog.compress.closepoints.title=Verwijder nabijliggende punten |
421 | 425 | dialog.compress.closepoints.paramdesc=Bereik |
422 | 426 | dialog.compress.wackypoints.title=Vreemde punten verwijderen |
514 | 518 | dialog.colourchooser.red=Rood |
515 | 519 | dialog.colourchooser.green=Groen |
516 | 520 | 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 | |
517 | 534 | dialog.setlanguage.firstintro=U kunt kiezen uit \u00e9\u00e9n van de meegeleverde talen,<p>of selecteer een tekstbestand. |
518 | 535 | dialog.setlanguage.secondintro=U dient uw instellingen op te slaan en<p>GpsPrune te herstarten om de taal te kunnen wijzigen |
519 | 536 | dialog.setlanguage.language=Taal |
559 | 576 | dialog.weather.day.friday=Vrijdag |
560 | 577 | dialog.weather.day.saturday=Zaterdag |
561 | 578 | dialog.weather.day.sunday=Zondag |
579 | dialog.weather.wind=Wind | |
580 | dialog.weather.temp=Temp | |
562 | 581 | dialog.weather.humidity=Luchtvocht. |
563 | 582 | 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 | |
564 | 590 | |
565 | 591 | # 3d window |
566 | 592 | dialog.3d.title=GpsPrune in 3D |
715 | 741 | fieldname.altitude=Hoogte |
716 | 742 | fieldname.timestamp=Tijd |
717 | 743 | fieldname.time=Tijd |
744 | fieldname.date=Datum | |
718 | 745 | fieldname.waypointname=Naam |
719 | 746 | fieldname.waypointtype=Type |
720 | 747 | fieldname.newsegment=Segment |
756 | 783 | units.deg=Graden |
757 | 784 | units.iso8601=ISO 8601 |
758 | 785 | units.degreescelsius=Celsius |
786 | units.degreescelsius.short=\u00baC | |
759 | 787 | units.degreesfahrenheit=Fahrenheit |
788 | units.degreesfahrenheit.short=\u00baF | |
760 | 789 | |
761 | 790 | # How to combine conditions, such as filters |
762 | 791 | logic.and=en |
847 | 876 | error.cache.notthere=De tegelcache map niet gevonden |
848 | 877 | error.cache.empty=De tegelcache map is leeg |
849 | 878 | error.cache.cannotdelete=Er konden geen tegels verwijderd worden |
850 | error.interpolate.invalidparameter=Aantal punten moet tussen 1 en 1000 liggen | |
851 | 879 | error.learnestimationparams.failed=Kan geen parameters bepalen van deze route.\nProbeer meer routes te laden. |
852 | 880 | error.tracksplit.nosplit=Deze route kon niet opgedeeld worden |
853 | 881 | error.downloadsrtm.nocache=De bestanden konden niet worden opgeslagen.\nControleer de schijfcache. |
6 | 6 | menu.file.recentfiles=Ostatnio u\u017cywane |
7 | 7 | menu.file.save=Zapisz |
8 | 8 | menu.file.exit=Zako\u0144cz |
9 | menu.online=Online | |
9 | 10 | menu.track=\u015acie\u017cka |
10 | 11 | menu.track.undo=Cofnij |
11 | 12 | menu.track.clearundo=Wyczy\u015b\u0107 list\u0119 zmian |
12 | 13 | menu.track.markrectangle=Zaznaczenie prostok\u0105tne |
13 | 14 | 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 | |
18 | 15 | menu.range=Zakres |
19 | 16 | menu.range.all=Zaznacz wszystko |
20 | 17 | menu.range.none=Usu\u0144 zaznaczenie |
22 | 19 | menu.range.end=Zaznacz koniec zakresu |
23 | 20 | menu.range.average=U\u015brednij zaznaczenie |
24 | 21 | 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 | |
26 | 23 | menu.range.cutandmove=Wytnij i przesu\u0144 zaznaczenie |
27 | 24 | menu.point=Punkt |
28 | 25 | menu.point.editpoint=Edytuj punkt |
56 | 53 | |
57 | 54 | # Alt keys for menus |
58 | 55 | altkey.menu.file=P |
56 | altkey.menu.online=O | |
59 | 57 | altkey.menu.track=C |
60 | 58 | altkey.menu.range=Z |
61 | 59 | altkey.menu.point=U |
89 | 87 | function.deleterange=Usu\u0144 zakres |
90 | 88 | function.croptrack=Przytnij \u015bcie\u017ck\u0119 |
91 | 89 | function.interpolate=Wstaw pomi\u0119dzy punkty |
90 | function.deletebydate=Usu\u0144 punkty wed\u0142ug daty | |
92 | 91 | function.addtimeoffset=Dodaj przesuni\u0119cie czasu |
93 | 92 | function.addaltitudeoffset=Dodaj przesuni\u0119cie wysoko\u015bci |
93 | function.rearrangewaypoints=Zmie\u0144 kolejno\u015b\u0107 punkt\u00f3w po\u015brednich | |
94 | 94 | function.convertnamestotimes=Zamie\u0144 nazwy punkt\u00f3w na czas |
95 | 95 | function.deletefieldvalues=Usu\u0144 warto\u015bci |
96 | 96 | function.findwaypoint=Znajd\u017a punkt po\u015bredni |
103 | 103 | function.learnestimationparams=Skoryguj wsp\u00f3\u0142czynniki szacowania czasu |
104 | 104 | function.setmapbg=Wybierz map\u0119 t\u0142a |
105 | 105 | 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 | |
106 | 109 | function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies |
107 | 110 | function.uploadgpsies=Wy\u015blij \u015bcie\u017cki do Gpsies |
108 | 111 | function.lookupsrtm=Pobierz wysoko\u015bci z SRTM |
135 | 138 | function.saveconfig=Zapisz ustawienia |
136 | 139 | function.diskcache=Zapisz mapy na dysk |
137 | 140 | 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 | |
139 | 143 | |
140 | 144 | # Dialogs |
141 | 145 | dialog.exit.confirm.title=Zako\u0144cz GpsPrune |
243 | 247 | dialog.exportpov.ballsandsticks=Kule i pa\u0142ki |
244 | 248 | dialog.exportpov.tubesandwalls=Rurki i \u015bciany |
245 | 249 | 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 | |
246 | 252 | dialog.exportpov.baseimage=Obraz podk\u0142adu |
247 | 253 | dialog.exportpov.cannotmakebaseimage=Nie mo\u017cna zapisa\u0107 obrazu podk\u0142adu |
248 | 254 | dialog.baseimage.title=Obrazu podk\u0142adu |
372 | 378 | dialog.gpsies.activity.skating=Wrotki/rolki |
373 | 379 | dialog.wikipedia.column.name=Tytu\u0142 artyku\u0142u |
374 | 380 | dialog.wikipedia.column.distance=Odleg\u0142o\u015b\u0107 |
381 | dialog.wikipedia.nonefound=Brak wpis\u00f3w w wikipedii | |
375 | 382 | dialog.correlate.notimestamps=Punkty nie maj\u0105 znacznik\u00f3w czasu, nie mo\u017cna ich powi\u0105za\u0107 ze zdj\u0119ciami. |
376 | 383 | dialog.correlate.nouncorrelatedphotos=Nie ma nie powi\u0105zanych zdj\u0119\u0107.\nCzy na pewno chcesz kontynuowa\u0107? |
377 | 384 | dialog.correlate.nouncorrelatedaudios=Nie ma nie powi\u0105zanych plik\u00f3w audio.\nCzy na pewno chcesz kontynuowa\u0107? |
405 | 412 | dialog.correlate.audioselect.intro=Wybierz jeden z powi\u0105zanych plik\u00f3w audio i u\u017cyj go jako wzorca do przesuni\u0119cia czasu |
406 | 413 | dialog.correlate.select.audioname=nazwa pliku audio |
407 | 414 | dialog.correlate.select.audiolater=p\u00f3\u017aniejszy plik audio |
415 | dialog.rearrangewaypoints.desc=Wybierz przeznaczenie i porz\u0105dek sortowania punkt\u00f3w po\u015brednich | |
408 | 416 | 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 | |
414 | 424 | dialog.compress.closepoints.title=Usuwanie bliskich sobie punkt\u00f3w |
415 | 425 | dialog.compress.closepoints.paramdesc=Wsp\u00f3\u0142czynnik rozpi\u0119to\u015bci |
416 | 426 | dialog.compress.wackypoints.title=Usuwanie dziwacznych punkt\u00f3w |
508 | 518 | dialog.colourchooser.red=Czerwony |
509 | 519 | dialog.colourchooser.green=Zielony |
510 | 520 | 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 | |
511 | 534 | dialog.setlanguage.firstintro=Mo\u017cesz wybra\u0107 jeden z do\u0142\u0105czonych j\u0119zyk\u00f3w<p>Albo wybra\u0107 wybrany przez siebie plik tekstowy. |
512 | 535 | dialog.setlanguage.secondintro=B\u0119dziesz musia\u0142 zapisa\u0107 ustawienia<p>i zrestartowa\u0107 GpsPrune by zmieni\u0107 j\u0119zyk. |
513 | 536 | dialog.setlanguage.language=J\u0119zyk |
535 | 558 | dialog.setlinewidth.text=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek |
536 | 559 | dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM: |
537 | 560 | 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 | |
538 | 569 | dialog.weather.day.now=Aktualny |
539 | 570 | dialog.weather.day.today=Dzisiaj |
540 | 571 | dialog.weather.day.tomorrow=Jutro |
541 | 572 | dialog.weather.day.monday=Poniedzia\u0142ek |
542 | 573 | dialog.weather.day.tuesday=Wtorek |
543 | dialog.weather.day.wednesday=\u015Aroda | |
574 | dialog.weather.day.wednesday=\u015aroda | |
544 | 575 | dialog.weather.day.thursday=Czwartek |
545 | 576 | dialog.weather.day.friday=Pi\u0105tek |
546 | 577 | dialog.weather.day.saturday=Sobota |
547 | 578 | 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 | |
548 | 590 | |
549 | 591 | # 3d window |
550 | 592 | dialog.3d.title=GpsPrune widok tr\u00f3jwymiarowy |
563 | 605 | confirm.addaltitudeoffset=Dodano przesuni\u0119cie wysoko\u015bci |
564 | 606 | confirm.rearrangewaypoints=Przestawiono punkty po\u015brednie |
565 | 607 | confirm.rearrangephotos=Zmieniono kolejno\u015b\u0107 zdj\u0119\u0107 |
608 | confirm.splitsegments= | |
609 | confirm.sewsegments=Po\u0142\u0105czono %d fragmenty/\u00f3w | |
566 | 610 | confirm.cutandmove=Przesuni\u0119to zaznaczenie |
567 | 611 | confirm.interpolate=Dodano punkty |
568 | 612 | confirm.convertnamestotimes=Zmieniono nazwy punkt\u00f3w po\u015brednich |
581 | 625 | confirm.rotatephoto=obr\u00f3cono zdj\u0119cie |
582 | 626 | confirm.running=Przetwarzam dane ... |
583 | 627 | 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 | |
584 | 631 | confirm.deletefieldvalues=Warto\u015bci p\u00f3l usuni\u0119to |
585 | 632 | confirm.audioload=dodano pliki audio |
586 | 633 | confirm.correlateaudios.single=audio zosta\u0142o po\u0142\u0105czone |
587 | 634 | confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone |
588 | 635 | |
589 | # Tips | |
636 | # Tips, shown just once when appropriate | |
590 | 637 | 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. | |
591 | 642 | tip.manuallycorrelateone=Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie. |
592 | 643 | |
593 | 644 | # Buttons |
690 | 741 | fieldname.altitude=Wysoko\u015b\u0107 |
691 | 742 | fieldname.timestamp=Czas |
692 | 743 | fieldname.time=Czas |
744 | fieldname.date=Data | |
693 | 745 | fieldname.waypointname=Nazwa |
694 | 746 | fieldname.waypointtype=Typ |
695 | 747 | fieldname.newsegment=Odcinek |
763 | 815 | undo.deletemarked=usu\u0144 punkty |
764 | 816 | undo.insert=wstaw punkty |
765 | 817 | 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 | |
767 | 821 | undo.addtimeoffset=dodaj przesuni\u0119cie czasowe |
768 | 822 | undo.addaltitudeoffset=dodaj przeuni\u0119cie wysoko\u015bci |
769 | 823 | undo.rearrangewaypoints=przestaw punkty po\u015brednie |
822 | 876 | error.cache.notthere=Nie znaleziono katalogu kesza |
823 | 877 | error.cache.empty=Katalog kesza jest pusty |
824 | 878 | 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 | |
826 | 879 | 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. |
12 | 12 | menu.track.clearundo=Limpar lista de desfazer |
13 | 13 | menu.track.markrectangle=Marcar pontos no ret\u00e2ngulo |
14 | 14 | 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 | |
19 | 16 | menu.range=Intervalo |
20 | 17 | menu.range.all=Selecionar tudo |
21 | 18 | menu.range.none=Desmarcar todas as sele\u00e7\u00f5es |
91 | 88 | function.deleterange=Remover intervalo |
92 | 89 | function.croptrack=Cortar rota |
93 | 90 | function.interpolate=Interpolar pontos |
91 | function.deletebydate=Remover pontos de acordo com a data | |
94 | 92 | function.addtimeoffset=Adicionar diferen\u00e7a de tempo |
95 | 93 | function.addaltitudeoffset=Adicionar diferen\u00e7a de altitude |
96 | 94 | function.convertnamestotimes=Converter nomes dos pontos para tempos |
105 | 103 | function.learnestimationparams=Aprender os par\u00e2metros para estimativa de tempo |
106 | 104 | function.setmapbg=Definir como fundo do mapa |
107 | 105 | function.setpaths=Definir caminhos do programa |
106 | function.selectsegment=Selecionar segmento atual | |
108 | 107 | function.splitsegments=Dividir rota em segmentos |
109 | 108 | function.sewsegments=Reunir segmentos em rota |
110 | 109 | function.getgpsies=Obter rotas Gpsies |
412 | 411 | dialog.correlate.select.audioname=Nome do \u00e1udio |
413 | 412 | dialog.correlate.select.audiolater=\u00c1udio posterior |
414 | 413 | 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 | |
420 | 421 | dialog.compress.closepoints.title=Remo\u00e7\u00e3o de ponto pr\u00f3ximo |
421 | 422 | dialog.compress.closepoints.paramdesc=Fator de deslocamento |
422 | 423 | dialog.compress.wackypoints.title=Remo\u00e7\u00e3o de ponto exc\u00eantrica |
851 | 852 | error.cache.notthere=A paste de cache de fundos n\u00e3o foi encontrada |
852 | 853 | error.cache.empty=A pasta de cache de fundos est\u00e1 vazia |
853 | 854 | error.cache.cannotdelete=Nenhum fundo pode ser removido |
854 | error.interpolate.invalidparameter=O n\u00famero de pontos deve estar entre 1 e 1000 | |
855 | 855 | error.learnestimationparams.failed=N\u00e3o foi poss\u00edvel aprender par\u00e2metros desta rota.\nTente baixar mais rotas. |
856 | 856 | error.tracksplit.nosplit=A rota n\u00e3o pode ser dividida. |
857 | 857 | error.downloadsrtm.nocache=Os arquivos n\u00e3o puderam ser salvos.\nPor favor, verifique a cache do disco. |
5 | 5 | menu.file.addphotos=Adaug\u0103 foto |
6 | 6 | menu.file.recentfiles=Fi\u015fiere recente |
7 | 7 | menu.file.save=Salvare |
8 | menu.file.exit=Iesire | |
8 | menu.file.exit=Ie\u015fire | |
9 | menu.online=Internet | |
9 | 10 | menu.track=Traseu |
10 | 11 | menu.track.undo=Anulare |
11 | 12 | menu.track.clearundo=\u015etergere lista de anulari |
12 | 13 | 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 | |
17 | 14 | menu.range=Interval |
18 | 15 | menu.range.all=Selectare toate |
19 | 16 | menu.range.none=Nu selecta niciun punct |
20 | 17 | menu.range.start=Seteaza inceputul selectiei |
21 | 18 | 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 | |
24 | 21 | menu.range.mergetracksegments=Unire segmente traseu |
25 | menu.range.cutandmove=Taiere si mutare selectie | |
22 | menu.range.cutandmove=Taiere si mutare selec\u0163ie | |
26 | 23 | menu.point=Punct |
27 | 24 | menu.point.editpoint=Editare punct |
28 | 25 | menu.point.deletepoint=\u015etergere punct |
37 | 34 | menu.view.browser.yahoo=Harti Yahoo |
38 | 35 | menu.view.browser.bing=Harti Bing |
39 | 36 | menu.settings=Set\u0103ri |
37 | menu.settings.onlinemode=\u00cencarc\u0103 h\u0103r\u021bi | |
40 | 38 | menu.help=Ajutor |
41 | 39 | # Popup menu for map |
42 | 40 | menu.map.zoomin=Apropie |
52 | 50 | |
53 | 51 | # Alt keys for menus |
54 | 52 | altkey.menu.file=F |
53 | altkey.menu.online=N | |
55 | 54 | altkey.menu.track=T |
56 | 55 | altkey.menu.range=I |
57 | 56 | altkey.menu.point=P |
86 | 85 | function.interpolate=Interpolare |
87 | 86 | function.addtimeoffset=Adaug\u0103 decalaj timp |
88 | 87 | function.addaltitudeoffset=Adaug\u0103 decalaj altitudine |
88 | function.rearrangewaypoints=Rearanjare waypoint | |
89 | 89 | function.findwaypoint=Gasire waypoint |
90 | 90 | function.charts=Grafice |
91 | 91 | function.show3d=Vizualizare arborescenta |
92 | 92 | function.distances=Distan\u0163e |
93 | 93 | 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 | |
96 | 100 | function.setcolours=Selectare culorile |
97 | 101 | function.setlanguage=Selectare limba |
98 | 102 | function.connecttopoint=Conecteaza la punct |
99 | 103 | function.disconnectfrompoint=Deconecteaza de la punct |
100 | 104 | function.removephoto=Elimina foto |
101 | 105 | 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 | |
102 | 113 | function.help=Ajutor |
103 | 114 | function.showkeys=Arat\u0103 tastele scurt\u0103turi |
104 | 115 | function.about=Despre GpsPrune |
105 | 116 | function.checkversion=Verific\u0103 pentru o versiune noua |
106 | 117 | function.saveconfig=Salvare set\u0103ri |
118 | function.diskcache=Salvare harti | |
107 | 119 | function.getweatherforecast=Prognoz\u0103 meteo |
108 | 120 | |
109 | 121 | # Dialogs |
113 | 125 | dialog.openappend.text=Adauga la datele deja incarcate? |
114 | 126 | dialog.deletepoint.title=\u015eterge Punct |
115 | 127 | dialog.deletepoint.deletephoto=\u015eterg fotografiile atasate acestui punct? |
116 | dialog.deletephoto.title=\u015eterge Foto | |
128 | dialog.deletephoto.title=\u015eterge foto | |
117 | 129 | dialog.deletephoto.deletepoint=\u015eterg punct atasat acestei fotografii? |
130 | dialog.deleteaudio.deletepoint=\u015eterg punct atasat acestei audio? | |
118 | 131 | dialog.openoptions.title=Optiuni deschidere |
119 | 132 | dialog.load.table.field=Cimp |
120 | 133 | dialog.load.table.datatype=Tip data |
128 | 141 | dialog.openoptions.deliminfo.records=inregistrari, cu |
129 | 142 | dialog.openoptions.deliminfo.fields=cimpuri |
130 | 143 | 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 | |
131 | 147 | dialog.selecttracks.noname=F\u0103r\u0103 nume |
132 | 148 | dialog.jpegload.subdirectories=Include subdirectori |
133 | 149 | dialog.jpegload.loadjpegswithoutcoords=Include fotografii fara coordonate |
134 | 150 | dialog.jpegload.loadjpegsoutsidearea=Include fotografii din afara zonei curente |
135 | dialog.jpegload.progress.title=Incarcare fotografii | |
151 | dialog.jpegload.progress.title=\u00cenc\u0103rcare fotografii | |
136 | 152 | dialog.jpegload.progress=Va rog sa asteptati, caut fotografiile |
137 | 153 | dialog.gpsload.nogpsbabel=Nu gasesc programul gpsbabel. Continui ? |
138 | 154 | dialog.gpsload.device=Nume dispozitiv |
139 | 155 | 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 | |
141 | 161 | dialog.gpssend.trackname=Nume traseu |
142 | 162 | 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 | |
144 | 166 | 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 > | |
145 | 171 | 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 > | |
146 | 178 | dialog.saveoptions.title=Salvare fi\u015fier |
147 | 179 | dialog.save.table.field=Cimp |
180 | dialog.save.table.hasdata=Date | |
148 | 181 | 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 | |
149 | 185 | dialog.save.overwrite.title=Fi\u015fierul exist\u0103 |
150 | 186 | dialog.save.overwrite.text=Fi\u015fierul exist\u0103. \u00cel suprascriu? |
151 | 187 | dialog.exportkml.text=Titlu |
188 | dialog.exportkml.imagesize=Dimensiune imaginii | |
152 | 189 | dialog.exportkml.trackcolour=Culoarea liniei |
153 | 190 | dialog.exportgpx.name=Nume |
154 | 191 | dialog.exportgpx.desc=Descriere |
192 | dialog.exportgpx.encoding=Codare | |
155 | 193 | dialog.exportgpx.encoding.system=Sistem |
156 | 194 | dialog.exportgpx.encoding.utf8=UTF-8 |
157 | 195 | dialog.exportpov.font=Fontul |
163 | 201 | dialog.3d.terraingridsize=Dimensiune a grilei |
164 | 202 | dialog.exportpov.baseimage=Imagine cartografice |
165 | 203 | dialog.baseimage.title=Imagine cartografice |
204 | dialog.baseimage.zoom=Nivel de zoom | |
205 | dialog.baseimage.incomplete=Imagine incomplet\u0103 | |
166 | 206 | 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 | |
169 | 214 | dialog.undo.title=Anulare |
215 | dialog.pointedit.title=Editare punct | |
170 | 216 | dialog.pointedit.intro=V\u0103 rog selecta\u0163i r\u00e2ndul care va fi editat |
171 | 217 | dialog.pointedit.table.field=Cimp |
172 | 218 | dialog.pointedit.table.value=Valoare |
219 | dialog.pointnameedit.name=Nume de waypoint | |
173 | 220 | dialog.pointnameedit.uppercase=Litere MARI |
174 | 221 | dialog.pointnameedit.lowercase=Litere mici |
175 | 222 | dialog.addtimeoffset.days=Zile |
176 | 223 | dialog.addtimeoffset.hours=Ore |
177 | 224 | dialog.addtimeoffset.minutes=Minute |
178 | 225 | dialog.findwaypoint.search=C\u0103utare |
226 | dialog.saveexif.table.photoname=Nume | |
227 | dialog.saveexif.title=Salvare Exif | |
179 | 228 | dialog.saveexif.table.status=Stare |
180 | 229 | dialog.saveexif.table.save=Salveaza |
181 | 230 | dialog.saveexif.photostatus.connected=Conectat |
184 | 233 | dialog.saveexif.overwrite=Suprascrie fi\u015fiere |
185 | 234 | dialog.charts.xaxis=Axa X |
186 | 235 | 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 | |
187 | 241 | 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 | |
188 | 253 | dialog.addmapsource.noname=F\u0103r\u0103 nume |
189 | 254 | dialog.gpsies.column.name=Nume |
190 | 255 | dialog.gpsies.column.length=Lungime |
191 | 256 | dialog.gpsies.description=Descriere |
192 | 257 | 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 | |
193 | 263 | dialog.wikipedia.column.name=Nume |
194 | 264 | 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 | |
195 | 268 | dialog.correlate.options.offset.hours=ore, |
196 | 269 | dialog.correlate.options.offset.minutes=minute, |
197 | 270 | 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 | |
198 | 283 | dialog.pastecoordinates.coords=Coordonate |
199 | 284 | dialog.about.version=Versiunea |
285 | dialog.about.build=Construi | |
286 | dialog.about.languages=Limbi | |
200 | 287 | dialog.about.systeminfo=Informa\u0163ii a sistemului |
201 | 288 | 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 | |
202 | 294 | dialog.about.systeminfo.exiflib=Bibliotec\u0103 Exif |
203 | 295 | dialog.about.systeminfo.exiflib.internal=Intern |
204 | 296 | dialog.about.systeminfo.exiflib.internal.failed=Intern (absent) |
209 | 301 | dialog.about.readme=Cite\u015fte-m\u0103 |
210 | 302 | dialog.checkversion.releasedate1=Aceasta versiune noua a fost lansapa pe |
211 | 303 | 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 | |
212 | 308 | dialog.setcolours.background=Fund |
213 | 309 | dialog.setcolours.lines=Linii |
214 | 310 | dialog.setcolours.primary=Primar |
215 | 311 | dialog.setcolours.secondary=Secundar |
216 | 312 | dialog.setcolours.point=Puncte |
313 | dialog.setcolours.selection=Selec\u0163ie | |
217 | 314 | dialog.setcolours.text=Text |
315 | dialog.colourchooser.title=Selectare culoare | |
218 | 316 | dialog.colourchooser.red=Ro\u0219u |
219 | 317 | dialog.colourchooser.green=Verde |
220 | 318 | 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 | |
221 | 329 | dialog.weather.day.today=Ast\u0103zi |
222 | 330 | dialog.weather.day.tomorrow=M\u00e2ine |
223 | 331 | dialog.weather.day.monday=Luni |
232 | 340 | confirm.loadfile=Date incarcate din fi\u015fier |
233 | 341 | confirm.save.ok1=Salvat cu succes |
234 | 342 | 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 | |
235 | 350 | |
236 | 351 | # Tips |
237 | 352 | tip.title=Indiciu |
257 | 372 | button.selectall=Selecteaza tot |
258 | 373 | button.selectnone=Deselecteaza tot |
259 | 374 | button.load=Descarca |
260 | button.upload=Inc\u0103rca | |
375 | button.upload=Trimite | |
261 | 376 | button.guessfields=Ghici cimpuri |
262 | 377 | button.check=Verifica |
263 | 378 | button.delete=\u015etergere |
280 | 395 | details.trackdetails=Detalii traseul |
281 | 396 | details.track.points=Puncte |
282 | 397 | details.pointdetails=Detalii punctul |
398 | details.index.selected=Punct | |
399 | details.index.of=de | |
400 | details.photofile=Fi\u015fier | |
283 | 401 | details.rangedetails=Detalii intervalul |
284 | 402 | details.range.selected=Selectat |
285 | 403 | details.range.to=la |
286 | 404 | details.altitude.to=la |
405 | details.range.climb=Urcare | |
406 | details.range.descent=Cobor\u00e2re | |
287 | 407 | details.coordformat=Format coordonate |
288 | details.distanceunits=Unitati de distan\u0163e | |
408 | details.distanceunits=Unit\u0103\u0163i de distan\u0163e | |
289 | 409 | display.range.time.secs=s |
290 | 410 | display.range.time.mins=m |
291 | 411 | display.range.time.hours=o |
292 | 412 | display.range.time.days=z |
293 | 413 | details.range.avespeed=Viteza medie |
294 | 414 | 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 | |
295 | 419 | details.lists.photos=Foto-uri |
296 | 420 | 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 | |
297 | 425 | details.audiodetails=Detalii audio |
426 | details.audio.file=Fi\u015fier | |
298 | 427 | |
299 | 428 | # Field names |
300 | 429 | fieldname.latitude=Latitudine |
301 | 430 | fieldname.longitude=Longitudine |
302 | 431 | fieldname.altitude=Altitudine |
432 | fieldname.timestamp=Timp | |
303 | 433 | fieldname.time=Timp |
434 | fieldname.date=Data | |
304 | 435 | fieldname.waypointname=Nume |
305 | 436 | fieldname.waypointtype=Tip |
306 | 437 | fieldname.newsegment=Segment |
321 | 452 | units.kilometresperhour.short=km/o |
322 | 453 | units.miles=Mil\u0103 |
323 | 454 | units.miles.short=mi |
455 | units.milesperhour=mil\u0103 pe or\u0103 | |
456 | units.milesperhour.short=mpo | |
324 | 457 | units.nauticalmiles=Mil\u0103 marin\u0103 |
325 | 458 | units.nauticalmiles.short=mm |
326 | 459 | units.nauticalmilesperhour.short=kn |
460 | units.metrespersec=metri pe secund | |
461 | units.metrespersec.short=m/s | |
327 | 462 | units.hours=ore |
328 | 463 | units.minutes=minute |
329 | 464 | 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 | |
330 | 473 | |
331 | 474 | # How to combine conditions, such as filters |
332 | logic.and=\ufeff\u0219i | |
475 | logic.and=\u0219i | |
333 | 476 | logic.or=sau |
477 | ||
478 | # External urls | |
479 | wikipedia.lang=ro | |
480 | openweathermap.lang=ro | |
334 | 481 | |
335 | 482 | # Cardinals for 3d plots |
336 | 483 | cardinal.n=N |
338 | 485 | cardinal.e=E |
339 | 486 | cardinal.w=V |
340 | 487 | |
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⏎ |
12 | 12 | 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 |
13 | 13 | 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 |
14 | 14 | 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 | |
19 | 16 | menu.range=\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b |
20 | 17 | menu.range.all=\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435 |
21 | 18 | menu.range.none=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u044b\u0431\u043e\u0440\u043a\u0443 |
384 | 381 | dialog.correlate.select.audioname=\u0418\u043c\u044f \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 |
385 | 382 | dialog.correlate.select.audiolater=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u0437\u0434\u043d\u0435\u0435 |
386 | 383 | 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 | |
392 | 390 | 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 |
393 | 391 | dialog.compress.closepoints.paramdesc=\u0420\u0430\u0437\u043c\u0430\u0445 |
394 | 392 | 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 |
793 | 791 | error.load.othererror=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438 \u0444\u0430\u0439\u043b\u0430: |
794 | 792 | error.jpegload.dialogtitle=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0444\u043e\u0442\u043e |
795 | 793 | 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 | |
797 | 795 | error.jpegload.nogpsfound=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 GPS-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f |
798 | 796 | 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. |
799 | 797 | error.audioload.nofilesfound=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b |
819 | 817 | 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 |
820 | 818 | 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 |
821 | 819 | 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 | |
823 | 820 | 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 |
11 | 11 | menu.track.clearundo=Rensa \u00e5ngra-historik |
12 | 12 | menu.track.markrectangle=Markera punkter i rektangel |
13 | 13 | 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 | |
18 | 18 | menu.range=Intervall |
19 | 19 | menu.range.all=V\u00e4lj alla |
20 | 20 | menu.range.none=V\u00e4lj ingen |
16 | 16 | menu.range.average=Se\u00e7me ortala |
17 | 17 | menu.range.reverse=S\u0131ra tersine \u00e7evir |
18 | 18 | 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 | |
23 | 23 | menu.range.cutandmove=Se\u00e7me kes ve ta\u015f\u0131 |
24 | 24 | menu.range=S\u0131ra |
25 | 25 | menu.point=Nokta |
11 | 11 | menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043c\u0456\u043d |
12 | 12 | 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 |
13 | 13 | 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 | |
18 | 18 | menu.range=\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b |
19 | 19 | menu.range.all=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u0443\u0441\u0456 |
20 | 20 | menu.range.none=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438 \u0432\u0438\u0431\u0456\u0440\u043a\u0443 |
12 | 12 | menu.track.clearundo=\u6e05\u9664\u64a4\u9500\u6e05\u5355 |
13 | 13 | menu.track.markrectangle=\u6807\u8bb0\u9009\u53d6\u533a\u57df\u5185\u7684\u70b9 |
14 | 14 | 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 | |
19 | 15 | menu.range=\u822a\u6bb5 |
20 | 16 | menu.range.all=\u5168\u9009 |
21 | 17 | menu.range.none=\u64a4\u9500\u9009\u62e9 |
91 | 87 | function.deleterange=\u5220\u9664\u8f68\u8ff9\u70b9\u6bb5 |
92 | 88 | function.croptrack=\u4fee\u526a\u8f68\u8ff9 |
93 | 89 | function.interpolate=\u91cd\u53e0\u8f68\u8ff9\u70b9 |
90 | function.deletebydate=\u6839\u636e\u65e5\u671f\u5220\u9664\u8f68\u8ff9\u70b9 | |
94 | 91 | function.addtimeoffset=\u52a0\u5165\u65f6\u95f4\u5dee |
95 | 92 | function.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb |
93 | function.rearrangewaypoints=\u91cd\u65b0\u6392\u5217\u822a\u70b9 | |
96 | 94 | function.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4 |
97 | 95 | function.deletefieldvalues=\u5220\u9664\u5b57\u6bb5\u503c |
98 | 96 | function.findwaypoint=\u67e5\u627e\u822a\u70b9 |
105 | 103 | function.learnestimationparams=\u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4 |
106 | 104 | function.setmapbg=\u80cc\u666f\u5730\u56fe |
107 | 105 | function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84 |
106 | function.selectsegment=\u9009\u4e2d\u5f53\u524d\u8f68\u8ff9\u6bb5 | |
108 | 107 | function.splitsegments=\u5206\u5272\u8f68\u8ff9 |
109 | 108 | function.sewsegments=\u63a5\u5408\u8f68\u8ff9\u7247\u6bb5 |
110 | 109 | function.getgpsies=\u83b7\u53d6Gpsies\u8f68\u8ff9 |
140 | 139 | function.diskcache=\u4fdd\u5b58\u5730\u56fe |
141 | 140 | function.managetilecache=\u7ba1\u7406\u5730\u56fe\u533a\u57df\u6570\u636e\u7f13\u5b58 |
142 | 141 | function.getweatherforecast=\u83b7\u53d6\u5929\u6c14\u9884\u62a5 |
142 | function.setaltitudetolerance=\u8bbe\u7f6e\u9ad8\u5ea6\u516c\u5dee | |
143 | 143 | |
144 | 144 | # Dialogs |
145 | 145 | dialog.exit.confirm.title=\u9000\u51fa |
378 | 378 | dialog.gpsies.activity.skating=\u6ed1\u51b0 |
379 | 379 | dialog.wikipedia.column.name=\u6587\u7ae0\u9898\u76ee |
380 | 380 | dialog.wikipedia.column.distance=\u8ddd\u79bb |
381 | dialog.wikipedia.nonefound=\u672a\u627e\u5230\u7ef4\u57fa\u767e\u79d1\u6761\u76ee | |
381 | 382 | dialog.correlate.notimestamps=\u6570\u636e\u70b9\u4e2d\u65e0\u65f6\u95f4\u4fe1\u606f\uff0c\u7167\u7247\u65e0\u6cd5\u94fe\u63a5 |
382 | 383 | dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u7167\u7247\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f |
383 | 384 | dialog.correlate.nouncorrelatedaudios=\u6240\u6709\u97f3\u9891\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f |
411 | 412 | dialog.correlate.audioselect.intro=\u9009\u62e9\u4ee5\u4e0b\u58f0\u97f3\u6587\u4ef6\u4f5c\u4e3a\u65f6\u95f4\u504f\u5dee |
412 | 413 | dialog.correlate.select.audioname=\u58f0\u97f3\u6587\u4ef6\u540d\u5b57 |
413 | 414 | dialog.correlate.select.audiolater=\u58f0\u97f3\u5ef6\u8fdf |
415 | dialog.rearrangewaypoints.desc=\u9009\u62e9\u76ee\u7684\u5730\u5e76\u6392\u5217\u8def\u70b9 | |
414 | 416 | 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 | |
420 | 424 | dialog.compress.closepoints.title=\u90bb\u8fd1\u70b9\u5220\u9664 |
421 | 425 | dialog.compress.closepoints.paramdesc=\u8303\u56f4\u7cfb\u6570 |
422 | 426 | dialog.compress.wackypoints.title=\u5f02\u5e38\u70b9\u5220\u9664 |
514 | 518 | dialog.colourchooser.red=\u7ea2 |
515 | 519 | dialog.colourchooser.green=\u7eff |
516 | 520 | 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 | |
517 | 534 | dialog.setlanguage.firstintro=\u4f60\u53ef\u4ee5\u9009\u62e9\u5df2\u6709\u8bed\u8a00,<p>\u6216\u9009\u62e9\u5916\u6302\u8bed\u8a00\u5305 |
518 | 535 | dialog.setlanguage.secondintro=\u8bf7\u4fdd\u5b58\u8bbe\u7f6e<p>\u5e76\u91cd\u542fGpsPrune\u4f7f\u8bbe\u7f6e\u751f\u6548 |
519 | 536 | dialog.setlanguage.language=\u8bed\u8a00 |
559 | 576 | dialog.weather.day.friday=\u5468\u4e94 |
560 | 577 | dialog.weather.day.saturday=\u5468\u516d |
561 | 578 | dialog.weather.day.sunday=\u5468\u65e5 |
579 | dialog.weather.wind=\u98ce\u529b | |
580 | dialog.weather.temp=\u6e29\u5ea6 | |
581 | dialog.weather.humidity=\u6e7f\u5ea6 | |
562 | 582 | 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= | |
563 | 590 | |
564 | 591 | # 3d window |
565 | 592 | dialog.3d.title=GpsPrune 3D \u663e\u793a |
714 | 741 | fieldname.altitude=\u9ad8\u5ea6 |
715 | 742 | fieldname.timestamp=\u65f6\u95f4 |
716 | 743 | fieldname.time=\u65f6\u95f4 |
744 | fieldname.date=\u65e5\u671f | |
717 | 745 | fieldname.waypointname=\u540d\u79f0 |
718 | 746 | fieldname.waypointtype=\u7c7b\u578b |
719 | 747 | fieldname.newsegment=\u6bb5 |
848 | 876 | error.cache.notthere=\u672a\u627e\u5230\u533a\u57df\u6570\u636e\u7f13\u5b58\u6587\u4ef6\u5939 |
849 | 877 | error.cache.empty=\u533a\u57df\u6570\u636e\u6587\u4ef6\u5939\u7a7a |
850 | 878 | 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 | |
852 | 879 | error.learnestimationparams.failed=\u65e0\u6cd5\u4ece\u6b64\u8f68\u8ff9\u5f97\u5230\u53c2\u6570\n \u5c1d\u8bd5\u8f7d\u5165\u66f4\u591a\u8f68\u8ff9 |
853 | 880 | error.tracksplit.nosplit=\u6b64\u8f68\u8ff9\u65e0\u6cd5\u5206\u5272 |
854 | 881 | error.downloadsrtm.nocache=\u6587\u4ef6\u65e0\u6cd5\u4fdd\u5b58\n\u8bf7\u68c0\u67e5\u78c1\u76d8\u7f13\u5b58 |
257 | 257 | // Apply timestamp to photo and its point (if any) |
258 | 258 | photo.setTimestamp(timestamp); |
259 | 259 | 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); | |
261 | 261 | } |
262 | 262 | return photo; |
263 | 263 | } |
0 | GpsPrune version 16.3 | |
1 | ===================== | |
0 | GpsPrune version 17 | |
1 | =================== | |
2 | 2 | |
3 | 3 | GpsPrune is an application for viewing, editing and managing coordinate data from GPS systems, |
4 | 4 | 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/ | |
6 | 6 | |
7 | 7 | GpsPrune is copyright 2006-2014 activityworkshop.net and distributed under the terms of the Gnu GPL version 2. |
8 | 8 | You may freely use the software, and may help others to freely use it too. For further information |
16 | 16 | ======= |
17 | 17 | |
18 | 18 | 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 | |
20 | 20 | |
21 | 21 | If the jar file is saved in a different directory, you will need to include the path. |
22 | 22 | Depending on your system settings, you may be able to click or double-click on the jar file |
24 | 24 | or other link can of course be made should you wish. |
25 | 25 | |
26 | 26 | 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 | ||
55 | 40 | New with version 16 |
56 | 41 | =================== |
57 | 42 | The following features were added since version 15: |
63 | 48 | - Function to download and save SRTM tiles |
64 | 49 | |
65 | 50 | New with version 15 |
66 | ===================== | |
51 | =================== | |
67 | 52 | The following features were added since version 14: |
68 | 53 | - Extend povray output using map image on base plane |
69 | 54 | - 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 | |
71 | 56 | - Allow altitude / speed profile to show any arbitrary field |
72 | 57 | - Accept files dragged and dropped onto the GpsPrune window |
73 | 58 | - Take account of timezone if present in track timestamps |
77 | 62 | - Allow loading of speeds and vertical speeds from text files |
78 | 63 | |
79 | 64 | New with version 14 |
80 | ===================== | |
65 | =================== | |
81 | 66 | The following features were added since version 13: |
82 | 67 | - Dragging of existing points |
83 | 68 | - Creation of new points by dragging the halfway point between two points |
119 | 104 | |
120 | 105 | New with version 11 |
121 | 106 | =================== |
122 | ||
123 | 107 | The following features were added since version 10: |
124 | 108 | - Option to select which of the named tracks to load out of a gpx file or gps |
125 | 109 | - Function to delete all values of a single field (eg all altitudes, all timestamps) |
131 | 115 | |
132 | 116 | New with version 10 |
133 | 117 | =================== |
134 | ||
135 | 118 | The following features were added since version 9: |
136 | 119 | - Function to lookup altitudes using SRTM data from the Space Shuttle |
137 | 120 | - Choice between altitude profile and speed profile in main view |
143 | 126 | |
144 | 127 | New with version 9 |
145 | 128 | ================== |
146 | ||
147 | 129 | The following features were added since version 8: |
148 | 130 | - Ability to paste coordinates (eg from wikipedia or geocaching sites) to create new points |
149 | 131 | - Configurable colour settings |
157 | 139 | |
158 | 140 | New with version 8 |
159 | 141 | ================== |
160 | ||
161 | 142 | The following features were added since version 7: |
162 | 143 | - Loading of NMEA files (with suffix .nmea) |
163 | 144 | - Loading of nearby tracks from gpsies.com |
171 | 152 | |
172 | 153 | New with version 7 |
173 | 154 | ================== |
174 | ||
175 | 155 | The following features were added since version 6: |
176 | 156 | - Loading of KMZ files and zipped GPX |
177 | 157 | - Improved compression functions with four configurable algorithms |
183 | 163 | |
184 | 164 | New with version 6 |
185 | 165 | ================== |
186 | ||
187 | 166 | The following features were added since version 5: |
188 | 167 | - Map view using OpenStreetMap images is now integrated in the main window, with control for map transparency |
189 | 168 | - Pov export has new option to use sphere sweeps for better appearance |
196 | 175 | |
197 | 176 | New with version 5 |
198 | 177 | ================== |
199 | ||
200 | The following features were added since version 4.1: | |
178 | The following features were added since version 4: | |
201 | 179 | - New map window in the View menu, showing points overlaid on OpenStreetMap images |
202 | 180 | - New function to launch a browser showing the area in either Google Maps or OpenStreetMap |
203 | 181 | - Handling of track segments, including loading, saving and exporting, and preservation during edits and undos |
208 | 186 | |
209 | 187 | New with version 4 |
210 | 188 | ================== |
211 | ||
212 | 189 | The following features were added since version 3: |
213 | 190 | - Automatic correlation of photos with points based on timestamps |
214 | 191 | - Manual disconnection of photos from points |
221 | 198 | |
222 | 199 | New with version 3 |
223 | 200 | ================== |
224 | ||
225 | 201 | The following features were added since version 2: |
226 | 202 | - Loading of GPX and KML files |
227 | 203 | - Loading of jpeg photos with or without coordinate data |
233 | 209 | |
234 | 210 | New with version 2 |
235 | 211 | ================== |
236 | ||
237 | 212 | The following features were added since version 1: |
238 | 213 | - Display of data in 3d view using Java3D library |
239 | 214 | - Export of 3d model to POV format for rendering by povray |
243 | 218 | |
244 | 219 | Features of version 1 |
245 | 220 | ===================== |
246 | ||
247 | 221 | The following features were included in version 1: |
248 | 222 | - Loading of text files, display in overhead and profile views |
249 | 223 | - Display of track details such as distances, speeds |
73 | 73 | private static final int[] FORMAT_COORDS = {Coordinate.FORMAT_NONE, Coordinate.FORMAT_DEG_MIN_SEC, |
74 | 74 | Coordinate.FORMAT_DEG_MIN, Coordinate.FORMAT_DEG}; |
75 | 75 | 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}; | |
77 | 77 | |
78 | 78 | |
79 | 79 | /** |
436 | 436 | } |
437 | 437 | } |
438 | 438 | // Get timestamp format |
439 | int timestampFormat = Timestamp.FORMAT_ORIGINAL; | |
439 | Timestamp.Format timestampFormat = Timestamp.Format.ORIGINAL; | |
440 | 440 | for (int i=0; i<_timestampUnitsRadios.length; i++) |
441 | 441 | { |
442 | 442 | if (_timestampUnitsRadios[i].isSelected()) { |
571 | 571 | * @param inTimestampFormat timestamp format |
572 | 572 | */ |
573 | 573 | private void saveField(StringBuffer inBuffer, DataPoint inPoint, Field inField, |
574 | int inCoordFormat, Unit inAltitudeUnit, int inTimestampFormat) | |
574 | int inCoordFormat, Unit inAltitudeUnit, Timestamp.Format inTimestampFormat) | |
575 | 575 | { |
576 | 576 | // Output field according to type |
577 | 577 | if (inField == Field.LATITUDE) |
594 | 594 | { |
595 | 595 | if (inPoint.hasTimestamp()) |
596 | 596 | { |
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)); | |
605 | 599 | } |
606 | 600 | } |
607 | 601 | else |
534 | 534 | source = replaceGpxTags(source, "lat=\"", "\"", inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); |
535 | 535 | source = replaceGpxTags(source, "lon=\"", "\"", inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); |
536 | 536 | 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)); | |
538 | 538 | if (inPoint.isWaypoint()) |
539 | 539 | { |
540 | 540 | source = replaceGpxTags(source, "<name>", "</name>", inPoint.getWaypointName()); |
684 | 684 | if (inPoint.hasTimestamp() && inTimestamps) |
685 | 685 | { |
686 | 686 | inWriter.write("\t\t<time>"); |
687 | inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601)); | |
687 | inWriter.write(inPoint.getTimestamp().getText(Timestamp.Format.ISO8601)); | |
688 | 688 | inWriter.write("</time>\n"); |
689 | 689 | } |
690 | 690 | // write waypoint name after elevation and time |
756 | 756 | if (inPoint.hasTimestamp() && inTimestamps) |
757 | 757 | { |
758 | 758 | 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)); | |
760 | 760 | inWriter.write("</time>\n"); |
761 | 761 | } |
762 | 762 | // photo, audio |
35 | 35 | import tim.prune.gui.BaseImageDefinitionPanel; |
36 | 36 | import tim.prune.gui.GuiGridLayout; |
37 | 37 | import tim.prune.gui.WholeNumberField; |
38 | import tim.prune.gui.colour.PointColourer; | |
38 | 39 | import tim.prune.gui.map.MapSource; |
39 | 40 | import tim.prune.gui.map.MapSourceLibrary; |
40 | 41 | import tim.prune.gui.map.MapUtils; |
288 | 289 | final int zoomFactor = 1 << _baseImagePanel.getImageDefinition().getZoom(); |
289 | 290 | Graphics g = inImage.getImage().getGraphics(); |
290 | 291 | // 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 | |
294 | 297 | final Track track = _app.getTrackInfo().getTrack(); |
295 | 298 | final int numPoints = track.getNumPoints(); |
296 | 299 | int prevX = 0, prevY = 0; |
299 | 302 | DataPoint point = track.getPoint(i); |
300 | 303 | if (!point.isWaypoint()) |
301 | 304 | { |
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 | } | |
302 | 311 | double x = track.getX(i) - xRange.getMinimum(); |
303 | 312 | double y = track.getY(i) - yRange.getMinimum(); |
304 | 313 | // use zoom level to calculate pixel coords on image |
317 | 326 | prevX = px; prevY = py; |
318 | 327 | } |
319 | 328 | } |
320 | // Draw waypoints | |
329 | ||
330 | // Now the waypoints | |
321 | 331 | final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT); |
322 | 332 | g.setColor(textColour); |
323 | // Loop over points | |
333 | // Loop again to draw waypoints | |
324 | 334 | for (int i=0; i<numPoints; i++) |
325 | 335 | { |
326 | 336 | DataPoint point = track.getPoint(i); |
50 | 50 | import tim.prune.data.Track; |
51 | 51 | import tim.prune.data.TrackInfo; |
52 | 52 | import tim.prune.data.UnitSetLibrary; |
53 | import tim.prune.gui.ColourChooser; | |
54 | import tim.prune.gui.ColourPatch; | |
55 | 53 | import tim.prune.gui.DialogCloser; |
56 | 54 | import tim.prune.gui.ImageUtils; |
57 | 55 | import tim.prune.gui.WholeNumberField; |
56 | import tim.prune.gui.colour.ColourChooser; | |
57 | import tim.prune.gui.colour.ColourPatch; | |
58 | 58 | import tim.prune.load.GenericFileFilter; |
59 | 59 | import tim.prune.save.xml.XmlUtils; |
60 | 60 | |
683 | 683 | // Add timestamp (if any) to the list |
684 | 684 | whenList.append("<when>"); |
685 | 685 | if (point.hasTimestamp()) { |
686 | whenList.append(point.getTimestamp().getText(Timestamp.FORMAT_ISO_8601)); | |
686 | whenList.append(point.getTimestamp().getText(Timestamp.Format.ISO8601)); | |
687 | 687 | } |
688 | 688 | whenList.append("</when>\n"); |
689 | 689 | // Add coordinates to the list |
320 | 320 | { |
321 | 321 | // file saved - store directory in config for later |
322 | 322 | Config.setConfigString(Config.KEY_TRACK_DIR, povFile.getParentFile().getAbsolutePath()); |
323 | // also store exaggeration | |
323 | // also store exaggeration and grid size | |
324 | 324 | 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 | } | |
325 | 328 | } |
326 | 329 | else |
327 | 330 | { |
7 | 7 | */ |
8 | 8 | public abstract class UndoDeleteOperation implements UndoOperation |
9 | 9 | { |
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 | ||
10 | 21 | /** |
11 | 22 | * Modify the current point/range selection after the delete operation is undone |
12 | 23 | * @param inTrackInfo track info object |
13 | 24 | * @param inStartIndex start index of reinserted range |
14 | 25 | * @param inEndIndex end index of reinserted range |
15 | 26 | */ |
16 | protected static void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex) | |
27 | protected void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex) | |
17 | 28 | { |
18 | 29 | final int numPointsInserted = inEndIndex - inStartIndex + 1; |
19 | 30 | // See if there is a currently selected point, if so does it need to be modified |
25 | 36 | // Same for currently selected range |
26 | 37 | int rangeStart = inTrackInfo.getSelection().getStart(); |
27 | 38 | 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) | |
29 | 43 | { |
30 | 44 | rangeEnd += numPointsInserted; |
31 | 45 | if (rangeStart >= inStartIndex) { |
32 | 46 | rangeStart += numPointsInserted; |
33 | 47 | } |
48 | // Extend selection if the deleted point was at the start or end | |
49 | if (wasAtStart) { | |
50 | rangeStart--; | |
51 | } | |
34 | 52 | inTrackInfo.getSelection().selectRange(rangeStart, rangeEnd); |
35 | 53 | } |
36 | 54 | } |