import tim.prune.load.TrackNameList;
import tim.prune.save.ExifSaver;
import tim.prune.save.FileSaver;
+import tim.prune.tips.TipManager;
import tim.prune.undo.*;
return _undoStack;
}
+
+ /**
+ * Show the specified tip if appropriate
+ * @param inTipNumber tip number from TipManager
+ */
+ public void showTip(int inTipNumber)
+ {
+ String key = TipManager.fireTipTrigger(inTipNumber);
+ if (key != null && !key.equals(""))
+ {
+ JOptionPane.showMessageDialog(_frame, I18nManager.getText(key),
+ I18nManager.getText("tip.title"), JOptionPane.INFORMATION_MESSAGE);
+ }
+ }
+
+
/**
* Load the specified data files one by one
* @param inDataFiles arraylist containing File objects to load
// ensure track's field list contains point's fields
_track.extendFieldList(inPoint.getFieldList());
_trackInfo.selectPoint(inIndex);
- final int selStart = _trackInfo.getSelection().getStart();
- final int selEnd = _trackInfo.getSelection().getEnd();
+ final int selStart = _trackInfo.getSelection().getStart();
+ final int selEnd = _trackInfo.getSelection().getEnd();
if (selStart < inIndex && selEnd > inIndex)
{
// Extend end of selection by 1
public static final int TOOL_GPSBABEL = 1;
/** Constant for Gnuplot */
public static final int TOOL_GNUPLOT = 2;
+ /** Constant for Xerces xml library */
+ public static final int TOOL_XERCES = 3;
/** Config keys in order that the tools are defined above */
private static final String[] CONFIG_KEYS = {Config.KEY_EXIFTOOL_PATH, Config.KEY_GPSBABEL_PATH, Config.KEY_GNUPLOT_PATH};
/** Verification flags for the tools in the order defined above */
if (toolPath != null && toolPath.length() > 0) {
return check(toolPath + " " + VERIFY_FLAGS[inToolNum]);
}
+ break;
+ case TOOL_XERCES:
+ try {
+ return Class.forName("org.apache.xerces.parsers.SAXParser").getClassLoader() != null;
+ }
+ catch (ClassNotFoundException e) {
+ // System.err.println(e.getClass().getName() + " : " + e.getMessage());
+ }
+ break;
}
// Not found
return false;
import tim.prune.function.*;
import tim.prune.function.charts.Charter;
import tim.prune.function.compress.CompressTrackFunction;
+import tim.prune.function.compress.MarkPointsInRectangleFunction;
import tim.prune.function.distance.DistanceFunction;
import tim.prune.function.edit.PointNameEditor;
import tim.prune.function.estimate.EstimateTime;
import tim.prune.function.estimate.LearnParameters;
import tim.prune.function.gpsies.GetGpsiesFunction;
import tim.prune.function.gpsies.UploadGpsiesFunction;
+import tim.prune.function.sew.SewTrackSegmentsFunction;
+import tim.prune.function.sew.SplitSegmentsFunction;
+import tim.prune.function.srtm.DownloadSrtmFunction;
import tim.prune.function.srtm.LookupSrtmFunction;
+import tim.prune.function.weather.GetWeatherForecastFunction;
import tim.prune.load.AudioLoader;
import tim.prune.load.BabelLoadFromFile;
import tim.prune.load.BabelLoadFromGps;
public static GenericFunction FUNCTION_SAVECONFIG = null;
public static GenericFunction FUNCTION_EDIT_WAYPOINT_NAME = null;
public static RearrangeWaypointsFunction FUNCTION_REARRANGE_WAYPOINTS = null;
+ public static GenericFunction FUNCTION_SPLIT_SEGMENTS = null;
+ public static GenericFunction FUNCTION_SEW_SEGMENTS = null;
public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null;
public static GenericFunction FUNCTION_COMPRESS = null;
public static GenericFunction FUNCTION_DELETE_RANGE = null;
public static GenericFunction FUNCTION_CROP_TRACK = null;
+ public static GenericFunction FUNCTION_MARK_IN_RECTANGLE = null;
public static GenericFunction FUNCTION_INTERPOLATE = null;
public static GenericFunction FUNCTION_LOOKUP_SRTM = null;
+ public static GenericFunction FUNCTION_DOWNLOAD_SRTM = null;
public static GenericFunction FUNCTION_LOOKUP_WIKIPEDIA = null;
public static GenericFunction FUNCTION_SEARCH_WIKIPEDIA = null;
public static GenericFunction FUNCTION_DOWNLOAD_OSM = null;
public static GenericFunction FUNCTION_LEARN_ESTIMATION_PARAMS = null;
public static GenericFunction FUNCTION_GET_GPSIES = null;
public static GenericFunction FUNCTION_UPLOAD_GPSIES = null;
+ public static GenericFunction FUNCTION_GET_WEATHER_FORECAST = null;
public static GenericFunction FUNCTION_LOAD_AUDIO = null;
public static GenericFunction FUNCTION_REMOVE_AUDIO = null;
public static GenericFunction FUNCTION_CORRELATE_AUDIOS = null;
FUNCTION_SAVECONFIG = new SaveConfig(inApp);
FUNCTION_EDIT_WAYPOINT_NAME = new PointNameEditor(inApp);
FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp);
+ FUNCTION_SPLIT_SEGMENTS = new SplitSegmentsFunction(inApp);
+ FUNCTION_SEW_SEGMENTS = new SewTrackSegmentsFunction(inApp);
FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp);
FUNCTION_COMPRESS = new CompressTrackFunction(inApp);
FUNCTION_DELETE_RANGE = new DeleteSelectedRangeFunction(inApp);
FUNCTION_CROP_TRACK = new CropToSelection(inApp);
+ FUNCTION_MARK_IN_RECTANGLE = new MarkPointsInRectangleFunction(inApp);
FUNCTION_INTERPOLATE = new InterpolateFunction(inApp);
FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp);
+ FUNCTION_DOWNLOAD_SRTM = new DownloadSrtmFunction(inApp);
FUNCTION_LOOKUP_WIKIPEDIA = new GetWikipediaFunction(inApp);
FUNCTION_SEARCH_WIKIPEDIA = new SearchWikipediaNames(inApp);
FUNCTION_DOWNLOAD_OSM = new DownloadOsmFunction(inApp);
FUNCTION_LEARN_ESTIMATION_PARAMS = new LearnParameters(inApp);
FUNCTION_GET_GPSIES = new GetGpsiesFunction(inApp);
FUNCTION_UPLOAD_GPSIES = new UploadGpsiesFunction(inApp);
+ FUNCTION_GET_WEATHER_FORECAST = new GetWeatherForecastFunction(inApp);
FUNCTION_LOAD_AUDIO = new AudioLoader(inApp);
FUNCTION_REMOVE_AUDIO = new RemoveAudioFunction(inApp);
FUNCTION_CORRELATE_AUDIOS = new AudioCorrelator(inApp);
/**
* GpsPrune is a tool to visualize, edit, convert and prune GPS data
* Please see the included readme.txt or http://activityworkshop.net
- * This software is copyright activityworkshop.net 2006-2013 and made available through the Gnu GPL version 2.
+ * This software is copyright activityworkshop.net 2006-2014 and made available through the Gnu GPL version 2.
* For license details please see the included license.txt.
* GpsPrune is the main entry point to the application, including initialisation and launch
*/
public class GpsPrune
{
/** Version number of application, used in about screen and for version check */
- public static final String VERSION_NUMBER = "15.2";
+ public static final String VERSION_NUMBER = "16";
/** Build number, just used for about screen */
- public static final String BUILD_NUMBER = "283b";
+ public static final String BUILD_NUMBER = "301";
/** Static reference to App object */
private static App APP = null;
// return the key itself
return inKey;
}
+
+ /**
+ * Lookup the given key and return the associated text, formatting with the number
+ * @param inKey key to lookup (text should contain a %d)
+ * @param inNumber number to substitute into the %d
+ * @return associated text, or the key if not found
+ */
+ public static String getTextWithNumber(String inKey, int inNumber)
+ {
+ String localText = getText(inKey);
+ try
+ {
+ localText = String.format(localText, inNumber);
+ }
+ catch (Exception e)
+ {} // printf formatting didn't work, maybe the placeholders are wrong?
+ return localText;
+ }
}
public abstract class UpdateMessageBroker
{
private static final int MAXIMUM_NUMBER_SUBSCRIBERS = 6;
+ /** Array of all subscribers */
private static DataSubscriber[] _subscribers = new DataSubscriber[MAXIMUM_NUMBER_SUBSCRIBERS];
+ /** Counter of the number of subscribers added so far */
private static int _subscriberNum = 0;
+ /** Enable/disabled flag */
+ private static boolean _enabled = true;
/**
_subscriberNum++;
}
+ /**
+ * Enable or disable the messaging (to allow temporary disabling for multiple operations)
+ * @param inEnabled false to disable, true to enable again
+ */
+ public static void enableMessaging(boolean inEnabled)
+ {
+ _enabled = inEnabled;
+ }
/**
* Send a message to all subscribers that
public static void informSubscribers(byte inChange)
{
// TODO: Launch separate thread so that whatever caused the inform can finish
+ if (!_enabled) return;
for (int i=0; i<_subscribers.length; i++)
{
if (_subscribers[i] != null)
*/
public static void informSubscribers(String inMessage)
{
+ if (!_enabled) return;
for (int i=0; i<_subscribers.length; i++)
{
if (_subscribers[i] != null)
import tim.prune.data.RecentFileList;
import tim.prune.data.UnitSet;
import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.map.MapSourceLibrary;
/**
public static final String KEY_RECENT_FILES = "prune.recentfiles";
/** Key for estimation parameters */
public static final String KEY_ESTIMATION_PARAMS = "prune.estimationparams";
+ /** Key for 3D exaggeration factor */
+ public static final String KEY_HEIGHT_EXAGGERATION = "prune.heightexaggeration";
/** Initialise the default properties */
_colourScheme.loadFromHex(_configValues.getProperty(KEY_COLOUR_SCHEME));
_recentFiles = new RecentFileList(_configValues.getProperty(KEY_RECENT_FILES));
_unitSet = UnitSetLibrary.getUnitSet(_configValues.getProperty(KEY_UNITSET_KEY));
+ // Adjust map source index if necessary
+ adjustSelectedMap();
if (loadFailed) {
throw new ConfigException();
props.put(KEY_KMZ_IMAGE_SIZE, "240");
props.put(KEY_AUTOSAVE_SETTINGS, "0"); // autosave false by default
props.put(KEY_UNITSET_KEY, "unitset.kilometres"); // metric by default
+ props.put(KEY_HEIGHT_EXAGGERATION, "100"); // 100%, no exaggeration
return props;
}
+ /**
+ * Adjust the index of the selected map
+ * (only required if config was loaded from a previous version of GpsPrune)
+ */
+ private static void adjustSelectedMap()
+ {
+ int sourceNum = getConfigInt(Config.KEY_MAPSOURCE_INDEX);
+ int prevNumFixed = getConfigInt(Config.KEY_NUM_FIXED_MAPS);
+ // Number of fixed maps not specified in version <=13, default to 6
+ if (prevNumFixed == 0) prevNumFixed = 6;
+ int currNumFixed = MapSourceLibrary.getNumFixedSources();
+ // Only need to do something if the number has changed
+ if (currNumFixed != prevNumFixed && (sourceNum >= prevNumFixed || sourceNum >= currNumFixed))
+ {
+ sourceNum += (currNumFixed - prevNumFixed);
+ setConfigInt(Config.KEY_MAPSOURCE_INDEX, sourceNum);
+ }
+ setConfigInt(Config.KEY_NUM_FIXED_MAPS, currNumFixed);
+ }
+
/**
* @param inString String to parse
* @return int value of String, or 0 if unparseable
-The source code of GpsPrune is copyright 2006-2013 activityworkshop.net
+The source code of GpsPrune is copyright 2006-2014 activityworkshop.net
and is distributed under the terms of the Gnu GPL version 2.
Portions of the package jpeg.drew (if included in this package) were taken
import tim.prune.data.Track;
import tim.prune.data.Unit;
import tim.prune.data.UnitSetLibrary;
+import tim.prune.tips.TipManager;
/**
* Abstract superclass of the two correlator functions
{
protected JDialog _dialog;
private CardStack _cards = null;
- private JLabel _tipLabel = null;
private JTable _selectionTable = null;
protected JTable _previewTable = null;
private boolean _previewEnabled = false; // flag required to enable preview function on final panel
private JRadioButton _timeLimitRadio = null, _distLimitRadio = null;
private JTextField _limitMinBox = null, _limitSecBox = null;
private JTextField _limitDistBox = null;
- private JComboBox _distUnitsDropdown = null;
+ private JComboBox<String> _distUnitsDropdown = null;
private JButton _nextButton = null, _backButton = null;
protected JButton _okButton = null;
_cards.showCard(card);
showCard(0); // does set up and next/prev enabling
_okButton.setEnabled(false);
- _tipLabel.setVisible(!isCardEnabled(1));
+ if (!isCardEnabled(1)) {
+ _app.showTip(TipManager.Tip_ManuallyCorrelateOne);
+ }
_dialog.setVisible(true);
}
card2.setLayout(new BorderLayout());
JPanel card2Top = new JPanel();
card2Top.setLayout(new BoxLayout(card2Top, BoxLayout.Y_AXIS));
- _tipLabel = new JLabel(I18nManager.getText("dialog.correlate.options.tip"));
- _tipLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
- card2Top.add(_tipLabel);
JLabel introLabel = new JLabel(I18nManager.getText("dialog.correlate.options.intro"));
introLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
card2Top.add(introLabel);
noTimeLimitRadio.addItemListener(optionsChangedListener);
noTimeLimitRadio.addActionListener(radioListener);
timeLimitPanel.add(noTimeLimitRadio);
- _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + " : ");
+ _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + ": ");
_timeLimitRadio.addItemListener(optionsChangedListener);
_timeLimitRadio.addActionListener(radioListener);
timeLimitPanel.add(_timeLimitRadio);
noDistLimitRadio.addItemListener(optionsChangedListener);
noDistLimitRadio.addActionListener(radioListener);
distLimitPanel.add(noDistLimitRadio);
- _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit"));
+ _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit") + ": ");
_distLimitRadio.addItemListener(optionsChangedListener);
_distLimitRadio.addActionListener(radioListener);
distLimitPanel.add(_distLimitRadio);
distLimitPanel.add(_limitDistBox);
String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"),
I18nManager.getText("units.miles")};
- _distUnitsDropdown = new JComboBox(distUnitsOptions);
+ _distUnitsDropdown = new JComboBox<String>(distUnitsOptions);
_distUnitsDropdown.addItemListener(optionsChangedListener);
distLimitPanel.add(_distUnitsDropdown);
limitsPanel.add(distLimitPanel);
* @param inIndex index number starting at zero
* @return field value, or null if not found
*/
- public String getFieldValue(int inIndex)
+ private String getFieldValue(int inIndex)
{
if (_fieldValues == null || inIndex < 0 || inIndex >= _fieldValues.length)
return null;
return (inOther._waypointName != null && inOther._waypointName.equals(_waypointName));
}
+ /**
+ * Add an altitude offset to this point, and keep the point's string value in sync
+ * @param inOffset offset as double
+ * @param inUnit unit of offset, feet or metres
+ * @param inDecimals number of decimal places
+ */
+ public void addAltitudeOffset(double inOffset, Unit inUnit, int inDecimals)
+ {
+ if (hasAltitude())
+ {
+ _altitude.addOffset(inOffset, inUnit, inDecimals);
+ _fieldValues[_fieldList.getFieldIndex(Field.ALTITUDE)] = _altitude.getStringValue(null);
+ setModified(false);
+ }
+ }
+
+ /**
+ * Reset the altitude to the previous value (by an undo)
+ * @param inClone altitude object cloned from earlier
+ */
+ public void resetAltitude(Altitude inClone)
+ {
+ _altitude.reset(inClone);
+ _fieldValues[_fieldList.getFieldIndex(Field.ALTITUDE)] = _altitude.getStringValue(null);
+ setModified(true);
+ }
+
+ /**
+ * Add a time offset to this point
+ * @param inOffset offset to add (-ve to subtract)
+ */
+ public void addTimeOffset(long inOffset)
+ {
+ if (hasTimestamp())
+ {
+ _timestamp.addOffset(inOffset);
+ _fieldValues[_fieldList.getFieldIndex(Field.TIMESTAMP)] = _timestamp.getText();
+ setModified(false);
+ }
+ }
/**
* Set the photo for this data point
_empty = false;
}
+ /**
+ * Combine this range with another one
+ * @param inOtherRange other range to add to this one
+ */
+ public void combine(DoubleRange inOtherRange)
+ {
+ if (inOtherRange != null && inOtherRange.getRange() > 1.0)
+ {
+ addValue(inOtherRange.getMinimum());
+ addValue(inOtherRange.getMaximum());
+ }
+ }
/**
* @return true if data values were found
*/
public class PointScaler
{
- // Original data
+ /** Original data */
private Track _track = null;
- // Scaled values
+ /** Secondary data for terrain grid */
+ private Track _terrainTrack = null;
+ // Scaled values for data track
private double[] _xValues = null;
private double[] _yValues = null;
private double[] _altValues = null;
+ // Scaled values for terrain track, if any
+ private double[] _terrainxValues = null;
+ private double[] _terrainyValues = null;
+ private double[] _terrainAltValues = null;
// Altitude range
private double _altitudeRange = 0.0;
+ private double _minAltitudeMetres = 0.0;
+ // Horizontal distance
+ private double _horizDistanceMetres = 0.0;
/**
_track = inTrack;
}
+ /**
+ * @param inTrack terrain track to add
+ */
+ public void addTerrain(Track inTrack)
+ {
+ _terrainTrack = inTrack;
+ }
/**
* Scale the points
// Work out extents
TrackExtents extents = new TrackExtents(_track);
extents.applySquareBorder();
- final double horizDistance = Math.max(extents.getHorizontalDistanceMetres(), 1.0);
+ _horizDistanceMetres = Math.max(extents.getHorizontalDistanceMetres(), 1.0);
final int numPoints = _track.getNumPoints();
- // Find altitude range
- _altitudeRange = extents.getAltitudeRange().getRange() / horizDistance;
- final double minAltitude = extents.getAltitudeRange().getMinimum();
+ // Find altitude range (including terrain)
+ DoubleRange altRangeMetres = extents.getAltitudeRange();
+ if (_terrainTrack != null) {
+ altRangeMetres.combine(new TrackExtents(_terrainTrack).getAltitudeRange());
+ }
+ _altitudeRange = altRangeMetres.getRange() / _horizDistanceMetres;
+ _minAltitudeMetres = altRangeMetres.getMinimum();
// create new arrays for scaled values
if (_xValues == null || _xValues.length != numPoints)
_xValues = new double[numPoints];
_yValues = new double[numPoints];
_altValues = new double[numPoints];
+ if (_terrainTrack != null)
+ {
+ _terrainxValues = new double[_terrainTrack.getNumPoints()];
+ _terrainyValues = new double[_terrainTrack.getNumPoints()];
+ _terrainAltValues = new double[_terrainTrack.getNumPoints()];
+ }
}
final double midXvalue = extents.getXRange().getMidValue();
{
_xValues[p] = (_track.getX(p) - midXvalue) / xyRange;
_yValues[p] = (midYvalue - _track.getY(p)) / xyRange; // y values have to be inverted
- _altValues[p] = (point.getAltitude().getMetricValue() - minAltitude) / horizDistance;
+ _altValues[p] = (point.getAltitude().getMetricValue() - _minAltitudeMetres) / _horizDistanceMetres;
+ }
+ }
+ if (_terrainTrack != null)
+ {
+ for (int p=0; p<_terrainTrack.getNumPoints(); p++)
+ {
+ _terrainxValues[p] = (_terrainTrack.getX(p) - midXvalue) / xyRange;
+ _terrainyValues[p] = (midYvalue - _terrainTrack.getY(p)) / xyRange; // y values have to be inverted
+ _terrainAltValues[p] = (_terrainTrack.getPoint(p).getAltitude().getMetricValue() - _minAltitudeMetres) / _horizDistanceMetres;
}
}
}
}
/**
- * @return altitude range, in metres
+ * @return altitude range as fraction of horizontal range
*/
public double getAltitudeRange()
{
return _altitudeRange;
}
+
+ /**
+ * Get the horizontal value for the specified point
+ * @param inIndex index of point, starting at 0
+ * @return scaled horizontal value
+ */
+ public double getTerrainHorizValue(int inIndex)
+ {
+ return _terrainxValues[inIndex];
+ }
+
+ /**
+ * Get the vertical value for the specified point
+ * @param inIndex index of point, starting at 0
+ * @return scaled vertical value
+ */
+ public double getTerrainVertValue(int inIndex)
+ {
+ return _terrainyValues[inIndex];
+ }
+
+ /**
+ * @param inIndex index of point in terrain track
+ * @return scaled altitude value for the specified terrain point
+ */
+ public double getTerrainAltValue(int inIndex)
+ {
+ if (_terrainAltValues != null) {
+ return _terrainAltValues[inIndex];
+ }
+ return 0.0;
+ }
}
*/
public class RangeStats
{
+ // MAYBE: Split into basic stats (quick to calculate, for detailsdisplay) and full stats (for other two)
private boolean _valid = false;
private int _numPoints = 0;
private int _startIndex = 0, _endIndex = 0;
private AltitudeRange _gentleAltitudeRange = null, _steepAltitudeRange = null;
private Timestamp _earliestTimestamp = null, _latestTimestamp = null;
private long _movingMilliseconds = 0L;
- private boolean _timestampsIncomplete = false;
+ private boolean _timesIncomplete = false;
+ private boolean _timesOutOfSequence = false;
private double _totalDistanceRads = 0.0, _movingDistanceRads = 0.0;
// Note, maximum speed is not calculated here, use the SpeedData method instead
if (!p.getSegmentStart() && prevPoint != null && prevPoint.hasTimestamp())
{
long millisLater = p.getTimestamp().getMillisecondsSince(prevPoint.getTimestamp());
- if (millisLater < 0) {_timestampsIncomplete = true;}
- _movingMilliseconds += millisLater;
+ if (millisLater < 0) {_timesOutOfSequence = true;}
+ else {
+ _movingMilliseconds += millisLater;
+ }
}
}
- else if (!p.getSegmentStart()) {
- _timestampsIncomplete = true;
+ else {
+ _timesIncomplete = true;
}
prevPoint = p;
return _movingMilliseconds / 1000;
}
- /** @return true if any timestamps are missing or out of sequence */
+ /** @return true if any timestamps are missing */
public boolean getTimestampsIncomplete() {
- return _timestampsIncomplete;
+ return _timesIncomplete;
+ }
+
+ /** @return true if any timestamps are out of sequence */
+ public boolean getTimestampsOutOfSequence() {
+ return _timesOutOfSequence;
}
/** @return total distance in the current distance units (km or mi) */
private int _currentPhotoIndex = -1;
private int _currentAudioIndex = -1;
private AltitudeRange _altitudeRange = null;
- private long _totalSeconds = 0L, _movingSeconds = 0L;
- private double _angDistance = -1.0, _angMovingDistance = -1.0;
+ private long _movingSeconds = 0L;
+ private double _angMovingDistance = -1.0;
/**
{
final int numPoints = _track.getNumPoints();
// Recheck if the number of points has changed
- if (numPoints != _prevNumPoints) {
+ if (numPoints != _prevNumPoints)
+ {
_prevNumPoints = numPoints;
check();
}
{
_altitudeRange = new AltitudeRange();
Altitude altitude = null;
- Timestamp time = null, startTime = null, endTime = null;
- Timestamp previousTime = null;
+ Timestamp time = null, previousTime = null;
DataPoint lastPoint = null, currPoint = null;
- _angDistance = 0.0; _angMovingDistance = 0.0;
- _totalSeconds = 0L; _movingSeconds = 0L;
+ _angMovingDistance = 0.0;
+ _movingSeconds = 0L;
// Loop over points in selection
for (int i=_startIndex; i<=_endIndex; i++)
{
// Ignore waypoints in altitude calculations
if (!currPoint.isWaypoint() && altitude.isValid())
{
- _altitudeRange.addValue(altitude);
+ if (currPoint.getSegmentStart()) {
+ _altitudeRange.ignoreValue(altitude);
+ }
+ else {
+ _altitudeRange.addValue(altitude);
+ }
}
- // Store the first and last timestamp in the range
+ // Compare timestamps within the segments
time = currPoint.getTimestamp();
if (time.isValid())
{
- if (startTime == null || startTime.isAfter(time)) startTime = time;
- if (endTime == null || time.isAfter(endTime)) endTime = time;
// add moving time
if (!currPoint.getSegmentStart() && previousTime != null && time.isAfter(previousTime)) {
_movingSeconds += time.getSecondsSince(previousTime);
if (lastPoint != null)
{
double radians = DataPoint.calculateRadiansBetween(lastPoint, currPoint);
- _angDistance += radians;
if (!currPoint.getSegmentStart()) {
_angMovingDistance += radians;
}
lastPoint = currPoint;
}
}
- if (endTime != null) {
- _totalSeconds = endTime.getSecondsSince(startTime);
- }
}
_valid = true;
}
}
- /**
- * @return number of seconds spanned by selection
- */
- public long getNumSeconds()
- {
- if (!_valid) recalculate();
- return _totalSeconds;
- }
-
/**
* @return number of seconds spanned by segments within selection
*/
return _movingSeconds;
}
- /**
- * @return distance of Selection in specified units
- */
- public double getDistance()
- {
- return Distance.convertRadiansToDistance(_angDistance);
- }
-
/**
* @return moving distance of Selection in current units
*/
// Loop over all points within range
for (int i=inStart; i<=inEnd; i++)
{
- Timestamp timestamp = _dataPoints[i].getTimestamp();
- if (timestamp != null)
+ DataPoint p = _dataPoints[i];
+ if (p != null && p.hasTimestamp())
{
// This point has a timestamp so add the offset to it
foundTimestamp = true;
- timestamp.addOffset(inOffset);
- _dataPoints[i].setModified(inUndo);
+ p.addTimeOffset(inOffset);
+ p.setModified(inUndo);
}
}
return foundTimestamp;
// Loop over all points within range
for (int i=inStart; i<=inEnd; i++)
{
- Altitude alt = _dataPoints[i].getAltitude();
- if (alt != null && alt.isValid())
+ DataPoint p = _dataPoints[i];
+ if (p != null && p.hasAltitude())
{
// This point has an altitude so add the offset to it
foundAlt = true;
- alt.addOffset(inOffset, inUnit, inDecimals);
- _dataPoints[i].setModified(false);
+ p.addAltitudeOffset(inOffset, inUnit, inDecimals);
+ p.setModified(false);
}
}
// needs to be scaled again
new JLabel(System.getProperty("java.runtime.version")),
1, 1);
// Create install labels to be populated later
- final int NUM_INSTALL_CHECKS = 4;
+ final int NUM_INSTALL_CHECKS = 5;
_installedLabels = new JLabel[NUM_INSTALL_CHECKS];
for (int i=0; i<NUM_INSTALL_CHECKS; i++) {
_installedLabels[i] = new JLabel("...");
new JLabel(I18nManager.getText("dialog.about.systeminfo.gnuplot") + " : "),
0, 5);
addToGridBagPanel(sysInfoPanel, gridBag, constraints, _installedLabels[3], 1, 5);
+ addToGridBagPanel(sysInfoPanel, gridBag, constraints, new JLabel("Xerces : "), 0, 6);
+ addToGridBagPanel(sysInfoPanel, gridBag, constraints, _installedLabels[4], 1, 6);
// Exif library
addToGridBagPanel(sysInfoPanel, gridBag, constraints,
new JLabel(I18nManager.getText("dialog.about.systeminfo.exiflib") + " : "),
- 0, 6);
+ 0, 7);
final String exiflibkey = "dialog.about.systeminfo.exiflib." + ExifGateway.getDescriptionKey();
addToGridBagPanel(sysInfoPanel, gridBag, constraints,
- new JLabel(I18nManager.getText(exiflibkey)), 1, 6);
+ new JLabel(I18nManager.getText(exiflibkey)), 1, 7);
_tabs.add(I18nManager.getText("dialog.about.systeminfo"), sysInfoPanel);
// Third pane for credits
new JLabel(" katpatuka, R\u00E9mi, Marcus, Ali, Javier, Jeroen, prot_d, Gy\u00F6rgy,"),
1, 5);
addToGridBagPanel(creditsPanel, gridBag, constraints,
- new JLabel(" HooAU, Sergey"),
+ new JLabel(" HooAU, Sergey, P\u00E9ter, serhijdubyk"),
1, 6);
addToGridBagPanel(creditsPanel, gridBag, constraints,
new JLabel(I18nManager.getText("dialog.about.credits.translations") + " : "),
new JLabel(I18nManager.getText("dialog.about.credits.devtools") + " : "),
0, 8);
addToGridBagPanel(creditsPanel, gridBag, constraints,
- new JLabel("Debian Linux, Sun Java, Eclipse, Svn, Gimp, Inkscape"),
+ new JLabel("Debian Linux, Sun Java, OpenJDK, Eclipse, Svn, Gimp, Inkscape"),
1, 8);
addToGridBagPanel(creditsPanel, gridBag, constraints,
new JLabel(I18nManager.getText("dialog.about.credits.othertools") + " : "),
0, 9);
addToGridBagPanel(creditsPanel, gridBag, constraints,
- new JLabel("Openstreetmap, Povray, Exiftool, Google Earth, Gpsbabel, Gnuplot"),
+ new JLabel("Openstreetmap, Povray, Exiftool, Gpsbabel, Gnuplot"),
1, 9);
addToGridBagPanel(creditsPanel, gridBag, constraints,
new JLabel(I18nManager.getText("dialog.about.credits.thanks") + " : "),
String yesText = I18nManager.getText("dialog.about.yes");
String noText = I18nManager.getText("dialog.about.no");
_installedLabels[0].setText(WindowFactory.isJava3dEnabled()?yesText:noText);
- final int[] tools = {ExternalTools.TOOL_EXIFTOOL, ExternalTools.TOOL_GPSBABEL, ExternalTools.TOOL_GNUPLOT};
+ final int[] tools = {ExternalTools.TOOL_EXIFTOOL, ExternalTools.TOOL_GPSBABEL,
+ ExternalTools.TOOL_GNUPLOT, ExternalTools.TOOL_XERCES};
for (int i=0; i<tools.length; i++) {
_installedLabels[i+1].setText(ExternalTools.isToolInstalled(tools[i])?yesText:noText);
}
private JTextField _oNameField = null;
private JTextField _baseUrlField = null, _topUrlField = null;
private JRadioButton[] _baseTypeRadios = null, _topTypeRadios = null;
- private JComboBox _oZoomCombo = null;
+ private JComboBox<Integer> _oZoomCombo = null;
// controls for cloudmade panel
private JTextField _cNameField = null;
private JTextField _cStyleField = null;
- private JComboBox _cZoomCombo = null;
+ private JComboBox<Integer> _cZoomCombo = null;
private JButton _okButton = null;
/** array of file types */
// Max zoom
c.gridx = 0; c.gridy = 3;
gbPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.maxzoom")), c);
- _oZoomCombo = new JComboBox();
+ _oZoomCombo = new JComboBox<Integer>();
for (int i=10; i<=20; i++) {
- _oZoomCombo.addItem("" + i);
+ _oZoomCombo.addItem(i);
}
// zoom dropdown needs listener to call enableOk()
_oZoomCombo.addActionListener(okEnabler);
_cStyleField.addKeyListener(keyListener);
cloudGridPanel.add(_cStyleField);
cloudGridPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.maxzoom")));
- _cZoomCombo = new JComboBox();
+ _cZoomCombo = new JComboBox<Integer>();
for (int i=10; i<=20; i++) {
- _cZoomCombo.addItem("" + i);
+ _cZoomCombo.addItem(i);
}
cloudGridPanel.add(_cZoomCombo);
cloudPanel.add(cloudGridPanel, BorderLayout.NORTH);
public class DeleteFieldValues extends GenericFunction
{
private JDialog _dialog = null;
- private JList _fieldList = null;
+ private JList<String> _fieldList = null;
private FieldListModel _listModel = null;
private JButton _okButton = null;
dialogPanel.setLayout(new BorderLayout());
dialogPanel.add(new JLabel(I18nManager.getText("dialog.deletefieldvalues.intro")), BorderLayout.NORTH);
// List in centre
- _fieldList = new JList(new String[] {"First field", "Second field"});
+ _fieldList = new JList<String>(new String[] {"First field", "Second field"});
// These entries will be replaced by the initDialog method
_fieldList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
_fieldList.addListSelectionListener(new ListSelectionListener() {
import tim.prune.App;
import tim.prune.GenericFunction;
+import tim.prune.threedee.ImageDefinition;
+import tim.prune.threedee.TerrainDefinition;
/**
* Abstract superclass for pov and svg export functions
{
/** altitude exaggeration factor */
protected double _altFactor = 5.0;
+ /** definition of terrain */
+ protected TerrainDefinition _terrainDef = null;
+ /** definition of base image */
+ protected ImageDefinition _imageDef = null;
/**
* Required constructor
_altFactor = inFactor;
}
}
+
+ /**
+ * @param inDefinition terrain definition, or null
+ */
+ public void setTerrainDefinition(TerrainDefinition inDefinition)
+ {
+ _terrainDef = inDefinition;
+ }
+
+ /**
+ * @param inDefinition image definition, or null
+ */
+ public void setImageDefinition(ImageDefinition inDefinition)
+ {
+ _imageDef = inDefinition;
+ }
}
/**
* Class to act as a list model for the delete field values function
*/
-public class FieldListModel extends AbstractListModel
+public class FieldListModel extends AbstractListModel<String>
{
/** ArrayList containing fields */
private ArrayList<Field> _fields = new ArrayList<Field>();
* @param inRow row number
* @return String for specified row
*/
- public Object getElementAt(int inRow)
+ public String getElementAt(int inRow)
{
if (inRow < 0 || inRow >= getSize()) {return null;}
return _fields.get(inRow).getName();
private WaypointNameMatcher _nameMatcher = null;
private JDialog _dialog = null;
private JTextField _searchField = null;
- private JList _pointList = null;
+ private JList<String> _pointList = null;
private JButton _okButton = null;
// middle panel with list
_nameMatcher = new WaypointNameMatcher();
- _pointList = new JList(_nameMatcher);
+ _pointList = new JList<String>(_nameMatcher);
_pointList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
{
final boolean isMultiSegments = (stats.getNumSegments() > 1);
// Set visibility of third column accordingly
_movingDistanceLabel.setVisible(isMultiSegments);
- _movingDurationLabel.setVisible(isMultiSegments);
+ _movingDurationLabel.setVisible(isMultiSegments || stats.getTimestampsOutOfSequence());
+ // FIXME: What to show if timestamps are out of sequence? Warning message?
_movingClimbLabel.setVisible(isMultiSegments);
_movingDescentLabel.setVisible(isMultiSegments);
_movingSpeedLabel.setVisible(isMultiSegments);
import tim.prune.function.gpsies.GpsiesTrack;
/**
- * XML handler for dealing with XML returned from gpsies.com
+ * XML handler for dealing with XML returned from the geonames api
*/
public class GetWikipediaXmlHandler extends DefaultHandler
{
catch (NumberFormatException nfe) {}
}
else if (inTagName.equals("wikipediaUrl")) {
- _track.setWebUrl(_value);
+ _track.setWebUrl(_value.replaceFirst("http://", "https://"));
}
super.endElement(inUri, inLocalName, inTagName);
}
/**
* Class to act as list model for the map source list
*/
-public class MapSourceListModel extends AbstractListModel
+public class MapSourceListModel extends AbstractListModel<String>
{
/**
* @see javax.swing.ListModel#getSize()
/**
* @see javax.swing.ListModel#getElementAt(int)
*/
- public Object getElementAt(int inIndex)
+ public String getElementAt(int inIndex)
{
if (inIndex < 0 || inIndex >= getSize()) return "";
return MapSourceLibrary.getSource(inIndex).getName();
private JTextField _nameField = null;
private JTextField _coordField = null;
private JButton _okButton = null;
- private JComboBox _altUnitsDropDown;
+ private JComboBox<String> _altUnitsDropDown;
/**
formatLabel.setHorizontalAlignment(SwingConstants.RIGHT);
grid.add(formatLabel);
final String[] altunits = {I18nManager.getText("units.metres"), I18nManager.getText("units.feet")};
- _altUnitsDropDown = new JComboBox(altunits);
+ _altUnitsDropDown = new JComboBox<String>(altunits);
grid.add(_altUnitsDropDown);
// Waypoint name
JLabel nameLabel = new JLabel(I18nManager.getText("dialog.pointnameedit.name"));
private SourceInfo _sourceInfo = null;
private TrackNameList _trackNameList = null;
private JDialog _dialog = null;
- private JList _trackList = null;
+ private JList<String> _trackList = null;
/**
* Constructor
}
names[i] = name + " (" + _trackNameList.getNumPointsInTrack(i) + ")";
}
- _trackList = new JList(names);
+ _trackList = new JList<String>(names);
_trackList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
mainPanel.add(new JScrollPane(_trackList), BorderLayout.CENTER);
// select all button
public class SetLanguage extends GenericFunction
{
private JDialog _dialog = null;
- private JComboBox _languageDropDown = null;
+ private JComboBox<String> _languageDropDown = null;
private JTextField _langFileBox = null;
private int _startIndex = 0;
"espa\u00F1ol", "fran\u00E7ais", "italiano", "magyar", "nederlands", "polski",
"portugu\u00EAs", "\u0440\u0443\u0441\u0441\u043a\u0438\u0439 (russian)", "\u4e2d\u6587 (chinese)", "\u65E5\u672C\u8A9E (japanese)",
"\uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean)", "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e",
- "afrikaans", "rom\u00E2n\u0103"
+ "afrikaans", "rom\u00E2n\u0103", "\u0443\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430 \u043c\u043e\u0432\u0430 (ukrainian)"
};
/** Associated language codes (must be in same order as names!) */
private static final String[] LANGUAGE_CODES = {"cz", "de", "en", "en_us", "es", "fr", "it", "hu",
- "nl", "pl", "pt", "ru", "zh", "ja", "ko", "de_ch", "tr", "af", "ro"
+ "nl", "pl", "pt", "ru", "zh", "ja", "ko", "de_ch", "tr", "af", "ro", "uk"
};
builtinPanel.setLayout(new BoxLayout(builtinPanel, BoxLayout.X_AXIS));
builtinPanel.add(new JLabel(I18nManager.getText("dialog.setlanguage.language") + " : "));
// Language dropdown
- _languageDropDown = new JComboBox(LANGUAGE_NAMES);
+ _languageDropDown = new JComboBox<String>(LANGUAGE_NAMES);
builtinPanel.add(_languageDropDown);
builtinPanel.add(Box.createHorizontalGlue());
JButton selectLangButton = new JButton(I18nManager.getText("button.select"));
public class SetMapBgFunction extends GenericFunction
{
private JDialog _dialog = null;
- private JList _list = null;
+ private JList<String> _list = null;
private MapSourceListModel _listModel = null;
private String _initialSource = null;
private JButton _okButton = null, _cancelButton = null;
dialogPanel.add(introLabel, BorderLayout.NORTH);
// list box
_listModel = new MapSourceListModel();
- _list = new JList(_listModel);
+ _list = new JList<String>(_listModel);
_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
dialogPanel.add(new JScrollPane(_list), BorderLayout.CENTER);
_list.addListSelectionListener(new ListSelectionListener() {
package tim.prune.function;
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
import javax.swing.JOptionPane;
+import javax.swing.JPanel;
import tim.prune.App;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.gui.BaseImageDefinitionPanel;
+import tim.prune.gui.DecimalNumberField;
+import tim.prune.gui.TerrainDefinitionPanel;
+import tim.prune.threedee.TerrainDefinition;
import tim.prune.threedee.ThreeDException;
import tim.prune.threedee.ThreeDWindow;
import tim.prune.threedee.WindowFactory;
+import tim.prune.tips.TipManager;
/**
* Class to show the 3d window
*/
public class ShowThreeDFunction extends GenericFunction
{
+ /** Dialog for input parameters */
+ private JDialog _dialog = null;
+ /** Field for altitude exaggeration value */
+ private DecimalNumberField _exaggField = null;
+ /** Component for defining the base image */
+ private BaseImageDefinitionPanel _baseImagePanel = null;
+ /** Component for defining the terrain */
+ private TerrainDefinitionPanel _terrainPanel = null;
+
/**
* Constructor
* @param inApp app object
I18nManager.getText("error.function.notavailable.title"), JOptionPane.WARNING_MESSAGE);
}
else
+ {
+ // See if the track has any altitudes at all - if not, show a tip to use SRTM
+ if (!_app.getTrackInfo().getTrack().hasAltitudeData()) {
+ _app.showTip(TipManager.Tip_UseSrtmFor3d);
+ }
+ // Show a dialog to get the parameters
+ if (_dialog == null)
+ {
+ _dialog = new JDialog(_app.getFrame(), I18nManager.getText(getNameKey()), true);
+ _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ _dialog.getContentPane().add(makeDialogComponents());
+ _dialog.pack();
+ }
+ final int exaggFactor = Config.getConfigInt(Config.KEY_HEIGHT_EXAGGERATION);
+ if (exaggFactor > 0) {
+ _exaggField.setValue(exaggFactor / 100.0);
+ }
+ _baseImagePanel.updateBaseImageDetails();
+ _dialog.setLocationRelativeTo(_app.getFrame());
+ _dialog.setVisible(true);
+ }
+ }
+
+ /**
+ * Make the dialog components to select the options
+ * @return JPanel holding the gui elements
+ */
+ private JPanel makeDialogComponents()
+ {
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BorderLayout(4, 4));
+
+ JPanel innerPanel = new JPanel();
+ innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS));
+ // Panel for altitude exaggeration
+ JPanel exaggPanel = new JPanel();
+ exaggPanel.setLayout(new FlowLayout());
+ exaggPanel.add(new JLabel(I18nManager.getText("dialog.3d.altitudefactor") + ": "));
+ _exaggField = new DecimalNumberField(); // don't allow negative numbers
+ _exaggField.setText("5.0");
+ exaggPanel.add(_exaggField);
+ innerPanel.add(exaggPanel);
+ innerPanel.add(Box.createVerticalStrut(4));
+
+ // Panel for terrain
+ _terrainPanel = new TerrainDefinitionPanel();
+ innerPanel.add(_terrainPanel);
+ mainPanel.add(innerPanel, BorderLayout.NORTH);
+ innerPanel.add(Box.createVerticalStrut(4));
+
+ // Panel for base image (null because we don't need callback)
+ _baseImagePanel = new BaseImageDefinitionPanel(null, _dialog, _app.getTrackInfo().getTrack());
+ innerPanel.add(_baseImagePanel);
+
+ // OK, Cancel buttons
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ JButton okButton = new JButton(I18nManager.getText("button.ok"));
+ okButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ _dialog.dispose();
+ new Thread(new Runnable() {
+ public void run() {
+ finish(); // needs to be in separate thread
+ }
+ }).start();
+ }
+ });
+ buttonPanel.add(okButton);
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(cancelButton);
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+ return mainPanel;
+ }
+
+ /**
+ * All parameters have been selected in the input dialog, now we can go to the 3d window
+ */
+ private void finish()
+ {
+ // Store exaggeration factor in config
+ Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_exaggField.getValue() * 100));
+ ThreeDWindow window = WindowFactory.getWindow(_parentFrame);
+ if (window != null)
{
try
{
- // Pass the track object and show the window
+ // Pass the parameters to use and show the window
window.setTrack(_app.getTrackInfo().getTrack());
+ window.setAltitudeFactor(_exaggField.getValue());
+ // Also pass the base image parameters from input dialog
+ window.setBaseImageParameters(_baseImagePanel.getImageDefinition());
+ window.setTerrainParameters(new TerrainDefinition(_terrainPanel.getUseTerrain(), _terrainPanel.getGridSize()));
window.show();
}
catch (ThreeDException e)
{
// which exists, so try browsers in turn
String[] browsersToTry = {"firefox", "iceweasel", "konqueror", "opera", "epiphany",
- "mozilla", "safari", "google-chrome", "lynx"};
+ "mozilla", "chromium", "midori", "safari", "lynx"};
String browserFound = null;
- for (int i=0; i<browsersToTry.length && browserFound == null; i++)
+ for (String browser : browsersToTry)
{
- if (commandExists(browsersToTry[i]))
- browserFound = browsersToTry[i];
+ if (commandExists(browser))
+ {
+ browserFound = browser;
+ break;
+ }
}
if (browserFound != null) {
_browserCommand = new String[] {browserFound, null};
if (totalDeleted > 0)
{
// Show confirmation message
- JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.diskcache.deleted1")
- + " " + totalDeleted + " " + I18nManager.getText("dialog.diskcache.deleted2"),
+ JOptionPane.showMessageDialog(_dialog, I18nManager.getTextWithNumber("dialog.diskcache.deleted", totalDeleted),
I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
// reload model
_cards.first(_cardPanel);
private JButton _okButton = null;
private CompressionAlgorithm[] _algorithms = null;
private SummaryLabel _summaryLabel = null;
+ /** flag to remember whether the automatic deletion has been set to always */
+ private boolean _automaticallyDelete = false;
/**
UpdateMessageBroker.informSubscribers();
_dialog.dispose();
// Show confirmation dialog with OK button (not status bar message)
- if (numMarked > 0) {
- JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.compress.confirm1")
- + " " + numMarked + " " + I18nManager.getText("dialog.compress.confirm2"),
- I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+ if (numMarked > 0)
+ {
+ // Allow calling of delete function with one click
+ final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
+ I18nManager.getText("button.always")};
+ int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
+ JOptionPane.showOptionDialog(_parentFrame,
+ I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked),
+ I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
+ JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
+ if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
+ if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
+ {
+ new Thread(new Runnable() {
+ public void run() {
+ _app.finishCompressTrack();
+ }
+ }).start();
+ }
}
- else {
+ else
+ {
JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.compress.confirmnone"),
I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
}
private double _minLat = 0.0, _maxLat = 0.0;
/** Minimum and maximum longitude values of rectangle */
private double _minLon = 0.0, _maxLon = 0.0;
+ /** flag to remember whether the automatic deletion has been set to always */
+ private boolean _automaticallyDelete = false;
/**
super(inApp);
}
+ /** @return name key */
+ public String getNameKey() {
+ return "menu.track.markrectangle";
+ }
+
/**
* Set the coordinates of the rectangle
* @param inLon1 first longitude value
// Inform subscribers to update display
UpdateMessageBroker.informSubscribers();
// Confirm message showing how many marked
- if (numMarked > 0) {
- JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.compress.confirm1")
- + " " + numMarked + " " + I18nManager.getText("dialog.compress.confirm2"),
- I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+ if (numMarked > 0)
+ {
+ // Allow calling of delete function with one click
+ final String[] buttonTexts = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
+ I18nManager.getText("button.always")};
+ int answer = _automaticallyDelete ? JOptionPane.YES_OPTION :
+ JOptionPane.showOptionDialog(_parentFrame,
+ I18nManager.getTextWithNumber("dialog.compress.confirm", numMarked),
+ I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION,
+ JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]);
+ if (answer == JOptionPane.CANCEL_OPTION) {_automaticallyDelete = true;} // "always" is third option
+ if (_automaticallyDelete || answer == JOptionPane.YES_OPTION)
+ {
+ new Thread(new Runnable() {
+ public void run() {
+ _app.finishCompressTrack();
+ }
+ }).start();
+ }
}
}
-
- /** @return name key */
- public String getNameKey() {
- return "menu.track.markrectangle";
- }
}
import tim.prune.gui.DecimalNumberField;
import tim.prune.gui.DisplayUtils;
import tim.prune.gui.GuiGridLayout;
+import tim.prune.tips.TipManager;
/**
* Class to calculate and show the results of estimating (hike) time for the current range
}
if (_dialog == null)
{
- // TODO: Check whether params are at default, show tip message if unaltered?
+ // First time in, check whether params are at default, show tip message if unaltered
+ showTip();
_dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
_dialog.setLocationRelativeTo(_parentFrame);
_dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
}
_dialog.dispose();
}
+
+ /**
+ * Show a tip to use the learn function, if appropriate
+ */
+ private void showTip()
+ {
+ EstimationParameters currParams = new EstimationParameters(
+ Config.getConfigString(Config.KEY_ESTIMATION_PARAMS));
+ if (currParams.sameAsDefaults()) {
+ _app.showTip(TipManager.Tip_LearnTimeParams);
+ }
+ }
}
_parseFailed = false;
}
+ /**
+ * @return true if this set of parameters is the same as the default set
+ */
+ public boolean sameAsDefaults()
+ {
+ EstimationParameters defaultParams = new EstimationParameters();
+ return _flatMins == defaultParams._flatMins
+ && _gentleClimbMins == defaultParams._gentleClimbMins
+ && _steepClimbMins == defaultParams._steepClimbMins
+ && _gentleDescentMins == defaultParams._gentleDescentMins
+ && _steepDescentMins == defaultParams._steepDescentMins;
+ }
+
/**
* Populate the values from the config, which means all values are metric
* @param inString semicolon-separated string of five parameters
int startIndex = i * sampleSize;
RangeStats stats = getRangeStats(track, startIndex, startIndex + sampleSize, prevStartIndex);
if (stats != null && stats.getMovingDistanceKilometres() > 1.0
- && !stats.getTimestampsIncomplete()
+ && !stats.getTimestampsIncomplete() && !stats.getTimestampsOutOfSequence()
&& stats.getTotalDurationInSeconds() > 100
&& stats.getStartIndex() > prevStartIndex)
{
--- /dev/null
+package tim.prune.function.sew;
+
+import java.util.Comparator;
+
+/**
+ * Class to sort the candidates for segment splitting
+ */
+public class CandidateSorter implements Comparator<SplitPoint>
+{
+ /**
+ * Sort the objects by distance (greatest first)
+ */
+ public int compare(SplitPoint inFirst, SplitPoint inSecond)
+ {
+ if (inFirst == null) return 1;
+ if (inSecond == null) return -1;
+ // First, sort by distance
+ final double dist1 = inFirst.getDistanceToPrevPoint();
+ final double dist2 = inSecond.getDistanceToPrevPoint();
+ if (dist1 > dist2) {
+ return -1;
+ }
+ if (dist1 < dist2) {
+ return 1;
+ }
+ // If the distances are identical, then just sort by point index
+ return inFirst.getPointIndex() - inSecond.getPointIndex();
+ }
+}
--- /dev/null
+package tim.prune.function.sew;
+
+import tim.prune.data.Coordinate;
+import tim.prune.data.DataPoint;
+
+/**
+ * Class to represent one end of a segment, including the
+ * coordinates and the other end of the segment
+ */
+public class SegmentEnd implements Comparable<SegmentEnd>
+{
+ private SegmentEnd _otherEnd = null;
+ private Coordinate _longitude = null;
+ private Coordinate _latitude = null;
+ private int _pointIndex = 0;
+ private boolean _active = true;
+
+
+ /**
+ * Constructor
+ * @param inPoint data point
+ * @param inIndex point index within track
+ */
+ public SegmentEnd(DataPoint inPoint, int inIndex)
+ {
+ _longitude = inPoint.getLongitude();
+ _latitude = inPoint.getLatitude();
+ _pointIndex = inIndex;
+ _active = true;
+ }
+
+ /**
+ * @param inOther other end of the segment
+ */
+ public void setOtherEnd(SegmentEnd inOther)
+ {
+ _otherEnd = inOther;
+ }
+
+ /**
+ * @return other end
+ */
+ public SegmentEnd getOtherEnd()
+ {
+ return _otherEnd;
+ }
+
+ /**
+ * @return true if this is the start of the segment
+ */
+ public boolean isStart()
+ {
+ return _otherEnd == null || _otherEnd._pointIndex > _pointIndex;
+ }
+
+ /** @return point index */
+ public int getPointIndex() {
+ return _pointIndex;
+ }
+
+ /** @return point index of other end */
+ public int getOtherPointIndex()
+ {
+ return _otherEnd == null ? _pointIndex : _otherEnd._pointIndex;
+ }
+
+ /** @return get the earlier of the two point indices */
+ public int getEarlierIndex() {
+ return isStart() ? _pointIndex : _otherEnd._pointIndex;
+ }
+
+ /** @return get the later of the two point indices */
+ public int getLaterIndex() {
+ return isStart() ? _otherEnd._pointIndex : _pointIndex;
+ }
+
+ /**
+ * @return earlier end of this segment
+ */
+ public SegmentEnd getEarlierEnd() {
+ return isStart() ? this : _otherEnd;
+ }
+
+ /**
+ * @return later end of this segment
+ */
+ public SegmentEnd getLaterEnd() {
+ return isStart() ? _otherEnd : this;
+ }
+
+ /**
+ * Reverse this segment, by swapping the point indices of the start and end
+ * isStart() will thereby also be reversed for both ends
+ */
+ public void reverseSegment()
+ {
+ if (_otherEnd != null)
+ {
+ int pointIndex = _pointIndex;
+ _pointIndex = _otherEnd._pointIndex;
+ _otherEnd._pointIndex = pointIndex;
+ }
+ }
+
+ /**
+ * @return true if this node is still active
+ */
+ public boolean isActive() {
+ return _active;
+ }
+
+ /**
+ * Deactive this node, don't use it any more (it's already been merged)
+ */
+ public void deactivate() {
+ _active = false;
+ }
+
+ /**
+ * @param inOther other segment end
+ * @return true if the coordinates are identical
+ */
+ public boolean atSamePointAs(SegmentEnd inOther)
+ {
+ return inOther != null && _latitude.equals(inOther._latitude) && _longitude.equals(inOther._longitude);
+ }
+
+ /**
+ * Compare two objects for sorting
+ */
+ public int compareTo(SegmentEnd o)
+ {
+ if (o == null) return -1;
+ // First, sort by latitude
+ if (!_latitude.equals(o._latitude)) {
+ return (_latitude.getDouble() < o._latitude.getDouble() ? -1 : 1);
+ }
+ // Latitudes same, so sort by longitude
+ if (!_longitude.equals(o._longitude)) {
+ return (_longitude.getDouble() < o._longitude.getDouble() ? -1 : 1);
+ }
+ // Points are identical so just sort by index
+ return _pointIndex - o._pointIndex;
+ }
+
+ /**
+ * Adjust the point index as a result of a cut/move operation on the track
+ * @param inSegmentStart index of start of segment to be moved
+ * @param inSegmentEnd index of end of segment to be moved
+ * @param inMoveTo index of point before which the segment should be moved
+ */
+ public void adjustPointIndex(int inSegmentStart, int inSegmentEnd, int inMoveTo)
+ {
+ final int segmentSize = inSegmentEnd - inSegmentStart + 1; // number of points moved
+ final boolean forwardsMove = inMoveTo > inSegmentEnd;
+ // Min and max indices of affected points (apart from segment to be moved)
+ final int minIndex = forwardsMove ? inSegmentEnd + 1 : inMoveTo;
+ final int maxIndex = forwardsMove ? inMoveTo - 1 : inSegmentStart - 1;
+ if (_pointIndex >= minIndex && _pointIndex <= maxIndex)
+ {
+ // final int origIndex = _pointIndex;
+ if (forwardsMove) {
+ _pointIndex -= segmentSize; // segment moved forwards, point indices reduced
+ }
+ else {
+ _pointIndex += segmentSize; // segment moved backwards, point indices shifted forwards
+ }
+ // System.out.println(" Need to adjust index: " + origIndex + " -> " + _pointIndex);
+ }
+ else if (_pointIndex == inSegmentStart)
+ {
+ // final int origIndex = _pointIndex;
+ if (forwardsMove) {
+ _pointIndex = inMoveTo - segmentSize;
+ }
+ else
+ {
+ // Point index moves to moveTo
+ _pointIndex = inMoveTo;
+ }
+ // System.out.println(" Need to adjust movedseg: " + origIndex + " -> " + _pointIndex);
+ }
+ else if (_pointIndex == inSegmentEnd)
+ {
+ // final int origEndIndex = _otherEnd._pointIndex;
+ if (forwardsMove) {
+ _pointIndex = inMoveTo - 1;
+ }
+ else
+ {
+ // Point index moves to moveTo
+ _pointIndex = inMoveTo + inSegmentEnd - inSegmentStart;
+ }
+ // System.out.println(" Need to adjust movedseg: " + origEndIndex + " -> " + _pointIndex);
+ }
+ }
+}
--- /dev/null
+package tim.prune.function.sew;
+
+import java.util.TreeSet;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Track;
+import tim.prune.function.Cancellable;
+import tim.prune.gui.GenericProgressDialog;
+import tim.prune.undo.UndoException;
+import tim.prune.undo.UndoSewSegments;
+
+/**
+ * Function to sew the track segments together if possible,
+ * reversing and moving as required
+ */
+public class SewTrackSegmentsFunction extends GenericFunction implements Runnable, Cancellable
+{
+ /** Set of sorted segment endpoints */
+ private TreeSet<SegmentEnd> _nodes = null;
+ /** Cancel flag */
+ private boolean _cancelled = false;
+
+
+ /** Constructor */
+ public SewTrackSegmentsFunction(App inApp) {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.sewsegments";
+ }
+
+ /**
+ * Execute the function
+ */
+ public void begin()
+ {
+ // Run in separate thread, with progress bar
+ new Thread(this).start();
+ }
+
+ /**
+ * Run the function in a separate thread
+ */
+ public void run()
+ {
+ // Make a progress bar
+ GenericProgressDialog progressDialog = new GenericProgressDialog(getNameKey(), null, _parentFrame, this);
+ progressDialog.show();
+ // Make an undo object to store the current points and sequence
+ UndoSewSegments undo = new UndoSewSegments(_app.getTrackInfo().getTrack());
+
+ // Make list of all the segment ends
+ _nodes = buildNodeList(_app.getTrackInfo().getTrack());
+ final int numNodes = (_nodes == null ? 0 : _nodes.size());
+ if (numNodes < 4)
+ {
+ System.out.println("Can't do anything with this, not enough segments");
+ progressDialog.close();
+ }
+ else
+ {
+ progressDialog.showProgress(10, 100); // Say 10% for building the nodes
+
+ // Disable messaging because we're probably doing a lot of reverses and moves
+ UpdateMessageBroker.enableMessaging(false);
+ // Set now contains all pairs of segment ends, ends at the same location are adjacent
+ // Now we're just interested in pairs of nodes, not three or more at the same location
+ SegmentEnd firstNode = null, secondNode = null;
+ int numJoins = 0, currNode = 0;
+ for (SegmentEnd node : _nodes)
+ {
+ if (!node.isActive()) {continue;}
+ if (firstNode == null)
+ {
+ firstNode = node;
+ }
+ else if (secondNode == null)
+ {
+ if (node.atSamePointAs(firstNode)) {
+ secondNode = node;
+ }
+ else {
+ firstNode = node;
+ }
+ }
+ else if (node.atSamePointAs(secondNode))
+ {
+ // Found three colocated nodes, not interested
+ firstNode = secondNode = null;
+ }
+ else
+ {
+ // Found a pair
+ joinSegments(firstNode, secondNode);
+ numJoins++;
+ firstNode = node; secondNode = null;
+ }
+ if (_cancelled) {break;}
+ final double fractionDone = 1.0 * currNode / numNodes;
+ progressDialog.showProgress(10 + (int) (fractionDone * 80), 100);
+ currNode++;
+ }
+ if (firstNode != null && secondNode != null)
+ {
+ joinSegments(firstNode, secondNode);
+ numJoins++;
+ }
+
+ progressDialog.showProgress(90, 100); // Say 90%, only duplicate point deletion left
+
+ // Delete the duplicate points
+ final int numDeleted = _cancelled ? 0 : deleteSegmentStartPoints(_app.getTrackInfo().getTrack());
+
+ progressDialog.close();
+ // Enable the messaging again
+ UpdateMessageBroker.enableMessaging(true);
+ if (_cancelled) // TODO: Also revert if any of the operations failed
+ {
+ // try to restore using undo object
+ try {
+ undo.performUndo(_app.getTrackInfo());
+ }
+ catch (UndoException ue) {
+ _app.showErrorMessage("oops", "CANNOT UNDO");
+ }
+ }
+ else if (numJoins > 0 || numDeleted > 0)
+ {
+ // Give Undo object back to App to confirm
+ final String confirmMessage = (numJoins > 0 ? I18nManager.getTextWithNumber("confirm.sewsegments", numJoins)
+ : "" + numDeleted + " " + I18nManager.getText("confirm.deletepoint.multi"));
+ _app.completeFunction(undo, confirmMessage);
+ UpdateMessageBroker.informSubscribers();
+ }
+ else
+ {
+ // Nothing done
+ _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getTextWithNumber("error.sewsegments.nothingdone", numNodes/2));
+ }
+ }
+ }
+
+ /**
+ * Build a sorted list of all the segment start points and end points
+ * Creates a TreeSet containing two SegmentEnd objects for each segment
+ * @param inTrack track object
+ * @return sorted list of segment ends
+ */
+ private static TreeSet<SegmentEnd> buildNodeList(Track inTrack)
+ {
+ TreeSet<SegmentEnd> nodes = new TreeSet<SegmentEnd>();
+ final int numPoints = inTrack.getNumPoints();
+ DataPoint prevTrackPoint = null;
+ int prevTrackPointIndex = -1;
+ SegmentEnd segmentStart = null;
+ for (int i=0; i<numPoints; i++)
+ {
+ DataPoint point = inTrack.getPoint(i);
+ if (!point.isWaypoint() && !point.hasMedia())
+ {
+ if (point.getSegmentStart())
+ {
+ // Start of new segment - does previous one need to be saved?
+ if (segmentStart != null && prevTrackPointIndex > 0 && prevTrackPointIndex != segmentStart.getPointIndex())
+ {
+ // Finish previous segment and store in list
+ SegmentEnd segmentEnd = new SegmentEnd(prevTrackPoint, prevTrackPointIndex);
+ segmentStart.setOtherEnd(segmentEnd);
+ segmentEnd.setOtherEnd(segmentStart);
+ // Don't add closed loops
+ if (!segmentStart.atSamePointAs(segmentEnd))
+ {
+ nodes.add(segmentStart);
+ nodes.add(segmentEnd);
+ }
+ }
+ // Remember segment start
+ segmentStart = new SegmentEnd(point, i);
+ }
+ prevTrackPoint = point;
+ prevTrackPointIndex = i;
+ }
+ }
+ // Probably need to deal with segmentStart and prevTrackPoint, prevTrackPointIndex
+ if (segmentStart != null && prevTrackPointIndex > 0 && prevTrackPointIndex != segmentStart.getPointIndex())
+ {
+ // Finish last segment and store in list
+ SegmentEnd segmentEnd = new SegmentEnd(prevTrackPoint, prevTrackPointIndex);
+ segmentStart.setOtherEnd(segmentEnd);
+ segmentEnd.setOtherEnd(segmentStart);
+ // Don't add closed loops
+ if (!segmentStart.atSamePointAs(segmentEnd))
+ {
+ nodes.add(segmentStart);
+ nodes.add(segmentEnd);
+ }
+ }
+ return nodes;
+ }
+
+ /**
+ * Join the two segments together represented by the given nodes
+ * @param inFirstNode first node (order doesn't matter)
+ * @param inSecondNode other node
+ */
+ private void joinSegments(SegmentEnd inFirstNode, SegmentEnd inSecondNode)
+ {
+ final Track track = _app.getTrackInfo().getTrack();
+ // System.out.println("Join: (" + inFirstNode.getPointIndex() + "-" + inFirstNode.getOtherPointIndex() + ") with ("
+ // + inSecondNode.getPointIndex() + "-" + inSecondNode.getOtherPointIndex() + ")");
+ // System.out.println(" : " + (inFirstNode.isStart() ? "start" : "end") + " to " + (inSecondNode.isStart() ? "start" : "end"));
+ final boolean moveSecondBeforeFirst = inFirstNode.isStart();
+ if (inFirstNode.isStart() == inSecondNode.isStart())
+ {
+ if (track.reverseRange(inSecondNode.getEarlierIndex(), inSecondNode.getLaterIndex()))
+ {
+ inSecondNode.reverseSegment();
+ // System.out.println(" : Reverse segment: " + inSecondNode.getEarlierIndex() + " - " + inSecondNode.getLaterIndex());
+ }
+ else {
+ System.err.println("Oops, reverse range didn't work");
+ // TODO: Abort?
+ }
+ }
+ if (moveSecondBeforeFirst)
+ {
+ if ((inSecondNode.getLaterIndex()+1) != inFirstNode.getPointIndex())
+ {
+ // System.out.println(" : Move second segment before first");
+ cutAndMoveSegment(inSecondNode.getEarlierIndex(), inSecondNode.getLaterIndex(), inFirstNode.getPointIndex());
+ }
+ }
+ else if ((inFirstNode.getLaterIndex()+1) != inSecondNode.getPointIndex())
+ {
+ // System.out.println(" : Move first segment before second (because " + (inFirstNode.getLaterIndex()+1) + " isn't " + inSecondNode.getPointIndex() + ")");
+ cutAndMoveSegment(inFirstNode.getEarlierIndex(), inFirstNode.getLaterIndex(), inSecondNode.getPointIndex());
+ }
+ // Now merge the SegmentEnds so that they're not split up again
+ if (inSecondNode.getEarlierIndex() == (inFirstNode.getLaterIndex()+1)) {
+ // System.out.println("second node is now directly after the first node");
+ }
+ else if (inFirstNode.getEarlierIndex() == (inSecondNode.getLaterIndex()+1)) {
+ //System.out.println("first node is now directly after the second node");
+ }
+ else {
+ System.err.println("Why aren't the segments directly consecutive after the join?");
+ }
+ // Find the earliest and latest ends of these two segments
+ SegmentEnd earlierSegmentEnd = (inFirstNode.getEarlierIndex() < inSecondNode.getEarlierIndex() ? inFirstNode : inSecondNode).getEarlierEnd();
+ SegmentEnd laterSegmentEnd = (inFirstNode.getLaterIndex() > inSecondNode.getLaterIndex() ? inFirstNode : inSecondNode).getLaterEnd();
+ // Get rid of the inner two segment ends, join the earliest and latest together
+ earlierSegmentEnd.getOtherEnd().deactivate();
+ laterSegmentEnd.getOtherEnd().deactivate();
+ earlierSegmentEnd.setOtherEnd(laterSegmentEnd);
+ laterSegmentEnd.setOtherEnd(earlierSegmentEnd);
+ }
+
+ /**
+ * Cut and move the segment to a different position
+ * @param inSegmentStart start index of segment
+ * @param inSegmentEnd end index of segment
+ * @param inMoveToPos index before which the segment should be moved
+ */
+ private void cutAndMoveSegment(int inSegmentStart, int inSegmentEnd, int inMoveToPos)
+ {
+ if (!_app.getTrackInfo().getTrack().cutAndMoveSection(inSegmentStart, inSegmentEnd, inMoveToPos))
+ {
+ System.err.println(" Oops, cut and move didn't work");
+ // TODO: Throw exception? Return false?
+ }
+ else
+ {
+ // Loop over each node to inform it of the index changes
+ for (SegmentEnd node : _nodes) {
+ node.adjustPointIndex(inSegmentStart, inSegmentEnd, inMoveToPos);
+ }
+ }
+ }
+
+ /**
+ * The final step of the sewing, removing the duplicate points at the start of each segment
+ * @param inTrack track object
+ * @return number of points deleted
+ */
+ private static int deleteSegmentStartPoints(Track inTrack)
+ {
+ final int numPoints = inTrack.getNumPoints();
+ boolean[] deleteFlags = new boolean[numPoints];
+ // Loop over points in track, setting delete flags
+ int numToDelete = 0;
+ DataPoint prevPoint = null;
+ for (int i=0; i<numPoints; i++)
+ {
+ DataPoint point = inTrack.getPoint(i);
+ if (!point.isWaypoint())
+ {
+ if (prevPoint != null && point.getSegmentStart() && point.isDuplicate(prevPoint))
+ {
+ deleteFlags[i] = true;
+ numToDelete++;
+ }
+ prevPoint = point;
+ }
+ }
+ // Make new datapoint array of the right size
+ DataPoint[] pointCopies = new DataPoint[numPoints - numToDelete];
+ // Loop over points again, keeping the ones we want
+ int copyIndex = 0;
+ for (int i=0; i<numPoints; i++)
+ {
+ if (!deleteFlags[i]) {
+ pointCopies[copyIndex] = inTrack.getPoint(i);
+ copyIndex++;
+ }
+ }
+ // Finally, replace the copied points in the track
+ inTrack.replaceContents(pointCopies);
+ return numToDelete;
+ }
+
+ /** Function cancelled by progress dialog */
+ public void cancel() {
+ _cancelled = true;
+ }
+}
--- /dev/null
+package tim.prune.function.sew;
+
+import tim.prune.data.Coordinate;
+import tim.prune.data.DataPoint;
+
+/**
+ * Class to represent a possible split point, including
+ * the distances to the previous and next points
+ */
+public class SplitPoint implements Comparable<SplitPoint>
+{
+ private SplitPoint _nextPoint = null;
+ private Coordinate _longitude = null;
+ private Coordinate _latitude = null;
+ private int _pointIndex = 0;
+ private double _distToPrevPoint = 0.0;
+ private double _distToNextPoint = -1.0;
+
+
+ /**
+ * Constructor
+ * @param inPoint data point
+ * @param inIndex point index within track
+ */
+ public SplitPoint(DataPoint inPoint, int inIndex)
+ {
+ _longitude = inPoint.getLongitude();
+ _latitude = inPoint.getLatitude();
+ _pointIndex = inIndex;
+ }
+
+ /**
+ * @param inDist distance to previous track point
+ */
+ public void setDistanceToPrevPoint(double inDist) {
+ _distToPrevPoint = inDist;
+ }
+ /** @return distance to previous track point */
+ public double getDistanceToPrevPoint() {
+ return _distToPrevPoint;
+ }
+
+ /**
+ * @param inDist distance to next track point, or -1.0
+ */
+ public void setDistanceToNextPoint(double inDist) {
+ _distToNextPoint = inDist;
+ }
+ /** @return distance to next track point */
+ public double getDistanceToNextPoint() {
+ return _distToNextPoint;
+ }
+ /** @return true if this is closer to the next point than to the previous one */
+ public boolean closerToNext() {
+ return _distToNextPoint > 0.0 && _distToNextPoint < _distToPrevPoint;
+ }
+
+ /** @return point index */
+ public int getPointIndex() {
+ return _pointIndex;
+ }
+
+ /**
+ * @param inOther the next point
+ */
+ public void setNextPoint(SplitPoint inOther) {
+ _nextPoint = inOther;
+ }
+
+ /** @return the next point, or null */
+ public SplitPoint getNextPoint() {
+ return _nextPoint;
+ }
+
+ /**
+ * @param inOther other segment end
+ * @return true if the coordinates are identical
+ */
+ public boolean atSamePointAs(SplitPoint inOther)
+ {
+ return inOther != null && _latitude.equals(inOther._latitude) && _longitude.equals(inOther._longitude);
+ }
+
+ /**
+ * Compare two objects for sorting
+ */
+ public int compareTo(SplitPoint o)
+ {
+ if (o == null) return -1;
+ // First, sort by latitude
+ if (!_latitude.equals(o._latitude)) {
+ return (_latitude.getDouble() < o._latitude.getDouble() ? -1 : 1);
+ }
+ // Latitudes same, so sort by longitude
+ if (!_longitude.equals(o._longitude)) {
+ return (_longitude.getDouble() < o._longitude.getDouble() ? -1 : 1);
+ }
+ // Points are identical so just sort by index
+ return _pointIndex - o._pointIndex;
+ }
+}
--- /dev/null
+package tim.prune.function.sew;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Distance;
+import tim.prune.data.Field;
+import tim.prune.data.Unit;
+import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.WholeNumberField;
+import tim.prune.undo.UndoSplitSegments;
+
+/**
+ * Function to split a track into segments using
+ * either a distance limit or a time limit
+ */
+public class SplitSegmentsFunction extends GenericFunction
+{
+ /** Dialog */
+ private JDialog _dialog = null;
+ /** Radio buttons for splitting by distance and time */
+ private JRadioButton _distLimitRadio = null, _timeLimitRadio = null;
+ /** Dropdown for selecting distance units */
+ private JComboBox<String> _distUnitsDropdown = null;
+ /** Text field for entering distance */
+ private WholeNumberField _distanceField = null;
+ /** Text fields for entering distance */
+ private WholeNumberField _limitHourField = null, _limitMinField = null;
+ /** Ok button */
+ private JButton _okButton = null;
+
+
+ /**
+ * React to item changes and key presses
+ */
+ private abstract class ChangeListener extends KeyAdapter implements ItemListener
+ {
+ /** Method to be implemented */
+ public abstract void optionsChanged();
+
+ /** Item changed in ItemListener */
+ public void itemStateChanged(ItemEvent arg0) {
+ optionsChanged();
+ }
+
+ /** Key released in KeyListener */
+ public void keyReleased(KeyEvent arg0) {
+ optionsChanged();
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public SplitSegmentsFunction(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return name key
+ */
+ public String getNameKey() {
+ return "function.splitsegments";
+ }
+
+ /**
+ * Begin the function
+ */
+ public void begin()
+ {
+ if (_dialog == null)
+ {
+ _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+ _dialog.setLocationRelativeTo(_parentFrame);
+ _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ _dialog.getContentPane().add(makeDialogComponents());
+ _dialog.pack();
+ }
+ enableOkButton();
+ // TODO: Maybe set distance units according to current Config setting?
+ final boolean hasTimestamps = _app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP);
+ _timeLimitRadio.setEnabled(hasTimestamps);
+ _dialog.setVisible(true);
+ }
+
+ /**
+ * Create dialog components
+ * @return Panel containing all gui elements in dialog
+ */
+ private Component makeDialogComponents()
+ {
+ JPanel dialogPanel = new JPanel();
+ dialogPanel.setLayout(new BorderLayout(5, 5));
+
+ // Make radio buttons for three different options
+ _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit") + ": ");
+ _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + ": ");
+ ButtonGroup radioGroup = new ButtonGroup();
+ radioGroup.add(_distLimitRadio);
+ radioGroup.add(_timeLimitRadio);
+
+ // central panel for limits
+ JPanel limitsPanel = new JPanel();
+ limitsPanel.setLayout(new BoxLayout(limitsPanel, BoxLayout.Y_AXIS));
+ limitsPanel.add(Box.createVerticalStrut(8));
+ ChangeListener optionsChangedListener = new ChangeListener() {
+ public void optionsChanged() {
+ enableOkButton();
+ }
+ };
+ // distance limits
+ JPanel distLimitPanel = new JPanel();
+ distLimitPanel.setLayout(new FlowLayout());
+ _distLimitRadio.setSelected(true);
+ _distLimitRadio.addItemListener(optionsChangedListener);
+ distLimitPanel.add(_distLimitRadio);
+ _distanceField = new WholeNumberField(3);
+ _distanceField.addKeyListener(optionsChangedListener);
+ distLimitPanel.add(_distanceField);
+ String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"),
+ I18nManager.getText("units.miles")};
+ _distUnitsDropdown = new JComboBox<String>(distUnitsOptions);
+ _distUnitsDropdown.addItemListener(optionsChangedListener);
+ distLimitPanel.add(_distUnitsDropdown);
+ distLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ limitsPanel.add(distLimitPanel);
+
+ // time limit panel
+ JPanel timeLimitPanel = new JPanel();
+ timeLimitPanel.setLayout(new FlowLayout());
+ _timeLimitRadio.addItemListener(optionsChangedListener);
+ timeLimitPanel.add(_timeLimitRadio);
+ _limitHourField = new WholeNumberField(2);
+ _limitHourField.addKeyListener(optionsChangedListener);
+ timeLimitPanel.add(_limitHourField);
+ timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.hours")));
+ _limitMinField = new WholeNumberField(3);
+ _limitMinField.addKeyListener(optionsChangedListener);
+ timeLimitPanel.add(_limitMinField);
+ timeLimitPanel.add(new JLabel(I18nManager.getText("units.minutes")));
+ timeLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ limitsPanel.add(timeLimitPanel);
+
+ dialogPanel.add(limitsPanel, BorderLayout.NORTH);
+
+ // button panel at bottom
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ // OK button
+ _okButton = new JButton(I18nManager.getText("button.ok"));
+ _okButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ performSplit();
+ }
+ });
+ buttonPanel.add(_okButton);
+ // Cancel button
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _dialog.dispose();
+ }
+ });
+ cancelButton.addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent inE) {
+ if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
+ }
+ });
+ buttonPanel.add(cancelButton);
+ dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+ return dialogPanel;
+ }
+
+ /**
+ * Enable or disable the OK button according to the inputs
+ */
+ private void enableOkButton()
+ {
+ boolean enabled = false;
+ if (_distLimitRadio.isSelected()) {
+ enabled = _distanceField.getValue() > 0;
+ }
+ else if (_timeLimitRadio.isSelected()) {
+ enabled = _limitHourField.getValue() > 0 || _limitMinField.getValue() > 0;
+ }
+ _okButton.setEnabled(enabled);
+
+ // Also enable/disable the other fields
+ _distanceField.setEnabled(_distLimitRadio.isSelected());
+ _distUnitsDropdown.setEnabled(_distLimitRadio.isSelected());
+ _limitHourField.setEnabled(_timeLimitRadio.isSelected());
+ _limitMinField.setEnabled(_timeLimitRadio.isSelected());
+ }
+
+ /**
+ * The dialog has been completed and OK pressed, so do the split
+ */
+ private void performSplit()
+ {
+ // Split either by distance or time
+ boolean checkTimeLimit = _timeLimitRadio.isSelected()
+ && (_limitHourField.getValue() > 0 || _limitMinField.getValue() > 0);
+ int timeLimitSeconds = 0;
+ if (checkTimeLimit)
+ {
+ timeLimitSeconds = _limitHourField.getValue() * 60 * 60
+ + _limitMinField.getValue() * 60;
+ if (timeLimitSeconds <= 0) {checkTimeLimit = false;}
+ }
+ double distLimitRadians = 0.0;
+ final boolean checkDistLimit = _distLimitRadio.isSelected()
+ && _distanceField.getValue() > 0;
+ if (checkDistLimit)
+ {
+ final Unit[] distUnits = {UnitSetLibrary.UNITS_KILOMETRES,
+ UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_MILES};
+ Unit distUnit = distUnits[_distUnitsDropdown.getSelectedIndex()];
+ distLimitRadians = Distance.convertDistanceToRadians(_distanceField.getValue(), distUnit);
+ }
+ if (!checkTimeLimit && !checkDistLimit) {
+ return; // neither option selected
+ }
+
+ // Make undo object
+ UndoSplitSegments undo = new UndoSplitSegments(_app.getTrackInfo().getTrack());
+ final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
+ DataPoint currPoint = null, prevPoint = null;
+ int numSplitsMade = 0;
+
+ // Now actually do it, looping through the points in the track
+ for (int i=0; i<numPoints; i++)
+ {
+ currPoint = _app.getTrackInfo().getTrack().getPoint(i);
+ if (!currPoint.isWaypoint())
+ {
+ boolean splitHere = (prevPoint != null)
+ && ((checkDistLimit && DataPoint.calculateRadiansBetween(prevPoint, currPoint) > distLimitRadians)
+ || (checkTimeLimit && currPoint.hasTimestamp() && prevPoint.hasTimestamp()
+ && currPoint.getTimestamp().getSecondsSince(prevPoint.getTimestamp()) > timeLimitSeconds));
+ if (splitHere && !currPoint.getSegmentStart())
+ {
+ currPoint.setSegmentStart(true);
+ numSplitsMade++;
+ }
+ prevPoint = currPoint;
+ }
+ }
+
+ if (numSplitsMade > 0)
+ {
+ _app.completeFunction(undo, I18nManager.getTextWithNumber("confirm.splitsegments", numSplitsMade));
+ UpdateMessageBroker.informSubscribers();
+ _dialog.dispose();
+ }
+ else
+ {
+ // Complain that no split was made
+ JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.tracksplit.nosplit"),
+ I18nManager.getText("error.function.noop.title"), JOptionPane.WARNING_MESSAGE);
+ }
+ }
+}
--- /dev/null
+package tim.prune.function.srtm;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+
+import javax.swing.JOptionPane;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.GpsPrune;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.data.DoubleRange;
+import tim.prune.gui.ProgressDialog;
+
+/**
+ * Class to provide a download function for the Space Shuttle's SRTM data files.
+ * HGT files are downloaded into memory via HTTP and stored in the map cache.
+ */
+public class DownloadSrtmFunction extends GenericFunction implements Runnable
+{
+ /** Progress dialog */
+ private ProgressDialog _progress = null;
+ /** Flag to check whether this function is currently running or not */
+ private boolean _running = false;
+
+
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public DownloadSrtmFunction(App inApp) {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.downloadsrtm";
+ }
+
+ /**
+ * Begin the download
+ */
+ public void begin()
+ {
+ _running = true;
+ if (_progress == null) {
+ _progress = new ProgressDialog(_parentFrame, getNameKey());
+ }
+ _progress.show();
+ // start new thread for time-consuming part
+ new Thread(this).start();
+ }
+
+ /**
+ * Run method using separate thread
+ */
+ public void run()
+ {
+ // Compile list of tiles to get
+ ArrayList<SrtmTile> tileList = new ArrayList<SrtmTile>();
+
+ // First, loop to see which tiles are needed
+ DoubleRange lonRange = _app.getTrackInfo().getTrack().getLonRange();
+ DoubleRange latRange = _app.getTrackInfo().getTrack().getLatRange();
+ final int minLon = (int) Math.floor(lonRange.getMinimum());
+ final int maxLon = (int) Math.floor(lonRange.getMaximum());
+ final int minLat = (int) Math.floor(latRange.getMinimum());
+ final int maxLat = (int) Math.floor(latRange.getMaximum());
+
+ for (int lon=minLon; lon<= maxLon; lon++)
+ {
+ for (int lat=minLat; lat <= maxLat; lat++)
+ {
+ SrtmTile tile = new SrtmTile(lat, lon);
+ boolean alreadyGot = false;
+ for (int t = 0; t < tileList.size(); t++)
+ {
+ if (tileList.get(t).equals(tile)) {
+ alreadyGot = true;
+ }
+ }
+ if (!alreadyGot) {tileList.add(tile);}
+ }
+ }
+
+ downloadTiles(tileList);
+ // Finished
+ _running = false;
+ }
+
+
+ /**
+ * Download the tiles of SRTM data
+ * @param inTileList list of tiles to get
+ */
+ private void downloadTiles(ArrayList<SrtmTile> inTileList)
+ {
+ // Update progress bar
+ if (_progress != null)
+ {
+ _progress.setMaximum(inTileList.size());
+ _progress.setValue(0);
+ }
+
+ String errorMessage = null;
+
+ // Check the cache is ok
+ final String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
+ if (diskCachePath != null)
+ {
+ File srtmDir = new File(diskCachePath, "srtm");
+ if (!srtmDir.exists() && !srtmDir.mkdir()) {
+ // can't create the srtm directory
+ errorMessage = I18nManager.getText("error.downloadsrtm.nocache");
+ }
+ }
+ else {
+ // no cache set up
+ errorMessage = I18nManager.getText("error.downloadsrtm.nocache");
+ }
+
+ // Get urls for each tile
+ URL[] urls = TileFinder.getUrls(inTileList);
+ int numDownloaded = 0;
+ for (int t=0; t<inTileList.size() && !_progress.isCancelled(); t++)
+ {
+ if (urls[t] != null)
+ {
+ // Define streams
+ FileOutputStream outStream = null;
+ InputStream inStream = null;
+ try
+ {
+ // Set progress
+ _progress.setValue(t);
+ // See if we've already got this tile or not
+ File outputFile = getFileToWrite(urls[t]);
+ if (outputFile != null)
+ {
+ // System.out.println("Download: Need to download: " + urls[t]);
+ outStream = new FileOutputStream(outputFile);
+ URLConnection conn = urls[t].openConnection();
+ conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+ inStream = conn.getInputStream();
+ // Copy all the bytes to the file
+ int c;
+ while ((c = inStream.read()) != -1)
+ {
+ outStream.write(c);
+ }
+
+ numDownloaded++;
+ }
+ // else System.out.println("Don't need to download: " + urls[t].getFile());
+ }
+ catch (IOException ioe) {errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
+ }
+ // Make sure streams are closed
+ try {inStream.close();} catch (Exception e) {}
+ try {outStream.close();} catch (Exception e) {}
+ }
+ }
+
+ _progress.dispose();
+ if (_progress.isCancelled()) {
+ return;
+ }
+
+ if (errorMessage != null) {
+ _app.showErrorMessageNoLookup(getNameKey(), errorMessage);
+ }
+ else if (numDownloaded > 0)
+ {
+ JOptionPane.showMessageDialog(_parentFrame, I18nManager.getTextWithNumber("confirm.downloadsrtm", numDownloaded),
+ I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+ }
+ else if (inTileList.size() > 0) {
+ _app.showErrorMessage(getNameKey(), "confirm.downloadsrtm.none");
+ }
+ }
+
+ /**
+ * See whether the SRTM file is already available locally
+ * @param inUrl URL for online resource
+ * @return file object to write to, or null if already there
+ */
+ private static File getFileToWrite(URL inUrl)
+ {
+ String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
+ if (diskCachePath != null)
+ {
+ File srtmDir = new File(diskCachePath, "srtm");
+ if (srtmDir.exists() && srtmDir.isDirectory() && srtmDir.canRead())
+ {
+ File srtmFile = new File(srtmDir, new File(inUrl.getFile()).getName());
+ if (!srtmFile.exists() || !srtmFile.canRead() || srtmFile.length() <= 1) {
+ return srtmFile;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return true if a thread is currently running
+ */
+ public boolean isRunning()
+ {
+ return _running;
+ }
+}
package tim.prune.function.srtm;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
+import tim.prune.data.Altitude;
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
import tim.prune.data.Track;
+import tim.prune.data.UnitSetLibrary;
import tim.prune.gui.ProgressDialog;
+import tim.prune.tips.TipManager;
import tim.prune.undo.UndoLookupSrtm;
/**
- * Class to provide a lookup function for point altitudes
- * using the Space Shuttle's SRTM data files.
- * HGT files are downloaded into memory via HTTP and point altitudes
- * can then be interpolated from the 3m grid data.
+ * Class to provide a lookup function for point altitudes using the Space
+ * Shuttle's SRTM data files. HGT files are downloaded into memory via HTTP and
+ * point altitudes can then be interpolated from the 3m grid data.
*/
public class LookupSrtmFunction extends GenericFunction implements Runnable
{
/** Progress dialog */
- ProgressDialog _progress = null;
+ private ProgressDialog _progress = null;
+ /** Track to process */
+ private Track _track = null;
+ /** Flag for whether this is a real track or a terrain one */
+ private boolean _normalTrack = true;
+ /** Flag set when any tiles had to be downloaded (rather than just loaded locally) */
+ private boolean _hadToDownload = false;
+ /** Flag to check whether this function is currently running or not */
+ private boolean _running = false;
/** Expected size of hgt file in bytes */
private static final long HGT_SIZE = 2884802L;
/** Altitude below which is considered void */
private static final int VOID_VAL = -32768;
-
/**
* Constructor
- * @param inApp App object
+ * @param inApp App object
*/
- public LookupSrtmFunction(App inApp)
- {
+ public LookupSrtmFunction(App inApp) {
super(inApp);
}
}
/**
- * Begin the lookup
+ * Begin the lookup using the normal track
+ */
+ public void begin() {
+ begin(_app.getTrackInfo().getTrack(), true);
+ }
+
+ /**
+ * Begin the lookup with an alternative track
+ * @param inAlternativeTrack
*/
- public void begin()
+ public void begin(Track inAlternativeTrack) {
+ begin(inAlternativeTrack, false);
+ }
+
+ /**
+ * Begin the function with the given parameters
+ * @param inTrack track to process
+ * @param inNormalTrack true if this is a "normal" track, false for an artificially constructed one such as for terrain
+ */
+ private void begin(Track inTrack, boolean inNormalTrack)
{
- if (_progress == null)
- {
+ _running = true;
+ _hadToDownload = false;
+ if (_progress == null) {
_progress = new ProgressDialog(_parentFrame, getNameKey());
}
_progress.show();
+ _track = inTrack;
+ _normalTrack = inNormalTrack;
// start new thread for time-consuming part
new Thread(this).start();
}
-
/**
* Run method using separate thread
*/
public void run()
{
// Compile list of tiles to get
- Track track = _app.getTrackInfo().getTrack();
ArrayList<SrtmTile> tileList = new ArrayList<SrtmTile>();
boolean hasZeroAltitudePoints = false;
boolean hasNonZeroAltitudePoints = false;
// First, loop to see what kind of points we have
- for (int i=0; i<track.getNumPoints(); i++)
+ for (int i = 0; i < _track.getNumPoints(); i++)
{
- if (track.getPoint(i).hasAltitude())
+ if (_track.getPoint(i).hasAltitude())
{
- if (track.getPoint(i).getAltitude().getValue() == 0) {
+ if (_track.getPoint(i).getAltitude().getValue() == 0) {
hasZeroAltitudePoints = true;
}
else {
}
// Now loop again to extract the required tiles
- for (int i=0; i<track.getNumPoints(); i++)
+ for (int i = 0; i < _track.getNumPoints(); i++)
{
// Consider points which don't have altitudes or have zero values
- if (!track.getPoint(i).hasAltitude() || (overwriteZeros && track.getPoint(i).getAltitude().getValue() == 0))
+ if (!_track.getPoint(i).hasAltitude()
+ || (overwriteZeros && _track.getPoint(i).getAltitude().getValue() == 0))
{
- SrtmTile tile = new SrtmTile(track.getPoint(i));
+ SrtmTile tile = new SrtmTile(_track.getPoint(i));
boolean alreadyGot = false;
- for (int t=0; t<tileList.size(); t++) {
+ for (int t = 0; t < tileList.size(); t++)
+ {
if (tileList.get(t).equals(tile)) {
alreadyGot = true;
}
}
}
lookupValues(tileList, overwriteZeros);
+ // Finished
+ _running = false;
+ // Show tip if lots of online lookups were necessary
+ if (_hadToDownload) {
+ _app.showTip(TipManager.Tip_DownloadSrtm);
+ }
}
+
/**
* Lookup the values from SRTM data
* @param inTileList list of tiles to get
*/
private void lookupValues(ArrayList<SrtmTile> inTileList, boolean inOverwriteZeros)
{
- Track track = _app.getTrackInfo().getTrack();
UndoLookupSrtm undo = new UndoLookupSrtm(_app.getTrackInfo());
int numAltitudesFound = 0;
// Update progress bar
- _progress.setMaximum(inTileList.size());
- _progress.setValue(0);
+ if (_progress != null)
+ {
+ _progress.setMaximum(inTileList.size());
+ _progress.setValue(0);
+ }
String errorMessage = null;
// Get urls for each tile
URL[] urls = TileFinder.getUrls(inTileList);
SrtmTile tile = inTileList.get(t);
try
{
+ // Set progress
_progress.setValue(t);
- final int ARRLENGTH = 1201*1201;
+ final int ARRLENGTH = 1201 * 1201;
int[] heights = new int[ARRLENGTH];
// Open zipinputstream on url and check size
- ZipInputStream inStream = new ZipInputStream(urls[t].openStream());
- ZipEntry entry = inStream.getNextEntry();
- boolean entryOk = (entry.getSize() == HGT_SIZE);
- if (entryOk)
+ ZipInputStream inStream = getStreamToHgtFile(urls[t]);
+ boolean entryOk = false;
+ if (inStream != null)
{
- // Read entire file contents into one byte array
- for (int i=0; i<ARRLENGTH; i++) {
- heights[i] = inStream.read()*256 + inStream.read();
- if (heights[i] >= 32768) {heights[i] -= 65536;}
+ ZipEntry entry = inStream.getNextEntry();
+ entryOk = (entry != null && entry.getSize() == HGT_SIZE);
+ if (entryOk)
+ {
+ // Read entire file contents into one byte array
+ for (int i = 0; i < ARRLENGTH; i++)
+ {
+ heights[i] = inStream.read() * 256 + inStream.read();
+ if (heights[i] >= 32768) {heights[i] -= 65536;}
+ }
}
+ // else {
+ // System.out.println("length not ok: " + entry.getSize());
+ // }
+ // Close stream from url
+ inStream.close();
}
- //else {
- // System.out.println("length not ok: " + entry.getSize());
- //}
- // Close stream from url
- inStream.close();
if (entryOk)
{
// Loop over all points in track, try to apply altitude from array
- for (int p=0; p<track.getNumPoints(); p++)
+ for (int p = 0; p < _track.getNumPoints(); p++)
{
- DataPoint point = track.getPoint(p);
- if (!point.hasAltitude() || (inOverwriteZeros && point.getAltitude().getValue() == 0)) {
+ DataPoint point = _track.getPoint(p);
+ if (!point.hasAltitude()
+ || (inOverwriteZeros && point.getAltitude().getValue() == 0))
+ {
if (new SrtmTile(point).equals(tile))
{
double x = (point.getLongitude().getDouble() - tile.getLongitude()) * 1200;
double y = 1201 - (point.getLatitude().getDouble() - tile.getLatitude()) * 1200;
int idx1 = ((int)y)*1201 + (int)x;
- try {
+ try
+ {
int[] fouralts = {heights[idx1], heights[idx1+1], heights[idx1-1201], heights[idx1-1200]};
int numVoids = (fouralts[0]==VOID_VAL?1:0) + (fouralts[1]==VOID_VAL?1:0)
+ (fouralts[2]==VOID_VAL?1:0) + (fouralts[3]==VOID_VAL?1:0);
case 3: altitude = averageNonVoid(fouralts); break;
default: altitude = VOID_VAL;
}
- if (altitude != VOID_VAL) {
+ if (altitude != VOID_VAL)
+ {
point.setFieldValue(Field.ALTITUDE, ""+altitude, false);
+ // depending on settings, this value may have been added as feet, we need to force metres
+ point.getAltitude().reset(new Altitude((int)altitude, UnitSetLibrary.UNITS_METRES));
numAltitudesFound++;
}
}
catch (ArrayIndexOutOfBoundsException obe) {
- //System.err.println("lat=" + point.getLatitude().getDouble() + ", x=" + x + ", y=" + y + ", idx=" + idx1);
+ // System.err.println("lat=" + point.getLatitude().getDouble() + ", x=" + x + ", y=" + y + ", idx=" + idx1);
}
}
}
}
}
}
- catch (IOException ioe) {
- errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
+ catch (IOException ioe) {errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
}
}
}
+
_progress.dispose();
- if (_progress.isCancelled()) {return;}
+ if (_progress.isCancelled()) {
+ return;
+ }
+
if (numAltitudesFound > 0)
{
// Inform app including undo information
- track.requestRescale();
+ _track.requestRescale();
UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_ADDED_OR_REMOVED);
- _app.completeFunction(undo, I18nManager.getText("confirm.lookupsrtm1") + " " + numAltitudesFound
- + " " + I18nManager.getText("confirm.lookupsrtm2"));
+ // Don't update app if we're doing another track
+ if (_normalTrack)
+ {
+ _app.completeFunction(undo,
+ I18nManager.getTextWithNumber("confirm.lookupsrtm", numAltitudesFound));
+ }
}
else if (errorMessage != null) {
_app.showErrorMessageNoLookup(getNameKey(), errorMessage);
}
}
+ /**
+ * See whether the SRTM file is already available locally first, then try online
+ * @param inUrl URL for online resource
+ * @return ZipInputStream either on the local file or on the downloaded zip file
+ */
+ private ZipInputStream getStreamToHgtFile(URL inUrl)
+ throws IOException
+ {
+ String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
+ if (diskCachePath != null)
+ {
+ File srtmDir = new File(diskCachePath, "srtm");
+ if (srtmDir.exists() && srtmDir.isDirectory() && srtmDir.canRead())
+ {
+ File srtmFile = new File(srtmDir, new File(inUrl.getFile()).getName());
+ if (srtmFile.exists() && srtmFile.isFile() && srtmFile.canRead())
+ {
+ // System.out.println("Lookup: Using file " + srtmFile.getAbsolutePath());
+ // File found, use this one
+ return new ZipInputStream(new FileInputStream(srtmFile));
+ }
+ }
+ }
+ // System.out.println("Lookup: Trying online: " + inUrl.toString());
+ _hadToDownload = true;
+ // MAYBE: Only download if we're in online mode?
+ return new ZipInputStream(inUrl.openStream());
+ }
+
/**
* Perform a bilinear interpolation on the given altitude array
* @param inAltitudes array of four altitude values on corners of square (bl, br, tl, tr)
private static int[] fixVoid(int[] inAltitudes)
{
int[] fixed = new int[inAltitudes.length];
- for (int i=0; i<inAltitudes.length; i++) {
+ for (int i = 0; i < inAltitudes.length; i++)
+ {
if (inAltitudes[i] == VOID_VAL) {
fixed[i] = (int) Math.round(averageNonVoid(inAltitudes));
}
{
double totalAltitude = 0.0;
int numAlts = 0;
- for (int i=0; i<inAltitudes.length; i++) {
- if (inAltitudes[i] != VOID_VAL) {
+ for (int i = 0; i < inAltitudes.length; i++)
+ {
+ if (inAltitudes[i] != VOID_VAL)
+ {
totalAltitude += inAltitudes[i];
numAlts++;
}
if (numAlts < 1) {return VOID_VAL;}
return totalAltitude / numAlts;
}
+
+ /**
+ * @return true if a thread is currently running
+ */
+ public boolean isRunning()
+ {
+ return _running;
+ }
}
{
/** Latitude in degrees north/south */
private int _latitude = 0;
- /** Longitude ini degrees east/west */
+ /** Longitude in degrees east/west */
private int _longitude = 0;
/**
_longitude = (int) Math.floor(longitude.getDouble());
}
+ /**
+ * Constructor working out the tile for a single point
+ * @param inLatitude latitude in degrees
+ * @param inLongitude longitude in degrees
+ */
+ public SrtmTile(int inLatitude, int inLongitude)
+ {
+ _latitude = inLatitude;
+ _longitude = inLongitude;
+ }
+
/**
* Check for equality
* @param inOther other tile object
--- /dev/null
+package tim.prune.function.weather;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.table.TableCellRenderer;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.GpsPrune;
+import tim.prune.I18nManager;
+import tim.prune.data.DataPoint;
+import tim.prune.data.NumberUtils;
+import tim.prune.data.Track;
+import tim.prune.function.browser.BrowserLauncher;
+
+/**
+ * Function to display a weather forecast for the current location
+ * using the services of openweathermap.org
+ */
+public class GetWeatherForecastFunction extends GenericFunction implements Runnable
+{
+ /** Dialog object */
+ private JDialog _dialog = null;
+ /** Label for location */
+ private JLabel _locationLabel = null;
+ /** Label for the forecast update time */
+ private JLabel _updateTimeLabel = null;
+ /** Label for the sunrise and sunset times */
+ private JLabel _sunriseLabel = null;
+ /** Radio button for selecting current weather */
+ private JRadioButton _currentForecastRadio = null;
+ /** Radio button for selecting daily forecasts */
+ private JRadioButton _dailyForecastRadio = null;
+ /** Dropdown for selecting celsius / fahrenheit */
+ private JComboBox<String> _tempUnitsDropdown = null;
+ /** Table to hold the forecasts */
+ private JTable _forecastsTable = null;
+ /** Table model */
+ private WeatherTableModel _tableModel = new WeatherTableModel();
+ /** Set of previously obtained results, to avoid repeating calls */
+ private ResultSet _resultSet = new ResultSet();
+ /** Location id obtained from current forecast */
+ private String _locationId = null;
+ /** Flag to show that forecast is currently running, don't start another */
+ private boolean _isRunning = false;
+
+ /** True to just simulate the calls and read files instead, false to call real API */
+ private static final boolean SIMULATE_WITH_FILES = false;
+
+
+ /**
+ * Inner class to pass results asynchronously to the table model
+ */
+ private class ResultUpdater implements Runnable
+ {
+ private WeatherResults _results;
+ public ResultUpdater(WeatherResults inResults) {
+ _results = inResults;
+ }
+ public void run() {
+ _tableModel.setResults(_results);
+ adjustTable();
+ }
+ }
+
+
+ /** Constructor */
+ public GetWeatherForecastFunction(App inApp)
+ {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.getweatherforecast";
+ }
+
+ /**
+ * Begin the function
+ */
+ public void begin()
+ {
+ // Initialise dialog, show empty list
+ if (_dialog == null)
+ {
+ _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+ _dialog.setLocationRelativeTo(_parentFrame);
+ _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ _dialog.getContentPane().add(makeDialogComponents());
+ _dialog.pack();
+ }
+ // Clear results
+ _locationId = null;
+ _tableModel.clear();
+ _locationLabel.setText(I18nManager.getText("confirm.running"));
+ _updateTimeLabel.setText("");
+ _sunriseLabel.setText("");
+ _currentForecastRadio.setSelected(true);
+
+ // Start new thread to load list asynchronously
+ new Thread(this).start();
+
+ _dialog.setVisible(true);
+ }
+
+ /**
+ * Create dialog components
+ * @return Panel containing all gui elements in dialog
+ */
+ private Component makeDialogComponents()
+ {
+ JPanel dialogPanel = new JPanel();
+ dialogPanel.setLayout(new BorderLayout(0, 4));
+
+ JPanel topPanel = new JPanel();
+ topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
+ _locationLabel = new JLabel(I18nManager.getText("confirm.running"));
+ _locationLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ topPanel.add(_locationLabel);
+ _updateTimeLabel = new JLabel(" ");
+ _updateTimeLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ topPanel.add(_updateTimeLabel);
+ _sunriseLabel = new JLabel(" ");
+ _sunriseLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ topPanel.add(_sunriseLabel);
+ JPanel radioPanel = new JPanel();
+ radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.X_AXIS));
+ radioPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
+ ButtonGroup forecastTypeGroup = new ButtonGroup();
+ _currentForecastRadio = new JRadioButton(I18nManager.getText("dialog.weather.currentforecast"));
+ _dailyForecastRadio = new JRadioButton(I18nManager.getText("dialog.weather.dailyforecast"));
+ JRadioButton threeHourlyRadio = new JRadioButton(I18nManager.getText("dialog.weather.3hourlyforecast"));
+ forecastTypeGroup.add(_currentForecastRadio);
+ forecastTypeGroup.add(_dailyForecastRadio);
+ forecastTypeGroup.add(threeHourlyRadio);
+ radioPanel.add(_currentForecastRadio);
+ radioPanel.add(_dailyForecastRadio);
+ radioPanel.add(threeHourlyRadio);
+ _currentForecastRadio.setSelected(true);
+ ActionListener radioListener = new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ if (!_isRunning) new Thread(GetWeatherForecastFunction.this).start();
+ }
+ };
+ _currentForecastRadio.addActionListener(radioListener);
+ _dailyForecastRadio.addActionListener(radioListener);
+ threeHourlyRadio.addActionListener(radioListener);
+ radioPanel.add(Box.createHorizontalGlue());
+ radioPanel.add(Box.createHorizontalStrut(40));
+
+ // Dropdown for temperature units
+ radioPanel.add(new JLabel(I18nManager.getText("dialog.weather.temperatureunits") + ": "));
+ _tempUnitsDropdown = new JComboBox<String>(new String[] {
+ I18nManager.getText("units.degreescelsius"), I18nManager.getText("units.degreesfahrenheit")
+ });
+ _tempUnitsDropdown.setMaximumSize(_tempUnitsDropdown.getPreferredSize());
+ _tempUnitsDropdown.addActionListener(radioListener);
+ radioPanel.add(_tempUnitsDropdown);
+ radioPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ topPanel.add(radioPanel);
+ dialogPanel.add(topPanel, BorderLayout.NORTH);
+
+ final IconRenderer iconRenderer = new IconRenderer();
+ _forecastsTable = new JTable(_tableModel)
+ {
+ public TableCellRenderer getCellRenderer(int row, int column) {
+ if ((row == WeatherTableModel.ROW_ICON)) {
+ return iconRenderer;
+ }
+ return super.getCellRenderer(row, column);
+ }
+ };
+ _forecastsTable.setRowSelectionAllowed(false);
+ _forecastsTable.setRowHeight(2, 55); // make just that row high enough to see icons
+ _forecastsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+ _forecastsTable.getTableHeader().setReorderingAllowed(false);
+ _forecastsTable.setShowHorizontalLines(false);
+
+ JScrollPane scroller = new JScrollPane(_forecastsTable);
+ scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
+ scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
+ scroller.setPreferredSize(new Dimension(500, 210));
+ scroller.getViewport().setBackground(Color.white);
+
+ dialogPanel.add(scroller, BorderLayout.CENTER);
+
+ // button panel at bottom
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ JButton launchButton = new JButton(I18nManager.getText("button.showwebpage"));
+ launchButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ BrowserLauncher.launchBrowser("http://openweathermap.org/city/" + (_locationId == null ? "" : _locationId));
+ }
+ });
+ buttonPanel.add(launchButton);
+ // close
+ JButton closeButton = new JButton(I18nManager.getText("button.close"));
+ closeButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(closeButton);
+ // Add a holder panel with a static label to credit openweathermap
+ JPanel southPanel = new JPanel();
+ southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS));
+ southPanel.add(new JLabel(I18nManager.getText("dialog.weather.creditnotice")));
+ southPanel.add(buttonPanel);
+ dialogPanel.add(southPanel, BorderLayout.SOUTH);
+ dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+ return dialogPanel;
+ }
+
+ /**
+ * Get the weather forecast in a separate thread
+ */
+ public void run()
+ {
+ if (_isRunning) {return;} // don't run twice
+ _isRunning = true;
+
+ // Are we getting the current details, or getting a forecast?
+ final boolean isCurrent = _locationId == null || _currentForecastRadio.isSelected();
+ final boolean isDailyForecast = _dailyForecastRadio.isSelected() && !isCurrent;
+ final boolean isHourlyForecast = !isCurrent && !isDailyForecast;
+ final boolean isUsingCelsius = _tempUnitsDropdown.getSelectedIndex() == 0;
+
+ // Have we got these results already? Look in store
+ WeatherResults results = _resultSet.getWeather(_locationId, isCurrent, isDailyForecast, isHourlyForecast, isUsingCelsius);
+ if (results == null)
+ {
+ if (isCurrent)
+ {
+ // Get the current details using either lat/long or locationId
+ results = getCurrentWeather(isUsingCelsius);
+ // If the current radio isn't selected, select it
+ if (!_currentForecastRadio.isSelected()) {
+ _currentForecastRadio.setSelected(true);
+ }
+ }
+ else
+ {
+ // Get the specified forecast using the retrieved locationId
+ results = getWeatherForecast(isDailyForecast, isUsingCelsius);
+ }
+ // If it's a valid answer, store it for later
+ if (results != null)
+ {
+ _resultSet.setWeather(results, _locationId, isCurrent, isDailyForecast, isHourlyForecast, isUsingCelsius);
+ }
+ }
+
+ // update table contents and labels
+ if (results != null)
+ {
+ SwingUtilities.invokeLater(new ResultUpdater(results));
+ _locationLabel.setText(I18nManager.getText("dialog.weather.location") + ": " + results.getLocationName());
+ final String ut = results.getUpdateTime();
+ _updateTimeLabel.setText(I18nManager.getText("dialog.weather.update") + ": " + (ut == null ? "" : ut));
+ if (results.getSunriseTime() != null && results.getSunsetTime() != null)
+ {
+ _sunriseLabel.setText(I18nManager.getText("dialog.weather.sunrise") + ": " + results.getSunriseTime()
+ + ", " + I18nManager.getText("dialog.weather.sunset") + ": " + results.getSunsetTime());
+ }
+ else {
+ _sunriseLabel.setText("");
+ }
+ }
+
+ // finished running
+ _isRunning = false;
+ }
+
+
+ /**
+ * Adjust the column widths and row heights to fit the displayed data
+ */
+ private void adjustTable()
+ {
+ if (!_tableModel.isEmpty())
+ {
+ // adjust column widths for all columns
+ for (int i=0; i<_forecastsTable.getColumnCount(); i++)
+ {
+ double maxWidth = 0.0;
+ for (int j=0; j<_forecastsTable.getRowCount(); j++)
+ {
+ final String value = _tableModel.getValueAt(j, i).toString();
+ maxWidth = Math.max(maxWidth, _forecastsTable.getCellRenderer(0, 0).getTableCellRendererComponent(
+ _forecastsTable, value, false, false, 0, 0).getPreferredSize().getWidth());
+ }
+ _forecastsTable.getColumnModel().getColumn(i).setMinWidth((int) maxWidth + 2);
+ }
+ // Set minimum row heights
+ final int labelHeight = (int) (_forecastsTable.getCellRenderer(0, 0).getTableCellRendererComponent(
+ _forecastsTable, "M", false, false, 0, 0).getMinimumSize().getHeight() * 1.2f + 4);
+ for (int i=0; i<_forecastsTable.getRowCount(); i++)
+ {
+ if (i == WeatherTableModel.ROW_ICON) {
+ _forecastsTable.setRowHeight(i, 55);
+ }
+ else {
+ _forecastsTable.setRowHeight(i, labelHeight);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the current weather using the lat/long and populate _results
+ * @param inUseCelsius true for celsius, false for fahrenheit
+ * @return weather results
+ */
+ private WeatherResults getCurrentWeather(boolean inUseCelsius)
+ {
+ final Track track = _app.getTrackInfo().getTrack();
+ if (track.getNumPoints() < 1) {return null;}
+ // Get coordinates to lookup
+ double lat = 0.0, lon = 0.0;
+ // See if a point is selected, if so use that
+ DataPoint currPoint = _app.getTrackInfo().getCurrentPoint();
+ if (currPoint != null)
+ {
+ // Use selected point
+ lat = currPoint.getLatitude().getDouble();
+ lon = currPoint.getLongitude().getDouble();
+ }
+ else
+ {
+ lat = track.getLatRange().getMidValue();
+ lon = track.getLonRange().getMidValue();
+ }
+
+ InputStream inStream = null;
+ // Build url either with coordinates or with location id if available
+ final String urlString = "http://api.openweathermap.org/data/2.5/weather?"
+ + (_locationId == null ? ("lat=" + NumberUtils.formatNumberUk(lat, 5) + "&lon=" + NumberUtils.formatNumberUk(lon, 5))
+ : ("id=" + _locationId))
+ + "&lang=" + I18nManager.getText("openweathermap.lang")
+ + "&mode=xml&units=" + (inUseCelsius ? "metric" : "imperial");
+ // System.out.println(urlString);
+
+ // Parse the returned XML with a special handler
+ OWMCurrentHandler xmlHandler = new OWMCurrentHandler();
+ try
+ {
+ URL url = new URL(urlString);
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ // DEBUG: Simulate the call in case of no network connection
+ if (SIMULATE_WITH_FILES)
+ {
+ inStream = new FileInputStream(new File("tim/prune/test/examplecurrentweather.xml"));
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException tie) {}
+ }
+ else
+ {
+ URLConnection conn = url.openConnection();
+ conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+ inStream = conn.getInputStream();
+ }
+
+ saxParser.parse(inStream, xmlHandler);
+ }
+ catch (Exception e)
+ {
+ // Show error message but don't close dialog
+ _app.showErrorMessageNoLookup(getNameKey(), e.getClass().getName() + " - " + e.getMessage());
+ _isRunning = false;
+ return null;
+ }
+ // Close stream and ignore errors
+ try {
+ inStream.close();
+ } catch (Exception e) {}
+
+ // Save the location id
+ if (xmlHandler.getLocationId() != null) {
+ _locationId = xmlHandler.getLocationId();
+ }
+ // Get the results from the handler and return
+ WeatherResults results = new WeatherResults();
+ results.setForecast(xmlHandler.getCurrentWeather());
+ results.setLocationName(xmlHandler.getLocationName());
+ results.setUpdateTime(xmlHandler.getUpdateTime());
+ results.setSunriseSunsetTimes(xmlHandler.getSunriseTime(), xmlHandler.getSunsetTime());
+ results.setTempsCelsius(inUseCelsius);
+ return results;
+ }
+
+
+ /**
+ * Get the weather forecast for the current location id and populate in _results
+ * @param inDaily true for daily, false for 3-hourly
+ * @param inCelsius true for celsius, false for fahrenheit
+ * @return weather results
+ */
+ private WeatherResults getWeatherForecast(boolean inDaily, boolean inCelsius)
+ {
+ InputStream inStream = null;
+ // Build URL
+ final String forecastCount = inDaily ? "8" : "3";
+ final String urlString = "http://api.openweathermap.org/data/2.5/forecast"
+ + (inDaily ? "/daily" : "") + "?id=" + _locationId + "&lang=" + I18nManager.getText("openweathermap.lang")
+ + "&mode=xml&units=" + (inCelsius ? "metric" : "imperial") + "&cnt=" + forecastCount;
+ // System.out.println(urlString);
+
+ // Parse the returned XML with a special handler
+ OWMForecastHandler xmlHandler = new OWMForecastHandler();
+ try
+ {
+ URL url = new URL(urlString);
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ // DEBUG: Simulate the call in case of no network connection
+ if (SIMULATE_WITH_FILES)
+ {
+ inStream = new FileInputStream(new File("tim/prune/test/exampleweatherforecast.xml"));
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException tie) {}
+ }
+ else
+ {
+ URLConnection conn = url.openConnection();
+ conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+ inStream = conn.getInputStream();
+ }
+
+ saxParser.parse(inStream, xmlHandler);
+ }
+ catch (Exception e)
+ {
+ // Show error message but don't close dialog
+ _app.showErrorMessageNoLookup(getNameKey(), e.getClass().getName() + " - " + e.getMessage());
+ _isRunning = false;
+ return null;
+ }
+ // Close stream and ignore errors
+ try {
+ inStream.close();
+ } catch (Exception e) {}
+
+ // Get results from handler, put in model
+ WeatherResults results = new WeatherResults();
+ results.setForecasts(xmlHandler.getForecasts());
+ results.setLocationName(xmlHandler.getLocationName());
+ results.setUpdateTime(xmlHandler.getUpdateTime());
+ results.setTempsCelsius(inCelsius);
+ return results;
+ }
+}
--- /dev/null
+package tim.prune.function.weather;
+
+import java.awt.Component;
+import java.awt.Dimension;
+
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.SwingConstants;
+import javax.swing.table.TableCellRenderer;
+
+import tim.prune.gui.IconManager;
+
+/**
+ * Class to render the weather icons in the table
+ */
+public class IconRenderer extends JLabel implements TableCellRenderer
+{
+ /** Get the renderer component for the given row, column and value */
+ public Component getTableCellRendererComponent(JTable inTable, Object inValue, boolean inIsSelected,
+ boolean inHasFocus, int inRow, int inColumn)
+ {
+ if (inValue != null) {
+ setIcon(IconManager.getImageIcon("weather-" + inValue.toString()));
+ setHorizontalAlignment(SwingConstants.CENTER);
+ }
+ else {
+ setIcon(null);
+ setText("");
+ }
+ return this;
+ }
+
+ /** Override the minimum size method */
+ public Dimension getMinimumSize() {
+ return new Dimension(52, 52);
+ }
+}
--- /dev/null
+package tim.prune.function.weather;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * XML handler for dealing with the XML of current weather reports
+ * returned from openweathermap.org (forecasts have different structure)
+ */
+public class OWMCurrentHandler extends DefaultHandler
+{
+ /** The location name */
+ private String _locName = null;
+ /** The location id */
+ private String _locId = null;
+ /** The last update time */
+ private String _updateTime = null;
+ /** Sunrise and sunset times */
+ private String _sunriseTime = null, _sunsetTime = null;
+ /** The currently open forecast */
+ private SingleForecast _forecast = new SingleForecast();
+
+
+ /**
+ * React to the start of an XML tag
+ */
+ public void startElement(String inUri, String inLocalName, String inTagName,
+ Attributes inAttributes) throws SAXException
+ {
+ if (inTagName.equals("city")) {
+ _locName = inAttributes.getValue("name");
+ _locId = inAttributes.getValue("id");
+ }
+ else if (inTagName.equals("weather")) {
+ // numeric code, owm image name, description
+ _forecast.setSymbol(inAttributes.getValue("number"), inAttributes.getValue("icon"), inAttributes.getValue("value"));
+ }
+ else if (inTagName.equals("speed")) {
+ _forecast.setWindDesc(inAttributes.getValue("name"));
+ }
+ else if (inTagName.equals("temperature"))
+ {
+ String currTemp = inAttributes.getValue("value");
+ _forecast.setTemps(currTemp, currTemp);
+ // We can ignore the min and max here
+ }
+ else if (inTagName.equals("humidity")) {
+ _forecast.setHumidity(inAttributes.getValue("value") + inAttributes.getValue("unit"));
+ }
+ else if (inTagName.equals("lastupdate")) {
+ _updateTime = inAttributes.getValue("value");
+ }
+ else if (inTagName.equals("sun"))
+ {
+ _sunriseTime = inAttributes.getValue("rise");
+ _sunsetTime = inAttributes.getValue("set");
+ }
+
+ super.startElement(inUri, inLocalName, inTagName, inAttributes);
+ }
+
+ /** @return location name of forecast */
+ public String getLocationName() {
+ return _locName;
+ }
+
+ /** @return location id of forecast */
+ public String getLocationId() {
+ return _locId;
+ }
+
+ /** @return update time of report */
+ public String getUpdateTime() {
+ return _updateTime;
+ }
+
+ /** @return current weather conditions */
+ public SingleForecast getCurrentWeather() {
+ return _forecast;
+ }
+
+ /** @return sunrise time as 2013-07-25T03:55:14 */
+ public String getSunriseTime() {
+ return _sunriseTime;
+ }
+ /** @return sunset time as 2013-07-25T19:07:25 */
+ public String getSunsetTime() {
+ return _sunsetTime;
+ }
+}
--- /dev/null
+package tim.prune.function.weather;
+
+import java.util.ArrayList;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * XML handler for dealing with the XML of weather forecasts
+ * returned from openweathermap.org (current weather has different structure)
+ */
+public class OWMForecastHandler extends DefaultHandler
+{
+ private String _value = null;
+ /** The location name */
+ private String _locName = null;
+ /** The forecast update time */
+ private String _updateTime = null;
+ /** The currently open forecast */
+ private SingleForecast _forecast = null;
+ /** List of all the forecasts found so far */
+ private ArrayList<SingleForecast> _forecastList = new ArrayList<SingleForecast>();
+
+
+ /**
+ * React to the start of an XML tag
+ */
+ public void startElement(String inUri, String inLocalName, String inTagName,
+ Attributes inAttributes) throws SAXException
+ {
+ if (inTagName.equals("time")) { // start of a new forecast
+ _forecast = new SingleForecast();
+ // date, timefrom, timeto
+ _forecast.setTime(inAttributes.getValue("day"), inAttributes.getValue("from"), inAttributes.getValue("to"));
+ }
+ else if (inTagName.equals("symbol")) {
+ // numeric code, owm image name, description
+ _forecast.setSymbol(inAttributes.getValue("number"), inAttributes.getValue("var"), inAttributes.getValue("name"));
+ }
+ else if (inTagName.equals("windSpeed")) {
+ _forecast.setWindDesc(inAttributes.getValue("name"));
+ }
+ else if (inTagName.equals("temperature")) {
+ _forecast.setTemps(inAttributes.getValue("min"), inAttributes.getValue("max"));
+ }
+ else if (inTagName.equals("humidity")) {
+ _forecast.setHumidity(inAttributes.getValue("value") + inAttributes.getValue("unit"));
+ }
+ _value = null;
+ super.startElement(inUri, inLocalName, inTagName, inAttributes);
+ }
+
+ /**
+ * React to the end of an XML tag
+ */
+ public void endElement(String inUri, String inLocalName, String inTagName)
+ throws SAXException
+ {
+ if (inTagName.equals("name")) {
+ _locName = _value;
+ }
+ else if (inTagName.equals("lastupdate")) {
+ _updateTime = _value;
+ }
+ else if (inTagName.equals("time"))
+ {
+ // End of a time tag, add the current forecast to the list
+ _forecastList.add(_forecast);
+ }
+ super.endElement(inUri, inLocalName, inTagName);
+ }
+
+ /**
+ * React to characters received inside tags
+ */
+ public void characters(char[] inCh, int inStart, int inLength)
+ throws SAXException
+ {
+ String value = new String(inCh, inStart, inLength);
+ _value = (_value==null?value:_value+value);
+ super.characters(inCh, inStart, inLength);
+ }
+
+ /** @return location name of forecast */
+ public String getLocationName() {
+ return _locName;
+ }
+
+ /** @return update time of forecast */
+ public String getUpdateTime() {
+ return _updateTime;
+ }
+
+ /**
+ * @return the list of forecasts
+ */
+ public ArrayList<SingleForecast> getForecasts() {
+ return _forecastList;
+ }
+}
--- /dev/null
+package tim.prune.function.weather;
+
+/**
+ * Class to hold a set of (up to six) weather results,
+ * so that they don't have to be downloaded again
+ */
+public class ResultSet
+{
+ /** Array of six results */
+ private WeatherResults[] _results = new WeatherResults[6];
+ /** Location id for which these results apply */
+ private String _locationId = null;
+
+ /**
+ * Clear the array, forget all results
+ */
+ private void clear()
+ {
+ for (int i=0; i<6; i++) {
+ _results[i] = null;
+ }
+ }
+
+ /**
+ * Get the specified weather results, if available
+ * @param inLocationId location id
+ * @param inCurrent true to get the current weather
+ * @param inDaily true to get the daily forecast
+ * @param inHourly true to get the three-hourly forecast
+ * @param inCelsius true to get celsius
+ * @return weather results, or null if not available
+ */
+ public WeatherResults getWeather(String inLocationId,
+ boolean inCurrent, boolean inDaily, boolean inHourly, boolean inCelsius)
+ {
+ // Check location
+ if (inLocationId == null || _locationId == null || !inLocationId.equals(_locationId)) {
+ return null;
+ }
+ // check forecast type
+ final int numTypesGiven = (inCurrent ? 1 : 0) + (inDaily ? 1 : 0) + (inHourly ? 1 : 0);
+ if (numTypesGiven != 1) {
+ System.err.println("getWeather, numtypesgiven = " + numTypesGiven);
+ return null; // can't ask for more or less than one type
+ }
+ // Pull out from array
+ final int index = (inCurrent ? 0 : (inDaily ? 2 : 4)) + (inCelsius ? 1 : 0);
+ return _results[index];
+ }
+
+ /**
+ * Store the given weather results
+ * @param inResults results object
+ * @param inLocationId location id
+ * @param inCurrent true if this is the current weather
+ * @param inDaily true if this is the daily forecast
+ * @param inHourly true if this is the three-hourly forecast
+ * @param inCelsius true if numbers are celsius
+ */
+ public void setWeather(WeatherResults inResults, String inLocationId,
+ boolean inCurrent, boolean inDaily, boolean inHourly, boolean inCelsius)
+ {
+ // Check location
+ if (inLocationId == null || inLocationId.equals("")) {
+ return;
+ }
+ if (_locationId == null || !inLocationId.equals(_locationId))
+ {
+ // coordinates have changed
+ clear();
+ _locationId = inLocationId;
+ }
+ // check forecast type
+ final int numTypesGiven = (inCurrent ? 1 : 0) + (inDaily ? 1 : 0) + (inHourly ? 1 : 0);
+ if (numTypesGiven != 1) {
+ System.err.println("setWeather, numtypesgiven = " + numTypesGiven);
+ return; // can't set more or less than one type
+ }
+ // Store in array
+ final int index = (inCurrent ? 0 : (inDaily ? 2 : 4)) + (inCelsius ? 1 : 0);
+ _results[index] = inResults;
+ }
+}
--- /dev/null
+package tim.prune.function.weather;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * Class to represent a weather forecast
+ * for a single day or a 3-hour period
+ */
+public class SingleForecast
+{
+ private String _date = null;
+ private String _dayDescKey = null;
+ private String _timeFrom = null, _timeTo = null;
+ private String _imageName = null;
+ private String _desc = null;
+ private String _tempString = null;
+ private String _humidity = null;
+ private String _windDesc = null;
+
+ /** For getting today's and tomorrow's dates */
+ private static SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
+
+ /** Set the time of the forecast */
+ public void setTime(String inDate, String inTimeFrom, String inTimeTo)
+ {
+ _date = inDate;
+ if (inTimeFrom != null && inTimeFrom.length() > 10
+ && inTimeTo != null && inTimeTo.length() > 10)
+ {
+ _date = inTimeFrom.substring(0, 10);
+ _timeFrom = inTimeFrom.substring(11, 16);
+ _timeTo = inTimeTo.substring(11, 16);
+ }
+ _dayDescKey = getDayDescriptionKey(_date);
+ // System.out.println(_date + " is " + _dayDescKey);
+ }
+
+ /**
+ * Set the symbol details
+ */
+ public void setSymbol(String inNumber, String inImageCode, String inDesc)
+ {
+ _imageName = getIconName(inNumber, inImageCode);
+ // System.out.println("For number " + inNumber + "(" + inDesc + ") and code " + inImageCode + ", the symbol is " + _imageName);
+ _desc = inDesc;
+ }
+
+ /**
+ * Set the minimum and maximum temperatures (will be rounded to nearest int)
+ */
+ public void setTemps(String inMin, String inMax)
+ {
+ String tempMin = null, tempMax = null;
+ try {
+ tempMin = "" + Math.round(Double.parseDouble(inMin));
+ } catch (Exception e) {}; // tempMin stays null if temp can't be parsed
+ try {
+ tempMax = "" + Math.round(Double.parseDouble(inMax));
+ } catch (Exception e) {}; // tempMax stays null if temp can't be parsed
+
+ _tempString = tempMin;
+ if (tempMin != null && tempMax != null) {
+ if (!tempMin.equals(tempMax))
+ {
+ if (tempMin.charAt(0) == '-' && tempMax.charAt(0) != '-' && tempMax.charAt(0) != '0') {
+ // min temp is negative, max is positive, so add a + to the max
+ tempMax = "+" + tempMax;
+ }
+ _tempString = tempMin + " — " + tempMax;
+ }
+ }
+ else if (tempMax != null) {
+ _tempString = tempMax;
+ }
+ }
+
+ /** Set humidity */
+ public void setHumidity(String inHumidity) {
+ _humidity = inHumidity;
+ }
+
+ /** Set description of wind */
+ public void setWindDesc(String inDesc) {
+ _windDesc = inDesc;
+ }
+
+ /**
+ * Get the name of the image file for the given weather report
+ * @param inCode numeric three-digit code, as string
+ * @param inImage filename as given by openweather (just used for day/night)
+ * @return image file using GpsPrune's icons
+ */
+ public static String getIconName(String inCode, String inImage)
+ {
+ final boolean daytime = inImage == null || inImage.length() != 3 || inImage.charAt(2) != 'n';
+ final char leadDigit = (inCode == null || inCode.equals("")) ? '0' : inCode.charAt(0);
+ String iconName = null;
+ switch (leadDigit)
+ {
+ case '2': return "storm.png";
+ case '3': return "lightrain.png";
+ case '5':
+ iconName = "rain.png";
+ if (inCode.equals("500")) {iconName = "lightrain.png";}
+ else if (inCode.equals("511")) {iconName = "hail.png";}
+ break;
+ case '6': return "snow.png";
+ case '7': return "fog.png";
+ case '8':
+ iconName = daytime ? "clouds-day.png" : "clouds-night.png";
+ if (inCode.equals("800")) {iconName = daytime ? "clear-day.png" : "clear-night.png";}
+ else if (inCode.equals("804")) {iconName = "clouds.png";}
+ break;
+ case '9':
+ iconName = "extreme.png";
+ if (inCode.equals("906")) {iconName = "hail.png";}
+ break;
+ }
+ return iconName;
+ }
+
+ /**
+ * MAYBE: Maybe split off into separate DateFunctions class?
+ * @param inDate date
+ * @return day description, such as "today" or "saturday"
+ */
+ private static String getDayDescriptionKey(String inDate)
+ {
+ if (inDate == null || inDate.length() != 10) {return null;}
+ Calendar cal = Calendar.getInstance();
+ String todaysDate = DATE_FORMATTER.format(cal.getTime());
+ if (inDate.equals(todaysDate)) {return "today";}
+ cal.add(Calendar.DATE, 1);
+ String tomorrowsDate = DATE_FORMATTER.format(cal.getTime());
+ if (inDate.equals(tomorrowsDate)) {return "tomorrow";}
+ // Construct a date with this string and find out its day
+ try
+ {
+ cal.setTime(DATE_FORMATTER.parse(inDate));
+ switch (cal.get(Calendar.DAY_OF_WEEK))
+ {
+ case Calendar.MONDAY : return "monday";
+ case Calendar.TUESDAY : return "tuesday";
+ case Calendar.WEDNESDAY : return "wednesday";
+ case Calendar.THURSDAY : return "thursday";
+ case Calendar.FRIDAY : return "friday";
+ case Calendar.SATURDAY : return "saturday";
+ case Calendar.SUNDAY : return "sunday";
+ }
+ }
+ catch (ParseException pe) {}
+
+ return "other";
+ }
+
+ /** @return true if there are times present, not just a date */
+ public boolean hasTimes() {
+ return _timeFrom != null && _timeTo != null;
+ }
+ /** @return temperature range */
+ public String getTemps() {
+ return _tempString;
+ }
+
+ /** @return date */
+ public String getDate() {return _date;}
+ /** @return time from */
+ public String getTimeFrom() {return _timeFrom;}
+ /** @return time to */
+ public String getTimeTo() {return _timeTo;}
+ /** @return day description */
+ public String getDayDesc() {return _dayDescKey;}
+
+ /** @return image name */
+ public String getImageName() {return _imageName;}
+ /** @return description */
+ public String getDescription() {return _desc;}
+
+ /** @return humidity */
+ public String getHumidity() {return _humidity;}
+ /** @return wind description */
+ public String getWindDescription() {return _windDesc;}
+}
--- /dev/null
+package tim.prune.function.weather;
+
+import java.util.ArrayList;
+
+
+/**
+ * Model for results of weather forecast from openweathermap.org
+ */
+public class WeatherResults
+{
+ /** List of forecasts */
+ private ArrayList<SingleForecast> _forecastList = new ArrayList<SingleForecast>();
+ /** Flag whether the units are metric (Celsius) or not (Fahrenheit) */
+ private boolean _tempsCelsius = true;
+ /** Location name */
+ private String _locationName = null;
+ /** Last update timestamp */
+ private String _updateTime = null;
+ /** Sunrise and sunset times as HH:MM */
+ private String _sunriseTime = null, _sunsetTime = null;
+
+
+ /**
+ * Add a single forecast to this model (for the current weather)
+ * @param inResults current results
+ */
+ public void setForecast(SingleForecast inResults)
+ {
+ _forecastList.clear();
+ if (inResults != null) {
+ _forecastList.add(inResults);
+ }
+ }
+
+ /**
+ * Add a list of forecasts to this model
+ * @param inList list of forecasts to add
+ */
+ public void setForecasts(ArrayList<SingleForecast> inList)
+ {
+ _forecastList.clear();
+ if (inList != null && inList.size() > 0) {
+ _forecastList.addAll(inList);
+ }
+ }
+
+ /** @return the number of forecasts */
+ public int getNumForecasts()
+ {
+ return _forecastList.size();
+ }
+
+ /**
+ * @param inIndex index of forecast starting from 0
+ * @return the specified forecast
+ */
+ public SingleForecast getForecast(int inIndex)
+ {
+ if (inIndex < 0 || inIndex >= getNumForecasts()) {
+ return null;
+ }
+ return _forecastList.get(inIndex);
+ }
+
+ /**
+ * Clear the list of forecasts
+ */
+ public void clear()
+ {
+ _forecastList.clear();
+ _sunriseTime = _sunsetTime = null;
+ _updateTime = null;
+ }
+
+ /**
+ * @param inCelsius true for celsius, false for fahrenheit
+ */
+ public void setTempsCelsius(boolean inCelsius)
+ {
+ _tempsCelsius = inCelsius;
+ }
+
+ /** @return true if this forecast uses Celsius */
+ public boolean isCelsius() {
+ return _tempsCelsius;
+ }
+
+ /**
+ * Set the sunrise and sunset times (only from current weather, not from forecast)
+ * @param inRiseTime sunrise time as YYYY-MM-DDThh:mm:ss
+ * @param inSetTime sunset time as YYYY-MM-DDThh:mm:ss
+ */
+ public void setSunriseSunsetTimes(String inRiseTime, String inSetTime)
+ {
+ _sunriseTime = _sunsetTime = null;
+ if (inRiseTime != null && inRiseTime.length() == 19
+ && inSetTime != null && inSetTime.length() == 19)
+ {
+ _sunriseTime = inRiseTime.substring(11, 16);
+ _sunsetTime = inSetTime.substring(11, 16);
+ }
+ }
+
+ /** @return sunrise time as HH:MM */
+ public String getSunriseTime() {
+ return _sunriseTime;
+ }
+ /** @return sunset time as HH:MM */
+ public String getSunsetTime() {
+ return _sunsetTime;
+ }
+
+ /** @param inName location name */
+ public void setLocationName(String inName) {
+ _locationName = inName;
+ }
+
+ /** @return location name */
+ public String getLocationName() {
+ return _locationName;
+ }
+
+ /** @param inTime timestamp of forecast */
+ public void setUpdateTime(String inTime) {
+ _updateTime = inTime;
+ }
+
+ /** @return timestamp of last update */
+ public String getUpdateTime()
+ {
+ return _updateTime;
+ }
+}
--- /dev/null
+package tim.prune.function.weather;
+
+import javax.swing.table.AbstractTableModel;
+
+import tim.prune.I18nManager;
+
+
+/**
+ * Table model for results of weather forecast
+ */
+public class WeatherTableModel extends AbstractTableModel
+{
+ /** Weather results */
+ private WeatherResults _results;
+
+ /** Row indices */
+ public static final int ROW_DAY = 0;
+ public static final int ROW_DESC = 1;
+ public static final int ROW_WIND = 2;
+ public static final int ROW_ICON = 3;
+ public static final int ROW_TEMP = 4;
+ public static final int ROW_HUMID = 5;
+
+ /** String for degrees Celsius */
+ private static final String UNITS_DEGC = I18nManager.getText("units.degreescelsius.short");
+ /** String for degrees Fahrenheit */
+ private static final String UNITS_DEGF = I18nManager.getText("units.degreesfahrenheit.short");
+
+ /**
+ * @return column count
+ */
+ public int getColumnCount()
+ {
+ if (_results == null) {return 0;}
+ return _results.getNumForecasts();
+ }
+
+ /**
+ * @param inColNum column number
+ * @return column label for given column
+ */
+ public String getColumnName(int inColNum)
+ {
+ if (_results != null && inColNum >= 0 && inColNum < getColumnCount())
+ {
+ SingleForecast forecast = _results.getForecast(inColNum);
+ if (!forecast.hasTimes() || forecast.getTimeFrom().startsWith("00")) {
+ return forecast.getDate();
+ }
+ return forecast.getTimeFrom();
+ }
+ return "";
+ }
+
+ /**
+ * @return number of rows
+ */
+ public int getRowCount()
+ {
+ return 6;
+ }
+
+ /** @return true if there are no columns */
+ public boolean isEmpty()
+ {
+ return getColumnCount() == 0;
+ }
+
+ /**
+ * @param inRowNum row number
+ * @param inColNum column number
+ * @return cell entry at given row and column
+ */
+ public Object getValueAt(int inRowNum, int inColNum)
+ {
+ if (inColNum < 0 || inColNum >= getColumnCount()) {return "";}
+ SingleForecast forecast = _results.getForecast(inColNum);
+ if (forecast != null)
+ {
+ switch (inRowNum)
+ {
+ case ROW_DAY: {
+ final String dayDesc = forecast.getDayDesc() == null ? "now" : forecast.getDayDesc();
+ return buildDisplayString(null, I18nManager.getText("dialog.weather.day." + dayDesc));
+ }
+ case ROW_DESC: return buildDisplayString(null, forecast.getDescription());
+ case ROW_WIND: return buildDisplayString("Wind", forecast.getWindDescription());
+ case ROW_ICON: return forecast.getImageName();
+ case ROW_TEMP: return buildDisplayString("Temp", forecast.getTemps()
+ + (_results.isCelsius() ? UNITS_DEGC : UNITS_DEGF));
+ case ROW_HUMID: return buildDisplayString("Humidity", forecast.getHumidity());
+ }
+ // TODO: Use language-specific texts for wind, temp and humidity
+ }
+ return "";
+ }
+
+ /**
+ * Build a html string from the given title and value
+ */
+ private static final String buildDisplayString(String inTitle, String inValue)
+ {
+ if (inValue == null) {return null;}
+ return "<html>" + (inTitle == null ? "" : (inTitle + ": "))
+ + "<big>" + inValue.replaceAll(" ", " ") + "</big></html>";
+ }
+
+ /**
+ * Set the results
+ * @param inResults weather results including all forecasts
+ */
+ public void setResults(WeatherResults inResults)
+ {
+ _results = inResults;
+ fireTableStructureChanged();
+ }
+
+ /**
+ * Clear the list of forecasts
+ */
+ public void clear()
+ {
+ setResults(null);
+ }
+}
--- /dev/null
+package tim.prune.gui;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EtchedBorder;
+
+import tim.prune.I18nManager;
+import tim.prune.data.Track;
+import tim.prune.gui.map.MapSource;
+import tim.prune.gui.map.MapSourceLibrary;
+import tim.prune.save.BaseImageConfigDialog;
+import tim.prune.save.BaseImageConsumer;
+import tim.prune.save.MapGrouter;
+import tim.prune.threedee.ImageDefinition;
+
+/**
+ * Panel used to show the current base image details
+ * and an edit button to change the definition
+ */
+public class BaseImageDefinitionPanel extends JPanel implements BaseImageConsumer
+{
+ /** Parent object (if any) */
+ private BaseImageConsumer _parent = null;
+ /** Label to describe the current settings */
+ private JLabel _baseImageLabel = null;
+ /** Button for changing the definition */
+ private JButton _editButton = null;
+ /** Dialog called by the "Edit" button to change the settings */
+ private BaseImageConfigDialog _baseImageConfig = null;
+
+
+ /**
+ * Constructor
+ * @param inParent parent object to inform about changes
+ * @param inParentDialog parent dialog
+ * @param inTrack track object
+ */
+ public BaseImageDefinitionPanel(BaseImageConsumer inParent, JDialog inParentDialog,
+ Track inTrack)
+ {
+ _parent = inParent;
+ _baseImageConfig = new BaseImageConfigDialog(this, inParentDialog, inTrack);
+
+ // Etched border
+ setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
+ );
+ setLayout(new BorderLayout());
+
+ // GUI components
+ JPanel subPanel = new JPanel();
+ subPanel.setLayout(new BorderLayout(10, 4));
+ subPanel.add(new JLabel(I18nManager.getText("dialog.exportpov.baseimage") + ": "), BorderLayout.WEST);
+ _baseImageLabel = new JLabel("Typical sourcename");
+ subPanel.add(_baseImageLabel, BorderLayout.CENTER);
+ _editButton = new JButton(I18nManager.getText("button.edit"));
+ _editButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ changeBaseImage();
+ }
+ });
+ subPanel.add(_editButton, BorderLayout.EAST);
+ add(subPanel, BorderLayout.NORTH);
+ }
+
+ /**
+ * @param inDefinition image definition from interactive step
+ */
+ public void initImageParameters(ImageDefinition inDefinition)
+ {
+ _baseImageConfig.setImageDefinition(inDefinition);
+ }
+
+ /**
+ * Change the base image by calling the BaseImageConfigDialog
+ */
+ private void changeBaseImage()
+ {
+ // Check if there is a cache to use
+ if (BaseImageConfigDialog.isImagePossible())
+ {
+ // Show new dialog to choose image details
+ _baseImageConfig.begin();
+ }
+ // TODO: What if it isn't possible? Should the caller show the error message?
+ //else {
+ // _app.showErrorMessage(getNameKey(), "dialog.exportimage.noimagepossible");
+ //}
+ }
+
+ /**
+ * Callback from base image config dialog
+ */
+ public void baseImageChanged()
+ {
+ updateBaseImageDetails();
+ if (_parent != null) {
+ _parent.baseImageChanged();
+ }
+ }
+
+ /**
+ * Update the description label according to the selected base image details
+ */
+ public void updateBaseImageDetails()
+ {
+ String desc = null;
+ ImageDefinition imageDef = _baseImageConfig.getImageDefinition();
+ // Check if selected zoom level is suitable or not, if not then set image to no
+ if (imageDef.getUseImage() && !_baseImageConfig.isSelectedZoomValid()) {
+ imageDef.setUseImage(false, imageDef.getSourceIndex(), 5);
+ }
+ // Make a description for the label
+ if (imageDef.getUseImage())
+ {
+ MapSource source = MapSourceLibrary.getSource(imageDef.getSourceIndex());
+ if (source != null)
+ {
+ desc = source.getName() + " (" + imageDef.getZoom() + ")";
+ }
+ }
+ if (desc == null) {
+ desc = I18nManager.getText("dialog.about.no");
+ }
+ _baseImageLabel.setText(desc);
+ _editButton.setEnabled(BaseImageConfigDialog.isImagePossible());
+ }
+
+ /**
+ * @return the grouter object for reuse of the prepared images
+ */
+ public MapGrouter getGrouter()
+ {
+ return _baseImageConfig.getGrouter();
+ }
+
+ /**
+ * @return image definition
+ */
+ public ImageDefinition getImageDefinition() {
+ return _baseImageConfig.getImageDefinition();
+ }
+
+ /**
+ * @return true if any tiles were found
+ */
+ public boolean getFoundData() {
+ return _baseImageConfig.getFoundData();
+ }
+}
*/
public DecimalNumberField()
{
- super(6);
- setDocument(new DecimalNumberDocument(false));
+ this(false);
}
/**
private JPanel _playAudioPanel = null;
// Units
- private JComboBox _coordFormatDropdown = null;
- private JComboBox _distUnitsDropdown = null;
+ private JComboBox<String> _coordFormatDropdown = null;
+ private JComboBox<String> _distUnitsDropdown = null;
// Cached labels
private static final String LABEL_POINT_SELECTED = I18nManager.getText("details.index.selected") + ": ";
lowerPanel.add(coordFormatLabel);
String[] coordFormats = {I18nManager.getText("units.original"), I18nManager.getText("units.degminsec"),
I18nManager.getText("units.degmin"), I18nManager.getText("units.deg")};
- _coordFormatDropdown = new JComboBox(coordFormats);
+ _coordFormatDropdown = new JComboBox<String>(coordFormats);
_coordFormatDropdown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
unitsLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
lowerPanel.add(unitsLabel);
// Make dropdown for distance units
- _distUnitsDropdown = new JComboBox();
+ _distUnitsDropdown = new JComboBox<String>();
final UnitSet currUnits = Config.getUnitSet();
for (int i=0; i<UnitSetLibrary.getNumUnitSets(); i++) {
_distUnitsDropdown.addItem(I18nManager.getText(UnitSetLibrary.getUnitSet(i).getDistanceUnit().getNameKey()));
_rangeLabel.setText(LABEL_RANGE_SELECTED
+ (selection.getStart()+1) + " " + I18nManager.getText("details.range.to")
+ " " + (selection.getEnd()+1));
- _distanceLabel.setText(LABEL_RANGE_DISTANCE + DisplayUtils.roundedNumber(selection.getDistance()) + " " + distUnitsStr);
- if (selection.getNumSeconds() > 0)
+ _distanceLabel.setText(LABEL_RANGE_DISTANCE + DisplayUtils.roundedNumber(selection.getMovingDistance()) + " " + distUnitsStr);
+ final long numMovingSeconds = selection.getMovingSeconds();
+ if (numMovingSeconds > 0L)
{
- _durationLabel.setText(LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(selection.getNumSeconds()));
+ _durationLabel.setText(LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(numMovingSeconds));
_aveSpeedLabel.setText(I18nManager.getText("details.range.avespeed") + ": "
- + DisplayUtils.roundedNumber(selection.getDistance()/selection.getNumSeconds()*3600.0) + " " + speedUnitsStr);
+ + DisplayUtils.roundedNumber(selection.getMovingDistance()/numMovingSeconds*3600.0) + " " + speedUnitsStr);
}
else {
_durationLabel.setText("");
--- /dev/null
+package tim.prune.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+
+import tim.prune.I18nManager;
+import tim.prune.function.Cancellable;
+
+/**
+ * Class to show a progress dialog for various time-consuming functions
+ */
+public class GenericProgressDialog
+{
+ private JDialog _progressDialog = null;
+ private String _dialogTitleKey = null;
+ private String _labelKey = null;
+ private JProgressBar _progressBar = null;
+ private JFrame _parentFrame = null;
+ private Cancellable _function = null;
+
+ /**
+ * Constructor
+ * @param inTitleKey key for dialog title text
+ * @param inLabelKey key for label text
+ * @param inParentFrame parent frame for creating dialog
+ * @param inFunction function which can be cancelled
+ */
+ public GenericProgressDialog(String inTitleKey, String inLabelKey,
+ JFrame inParentFrame, Cancellable inFunction)
+ {
+ _dialogTitleKey = inTitleKey;
+ _labelKey = inLabelKey;
+ if (_labelKey == null) {
+ _labelKey = "confirm.running";
+ }
+ _parentFrame = inParentFrame;
+ _function = inFunction;
+ }
+
+ /**
+ * Create the dialog to show the progress
+ */
+ private void createProgressDialog()
+ {
+ _progressDialog = new JDialog(_parentFrame, I18nManager.getText(_dialogTitleKey));
+ _progressDialog.setLocationRelativeTo(_parentFrame);
+ _progressBar = new JProgressBar(0, 100);
+ _progressBar.setValue(0);
+ _progressBar.setStringPainted(true);
+ _progressBar.setString("");
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
+ panel.add(new JLabel(I18nManager.getText(_labelKey)));
+ panel.add(_progressBar);
+ panel.add(Box.createVerticalStrut(6)); // spacer
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ _function.cancel();
+ }
+ });
+ panel.add(cancelButton);
+ _progressDialog.getContentPane().add(panel);
+ _progressDialog.pack();
+ _progressDialog.setVisible(true);
+ }
+
+ /**
+ * Show the dialog in indeterminate mode, before limits are calculated
+ */
+ public void show()
+ {
+ if (_progressDialog == null)
+ {
+ createProgressDialog();
+ _progressBar.setIndeterminate(true);
+ }
+ }
+
+ /**
+ * Update the progress bar
+ * @param inCurrent current value
+ * @param inMax maximum value
+ */
+ public void showProgress(int inCurrent, int inMax)
+ {
+ if (_progressDialog == null)
+ createProgressDialog();
+ if (_progressBar.isIndeterminate())
+ _progressBar.setIndeterminate(false);
+ if (inMax > 0)
+ _progressBar.setMaximum(inMax);
+ _progressBar.setValue(inCurrent);
+ _progressBar.setString("" + inCurrent + " / " + _progressBar.getMaximum());
+ }
+
+ /**
+ * Close the dialog
+ */
+ public void close()
+ {
+ if (_progressDialog != null)
+ _progressDialog.dispose();
+ }
+}
/**
* Class to act as list model for the photo list and audio list
*/
-public class MediaListModel extends AbstractListModel
+public class MediaListModel extends AbstractListModel<String>
{
/** media list */
MediaList _media = null;
/**
* @see javax.swing.ListModel#getElementAt(int)
*/
- public Object getElementAt(int inIndex)
+ public String getElementAt(int inIndex)
{
MediaObject m = _media.getMedia(inIndex);
// * means modified since loading
private JMenuItem _addAltitudeOffsetItem = null;
private JMenuItem _mergeSegmentsItem = null;
private JMenu _rearrangeMenu = null;
+ private JMenuItem _splitSegmentsItem = null;
+ private JMenuItem _sewSegmentsItem = null;
private JMenuItem _cutAndMoveItem = null;
private JMenuItem _convertNamesToTimesItem = null;
private JMenuItem _deleteFieldValuesItem = null;
private JMenuItem _getGpsiesItem = null;
private JMenuItem _uploadGpsiesItem = null;
private JMenuItem _lookupSrtmItem = null;
+ private JMenuItem _downloadSrtmItem = null;
private JMenuItem _lookupWikipediaItem = null;
private JMenuItem _downloadOsmItem = null;
+ private JMenuItem _getWeatherItem = null;
private JMenuItem _distanceItem = null;
private JMenuItem _fullRangeDetailsItem = null;
private JMenuItem _estimateTimeItem = null;
fileMenu.add(exitMenuItem);
menubar.add(fileMenu);
+ ////////////////////////////////////////////////////
+ // Online menu
+ JMenu onlineMenu = new JMenu(I18nManager.getText("menu.online"));
+ setAltKey(onlineMenu, "altkey.menu.online");
+ // SRTM
+ _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false);
+ onlineMenu.add(_lookupSrtmItem);
+ _downloadSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_SRTM, false);
+ onlineMenu.add(_downloadSrtmItem);
+ // Get gpsies tracks
+ _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false);
+ onlineMenu.add(_getGpsiesItem);
+ // Upload to gpsies
+ _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES, false);
+ onlineMenu.add(_uploadGpsiesItem);
+ onlineMenu.addSeparator();
+ _lookupWikipediaItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_WIKIPEDIA, false);
+ onlineMenu.add(_lookupWikipediaItem);
+ JMenuItem searchWikipediaNamesItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_WIKIPEDIA);
+ onlineMenu.add(searchWikipediaNamesItem);
+ _downloadOsmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_OSM, false);
+ onlineMenu.add(_downloadOsmItem);
+ onlineMenu.addSeparator();
+ _getWeatherItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_WEATHER_FORECAST, false);
+ onlineMenu.add(_getWeatherItem);
+ menubar.add(onlineMenu);
+
+ ////////////////////////////////////////////////////
// Track menu
JMenu trackMenu = new JMenu(I18nManager.getText("menu.track"));
setAltKey(trackMenu, "altkey.menu.track");
rearrangeNearestItem.setEnabled(true);
_rearrangeMenu.add(rearrangeNearestItem);
trackMenu.add(_rearrangeMenu);
- // Get gpsies tracks
- _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false);
- trackMenu.add(_getGpsiesItem);
- // Upload to gpsies
- _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES, false);
- trackMenu.add(_uploadGpsiesItem);
- _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false);
- trackMenu.add(_lookupSrtmItem);
- _lookupWikipediaItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_WIKIPEDIA, false);
- trackMenu.add(_lookupWikipediaItem);
- JMenuItem searchWikipediaNamesItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_WIKIPEDIA);
- trackMenu.add(searchWikipediaNamesItem);
- _downloadOsmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_OSM, false);
- trackMenu.add(_downloadOsmItem);
+ // Split track segments
+ _splitSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SPLIT_SEGMENTS, false);
+ trackMenu.add(_splitSegmentsItem);
+ // Sew track segments
+ _sewSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SEW_SEGMENTS, false);
+ trackMenu.add(_sewSegmentsItem);
trackMenu.addSeparator();
_learnEstimationParams = makeMenuItem(FunctionLibrary.FUNCTION_LEARN_ESTIMATION_PARAMS, false);
trackMenu.add(_learnEstimationParams);
_markRectangleItem.setEnabled(hasData);
_deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints());
_rearrangeMenu.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
+ _splitSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3);
+ _sewSegmentsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3);
_selectAllItem.setEnabled(hasData);
_selectNoneItem.setEnabled(hasData);
_show3dItem.setEnabled(hasMultiplePoints);
_lookupSrtmItem.setEnabled(hasData);
_lookupWikipediaItem.setEnabled(hasData);
_downloadOsmItem.setEnabled(hasData);
+ _getWeatherItem.setEnabled(hasData);
_findWaypointItem.setEnabled(hasData && _track.hasWaypoints());
+ // have we got a cache?
+ _downloadSrtmItem.setEnabled(hasData && Config.getConfigString(Config.KEY_DISK_CACHE) != null);
// is undo available?
boolean hasUndo = !_app.getUndoStack().isEmpty();
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
JPanel dialogPanel = new JPanel();
dialogPanel.setLayout(new BorderLayout());
dialogPanel.add(new JLabel(I18nManager.getText("confirm.running")), BorderLayout.NORTH);
+ // Centre panel with an empty border
+ JPanel centrePanel = new JPanel();
+ centrePanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+ centrePanel.setLayout(new BorderLayout());
_progressBar = new JProgressBar();
_progressBar.setPreferredSize(new Dimension(250, 30));
- dialogPanel.add(_progressBar, BorderLayout.CENTER);
+ centrePanel.add(_progressBar, BorderLayout.CENTER);
+ dialogPanel.add(centrePanel, BorderLayout.CENTER);
// Cancel button at the bottom
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
private int _visiblePanels = 1;
// Waypoints
private JPanel _waypointListPanel = null;
- private JList _waypointList = null;
+ private JList<String> _waypointList = null;
private WaypointListModel _waypointListModel = null;
// Photos
private JPanel _photoListPanel = null;
- private JList _photoList = null;
+ private JList<String> _photoList = null;
private MediaListModel _photoListModel = null;
// Audio files
private JPanel _audioListPanel = null;
- private JList _audioList = null;
+ private JList<String> _audioList = null;
private MediaListModel _audioListModel = null;
// scrollbar interval
BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
);
_waypointListModel = new WaypointListModel(_trackInfo.getTrack());
- _waypointList = new JList(_waypointListModel);
+ _waypointList = new JList<String>(_waypointListModel);
_waypointList.setVisibleRowCount(NUM_LIST_ENTRIES);
_waypointList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
_listsPanel.add(_waypointListPanel);
// photo list
_photoListModel = new MediaListModel(_trackInfo.getPhotoList());
- _photoList = new JList(_photoListModel);
+ _photoList = new JList<String>(_photoListModel);
_photoList.setVisibleRowCount(NUM_LIST_ENTRIES);
_photoList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
// List for audio clips
_audioListModel = new MediaListModel(_trackInfo.getAudioList());
- _audioList = new JList(_audioListModel);
+ _audioList = new JList<String>(_audioListModel);
_audioList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
{
* @param inList list object
* @return panel object
*/
- private static JPanel makeListPanel(String inNameKey, JList inList)
+ private static JPanel makeListPanel(String inNameKey, JList<String> inList)
{
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
--- /dev/null
+package tim.prune.gui;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import tim.prune.I18nManager;
+import tim.prune.threedee.TerrainDefinition;
+
+/**
+ * Gui component for defining the 3d terrain,
+ * including whether to use one or not, and if so
+ * what resolution to use for the grid
+ */
+public class TerrainDefinitionPanel extends JPanel
+{
+ /** Checkbox to use a terrain or not */
+ private JCheckBox _useCheckbox = null;
+ /** Field for entering the grid size */
+ private WholeNumberField _gridSizeField = null;
+
+
+ /**
+ * Constructor
+ */
+ public TerrainDefinitionPanel()
+ {
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ // Components
+ _useCheckbox = new JCheckBox(I18nManager.getText("dialog.3d.useterrain"));
+ _useCheckbox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ activateGridField();
+ }
+ });
+ add(_useCheckbox);
+ add(Box.createHorizontalGlue());
+ JLabel label = new JLabel(I18nManager.getText("dialog.3d.terraingridsize") + ": ");
+ add(label);
+ _gridSizeField = new WholeNumberField(4);
+ _gridSizeField.setValue(10);
+ _gridSizeField.setMaximumSize(new Dimension(100, 50));
+ _gridSizeField.setEnabled(false);
+ add(_gridSizeField);
+ }
+
+ /**
+ * @param inDefinition terrain parameters to set
+ */
+ public void initTerrainParameters(TerrainDefinition inDefinition)
+ {
+ _useCheckbox.setSelected(inDefinition != null && inDefinition.getUseTerrain());
+ if (inDefinition != null && inDefinition.getGridSize() > 0) {
+ _gridSizeField.setValue(inDefinition.getGridSize());
+ }
+ activateGridField();
+ }
+
+ /**
+ * @return true if the terrain is selected
+ */
+ public boolean getUseTerrain() {
+ return _useCheckbox.isSelected() && getGridSize() > 2;
+ }
+
+ /**
+ * @return number of nodes along each side of the grid
+ */
+ public int getGridSize() {
+ return _gridSizeField.getValue();
+ }
+
+ /**
+ * Set the grid field to be enabled or not based on the checkbox
+ */
+ private void activateGridField() {
+ _gridSizeField.setEnabled(_useCheckbox.isSelected());
+ }
+}
{
private App _app;
private JDialog _dialog;
- private JList _actionList;
+ private JList<String> _actionList;
/**
{
undoActions[i] = undoStack.elementAt(undoStack.size()-1-i).getDescription();
}
- _actionList = new JList(undoActions);
+ _actionList = new JList<String>(undoActions);
_actionList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
_actionList.setSelectedIndex(0);
_actionList.addListSelectionListener(new ListSelectionListener()
/**
* Class to act as list model for the waypoint list
*/
-public class WaypointListModel extends AbstractListModel
+public class WaypointListModel extends AbstractListModel<String>
{
Track _track = null;
ArrayList<DataPoint> _waypoints = null;
/**
* @see javax.swing.ListModel#getElementAt(int)
*/
- public Object getElementAt(int inIndex)
+ public String getElementAt(int inIndex)
{
DataPoint p = null;
if (inIndex < 0 || inIndex >= getSize()
* Class to deal with the matching of waypoint names
* and the representation in a list
*/
-public class WaypointNameMatcher extends AbstractListModel
+public class WaypointNameMatcher extends AbstractListModel<String>
{
private ArrayList<DataPoint> _waypoints = null;
private int _numPoints = 0;
/**
* @see javax.swing.ListModel#getElementAt(int)
*/
- public Object getElementAt(int inIndex)
+ public String getElementAt(int inIndex)
{
return _matches.get(inIndex).getWaypointName();
}
try {
image = Toolkit.getDefaultToolkit().createImage(tileFile.getAbsolutePath());
}
- catch (Exception e) {}
+ catch (Exception e) {
+ System.err.println("createImage: " + e.getClass().getName() + " _ " + e.getMessage());
+ }
}
}
return image;
private static boolean isBeingLoaded(File inFile)
{
File tempFile = new File(inFile.getAbsolutePath() + ".temp");
- return tempFile.exists();
+ if (!tempFile.exists()) {
+ return false;
+ }
+ // File exists, so check if it was created recently
+ final long fileAge = System.currentTimeMillis() - tempFile.lastModified();
+ return fileAge < 1000000L; // overwrite if the temp file is still there after 1000s
}
/**
// Use a synchronized block across all threads to make sure this url is only fetched once
synchronized (DiskTileCacher.class)
{
- if (tempFile.exists()) {return;}
+ if (tempFile.exists()) {tempFile.delete();}
try {
if (!tempFile.createNewFile()) {return;}
}
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSlider;
+import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import tim.prune.function.edit.FieldEdit;
import tim.prune.function.edit.FieldEditList;
import tim.prune.gui.IconManager;
+import tim.prune.tips.TipManager;
/**
* Class for the map canvas, to display a background map and draw on it
*/
public class MapCanvas extends JPanel implements MouseListener, MouseMotionListener, DataSubscriber,
- KeyListener, MouseWheelListener
+ KeyListener, MouseWheelListener, TileConsumer
{
/** App object for callbacks */
private App _app = null;
add(_scaleBar, BorderLayout.SOUTH);
// Make popup menu
makePopup();
+ // Get currently selected map from Config, pass to MapTileManager
+ _tileManager.setMapSource(Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX));
}
g.fillRect(0, 0, getWidth(), getHeight());
// Check whether maps are on or not
- boolean showMap = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
+ final boolean showMap = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
_mapCheckBox.setSelected(showMap);
+ // Check whether disk cache is on or not
+ final boolean usingDiskCache = Config.getConfigString(Config.KEY_DISK_CACHE) != null;
+ // Show tip to recommend setting up a cache
+ if (showMap && !usingDiskCache && Config.getConfigBoolean(Config.KEY_ONLINE_MODE))
+ {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ _app.showTip(TipManager.Tip_UseAMapCache);
+ }
+ });
+ }
// reset error message
if (!showMap) {_shownOsmErrorAlready = false;}
// Loop over layers
for (int l=0; l<numLayers; l++)
{
- Image image = _tileManager.getTile(l, tileX, tileY);
+ Image image = _tileManager.getTile(l, tileX, tileY, true);
if (image != null) {
g.drawImage(image, x, y, 256, 256, null);
}
* Inform that tiles have been updated and the map can be repainted
* @param inIsOk true if data loaded ok, false for error
*/
- public synchronized void tilesUpdated(boolean inIsOk)
+ public void tilesUpdated(boolean inIsOk)
{
- // Show message if loading failed (but not too many times)
- if (!inIsOk && !_shownOsmErrorAlready && _mapCheckBox.isSelected())
+ synchronized(this)
{
- _shownOsmErrorAlready = true;
- // use separate thread to show message about failing to load osm images
- new Thread(new Runnable() {
- public void run() {
- try {Thread.sleep(500);} catch (InterruptedException ie) {}
- _app.showErrorMessage("error.osmimage.dialogtitle", "error.osmimage.failed");
- }
- }).start();
+ // Show message if loading failed (but not too many times)
+ if (!inIsOk && !_shownOsmErrorAlready && _mapCheckBox.isSelected())
+ {
+ _shownOsmErrorAlready = true;
+ // use separate thread to show message about failing to load osm images
+ new Thread(new Runnable() {
+ public void run() {
+ try {Thread.sleep(500);} catch (InterruptedException ie) {}
+ _app.showErrorMessage("error.osmimage.dialogtitle", "error.osmimage.failed");
+ }
+ }).start();
+ }
+ _recalculate = true;
+ repaint();
}
- _recalculate = true;
- repaint();
}
/**
_app.setCurrentMode(App.AppMode.NORMAL);
_drawMode = MODE_DEFAULT;
// Call a function to mark the points
- MarkPointsInRectangleFunction marker = new MarkPointsInRectangleFunction(_app);
+ MarkPointsInRectangleFunction marker = (MarkPointsInRectangleFunction) FunctionLibrary.FUNCTION_MARK_IN_RECTANGLE;
double lon1 = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_dragFromX, getWidth()));
double lat1 = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(_dragFromY, getHeight()));
double lon2 = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_dragToX, getWidth()));
_checkBounds = true;
}
if ((inUpdateType & DataSubscriber.MAPSERVER_CHANGED) > 0) {
- _tileManager.resetConfig();
+ // Get the selected map source index and pass to tile manager
+ _tileManager.setMapSource(Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX));
}
if ((inUpdateType & (DataSubscriber.DATA_ADDED_OR_REMOVED + DataSubscriber.DATA_EDITED)) > 0) {
_midpoints.updateData(_track);
import tim.prune.config.Config;
+
/**
* Class responsible for managing the map tiles,
* including invoking the correct memory cacher(s) and/or disk cacher(s)
*/
public class MapTileManager implements ImageObserver
{
- /** Parent object to inform when tiles received */
- private MapCanvas _parent = null;
+ /** Consumer object to inform when tiles received */
+ private TileConsumer _consumer = null;
/** Current map source */
private MapSource _mapSource = null;
/** Array of tile caches, one per layer */
private MemTileCacher[] _tempCaches = null;
+ /** Flag for whether to download any tiles or just pull from disk */
+ private boolean _downloadTiles = true;
+ /** Flag for whether to return incomplete images or just pass to tile cache until they're done */
+ private boolean _returnIncompleteImages = false;
/** Number of layers */
private int _numLayers = -1;
/** Current zoom level */
/**
* Constructor
- * @param inParent parent canvas to be informed of updates
+ * @param inConsumer consumer object to be notified
*/
- public MapTileManager(MapCanvas inParent)
+ public MapTileManager(TileConsumer inConsumer)
{
- _parent = inParent;
- // Adjust the index of the selected map source
- adjustSelectedMap();
- resetConfig();
+ _consumer = inConsumer;
}
/**
*/
public void centreMap(int inZoom, int inTileX, int inTileY)
{
- _zoom = inZoom;
- // Calculate number of tiles = 2^^zoom
- _numTileIndices = 1 << _zoom;
+ setZoom(inZoom);
// Pass params onto all memory cachers
if (_tempCaches != null) {
for (int i=0; i<_tempCaches.length; i++) {
}
}
+ /** @param inZoom zoom level to set */
+ public void setZoom(int inZoom)
+ {
+ _zoom = inZoom;
+ // Calculate number of tiles = 2^^zoom
+ _numTileIndices = 1 << _zoom;
+ }
+
/**
* @return true if zoom is too high for tiles
*/
return (_zoom > maxZoom);
}
+ /**
+ * Enable or disable tile downloading
+ * @param inEnabled true to enable downloading, false to just get tiles from disk
+ */
+ public void enableTileDownloading(boolean inEnabled)
+ {
+ _downloadTiles = inEnabled;
+ }
+
+ /** Configure to return incomplete images instead of going via caches (and another call) */
+ public void setReturnIncompleteImages()
+ {
+ _returnIncompleteImages = true;
+ }
+
/**
* Clear all the memory caches due to changed config / zoom
*/
}
/**
- * Reset the map source configuration, apparently it has changed
+ * @param inSourceNum selected map source index
*/
- public void resetConfig()
+ public void setMapSource(int inSourceNum)
{
- int sourceNum = Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX);
- _mapSource = MapSourceLibrary.getSource(sourceNum);
- if (_mapSource == null) {_mapSource = MapSourceLibrary.getSource(0);}
- clearMemoryCaches();
- _numLayers = _mapSource.getNumLayers();
+ setMapSource(MapSourceLibrary.getSource(inSourceNum));
}
/**
- * Adjust the index of the selected map
- * (only required if config was loaded from a previous version of GpsPrune)
+ * @param inMapSource selected map source
*/
- private void adjustSelectedMap()
+ public void setMapSource(MapSource inMapSource)
{
- int sourceNum = Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX);
- int prevNumFixed = Config.getConfigInt(Config.KEY_NUM_FIXED_MAPS);
- // Number of fixed maps not specified in version <=13, default to 6
- if (prevNumFixed == 0) prevNumFixed = 6;
- int currNumFixed = MapSourceLibrary.getNumFixedSources();
- // Only need to do something if the number has changed
- if (currNumFixed != prevNumFixed && (sourceNum >= prevNumFixed || sourceNum >= currNumFixed))
- {
- sourceNum += (currNumFixed - prevNumFixed);
- Config.setConfigInt(Config.KEY_MAPSOURCE_INDEX, sourceNum);
- }
- Config.setConfigInt(Config.KEY_NUM_FIXED_MAPS, currNumFixed);
+ _mapSource = inMapSource;
+ if (_mapSource == null) {_mapSource = MapSourceLibrary.getSource(0);}
+ clearMemoryCaches();
+ _numLayers = _mapSource.getNumLayers();
}
/**
}
/**
+ * Get a tile from the currently selected map source
* @param inLayer layer number, starting from 0
* @param inX x index of tile
* @param inY y index of tile
+ * @param inDownloadIfNecessary true to download the file if it's not available
* @return selected tile if already loaded, or null otherwise
*/
- public Image getTile(int inLayer, int inX, int inY)
+ public Image getTile(int inLayer, int inX, int inY, boolean inDownloadIfNecessary)
{
if (inY < 0 || inY >= _numTileIndices) return null;
// Wrap tile indices which are too big or too small
inX = ((inX % _numTileIndices) + _numTileIndices) % _numTileIndices;
// Check first in memory cache for tile
- MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
- Image tile = tempCache.getTile(inX, inY);
- if (tile != null) {
- return tile;
+ Image tile = null;
+ MemTileCacher tempCache = null;
+ if (_tempCaches != null)
+ {
+ tempCache = _tempCaches[inLayer]; // Should probably guard array indexes here
+ tile = tempCache.getTile(inX, inY);
+ if (tile != null) {
+ return tile;
+ }
}
// Tile wasn't in memory, but maybe it's in disk cache (if there is one)
tile = DiskTileCacher.getTile(diskCachePath, _mapSource.makeFilePath(inLayer, _zoom, inX, inY), onlineMode);
if (tile != null)
{
+ if (_returnIncompleteImages) {return tile;}
// Pass tile to memory cache
- tempCache.setTile(tile, inX, inY, _zoom);
+ if (tempCache != null) {
+ tempCache.setTile(tile, inX, inY, _zoom);
+ }
if (tile.getWidth(this) > 0) {return tile;}
return null;
}
+ // else System.out.println("DTC gave null tile for " + _zoom + ", " + inX + ", " + inY);
}
// Tile wasn't in memory or on disk, so if online let's get it
- if (onlineMode)
+ if (onlineMode && _downloadTiles && inDownloadIfNecessary)
{
try
{
URL tileUrl = new URL(_mapSource.makeURL(inLayer, _zoom, inX, inY));
+ // System.out.println("Going to fetch: " + tileUrl);
if (useDisk && DiskTileCacher.saveTile(tileUrl, diskCachePath,
_mapSource.makeFilePath(inLayer, _zoom, inX, inY), this))
{
else
{
// Load image asynchronously, using observer
- // tile = Toolkit.getDefaultToolkit().createImage(tileUrl);
// In order to set the http user agent, need to use a TileDownloader instead
TileDownloader.triggerLoad(this, tileUrl, inLayer, inX, inY, _zoom);
}
boolean loaded = (infoflags & ImageObserver.ALLBITS) > 0;
boolean error = (infoflags & ImageObserver.ERROR) > 0;
if (loaded || error) {
- _parent.tilesUpdated(loaded);
+ _consumer.tilesUpdated(loaded);
}
return !loaded;
}
*/
public void notifyImageLoaded(Image inTile, int inLayer, int inX, int inY, int inZoom)
{
- if (inTile != null)
+ if (inTile != null && _tempCaches != null)
{
MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
if (tempCache.getTile(inX, inY) == null)
inTile.getWidth(this); // trigger imageUpdate when image is ready
}
}
+ else if (inTile != null) {
+ inTile.getWidth(this);
+ }
}
}
--- /dev/null
+package tim.prune.gui.map;
+
+/**
+ * Interface used by the MapTileManager to communicate back to its consumers
+ */
+public interface TileConsumer
+{
+ /** Let the consumer know that the tiles have been updated */
+ public void tilesUpdated(boolean inIsOk);
+}
if (inManager != null && inUrl != null)
{
String url = inUrl.toString();
+ // System.out.println("Trigger load: " + url);
if (!BLOCKED_URLS.contains(url) && !LOADING_URLS.contains(url))
{
+ // System.out.println("Not blocked: " + url);
LOADING_URLS.add(url);
new Thread(new TileDownloader(inManager, inUrl, inLayer, inX, inY, inZoom)).start();
}
InputStream in = null;
try
{
+ // System.out.println("TD Running thread to get: " + _url.toString());
// Set http user agent on connection
URLConnection conn = _url.openConnection();
conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
dialog.correlate.select.photoname=Foto naam
dialog.correlate.select.timediff=Tyd verskil
dialog.correlate.select.photolater=Foto later
-dialog.correlate.options.tip=Wenk: Deur een item te verbind, kan die tyd afset bereken word vir jou.
+tip.title=Wenk
+tip.manuallycorrelateone=Deur een item te verbind, kan die tyd afset bereken word vir jou.
dialog.correlate.options.intro=Seleketeer die opsies vir automatiese korrelasie
dialog.correlate.options.offsetpanel=Tyd afset
dialog.correlate.options.offset=Afset
dialog.pastecoordinates.desc=Sleutel of plak die koordinate hier
dialog.pastecoordinates.coords=Koordinate
dialog.pastecoordinates.nothingfound=Gaan asseblief koordinate na en probeer weer
-dialog.help.help=Sien asseblief\n http://activityworkshop.net/software/gpsprune/\n vir meer inligting en gebruikers handleidings.
+dialog.help.help=Sien asseblief\n http://gpsprune.activityworkshop.net/\n vir meer inligting en gebruikers handleidings.
dialog.about.version=Weergawe
dialog.about.build=Bou
dialog.about.summarytext1=GpsPrune is 'n program vir die laai, vertoon en wysiging van data vanaf GPS ontvangers.
fieldname.custom=Persoonlike
fieldname.prefix=Veld
fieldname.distance=Afstand
-fieldname.movingdistance=Beweeg afstand
fieldname.duration=Tydperk
fieldname.speed=Spoed
fieldname.verticalspeed=Vertikale spoed
error.saveexif.filenotfound=Find van foto het misluk
error.saveexif.cannotoverwrite1=Foto leer
error.saveexif.cannotoverwrite2=is lees-alleen en kan nie oorskruif word nie. Skyf na kopie?
-error.saveexif.failed1=Stoor het misluk
-error.saveexif.failed2=van die beelde
-error.saveexif.forced2=van die beelde vereis vorsering
+error.saveexif.failed=Stoor het misluk %d van die beelde
+error.saveexif.forced=%d van die beelde vereis vorsering
error.load.dialogtitle=Fout met laai van data
error.load.noread=Kan nie leer lees
error.load.nopoints=Geen koordinaat informasie gevind in the leer
menu.file.recentfiles=Naposledy otev\u0159en\u00e9
menu.file.save=Ulo\u017eit jako text
menu.file.exit=Konec
+menu.online=Online
menu.track=Stopa
menu.track.undo=Undo
menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo
# Alt keys for menus
altkey.menu.file=S
+altkey.menu.online=O
altkey.menu.track=T
altkey.menu.range=R
altkey.menu.point=B
function.learnestimationparams=Anal\u00fdza stopy pro odhad \u010dasu
function.setmapbg=Nastavit pozad\u00ed
function.setpaths=Nastavit cestu k program\u016fm
+function.splitsegments=Rozd\u011blit stopu na \u010d\u00e1sti
+function.sewsegments=Spojit \u010d\u00e1sti stopy
function.getgpsies=St\u00e1hnout stopy z Gpsies
function.uploadgpsies=Nahr\u00e1t stopu na Gpsies
function.lookupsrtm=Na\u010d\u00edst nadm. v\u00fd\u0161ku ze SRTM
+function.downloadsrtm=St\u00e1hnout dla\u017edice ze SRTM
function.getwikipedia=Hledat na Wikipedii podle vzd\u00e1lenosti
function.searchwikipedianames=Hledat na Wikipedii podle jm\u00e9na
function.downloadosm=St\u00e1hnout data OSM pro oblast
function.saveconfig=Ulo\u017eit nastaven\u00ed
function.diskcache=Ulo\u017eit mapy na disk
function.managetilecache=Upravit cache map
+function.getweatherforecast=St\u00e1hnout p\u0159edpov\u011b\u010f po\u010das\u00ed
# Dialogs
dialog.exit.confirm.title=Ukon\u010dit GpsPrune
dialog.exportpov.ballsandsticks=Koule a ty\u010dky
dialog.exportpov.tubesandwalls=Roury a st\u011bny
dialog.3d.warningtracksize=Tato stopa sest\u00e1v\u00e1 z mnoha bod\u016f, kter\u00e9 mo\u017en\u00e1 Java3D nebude um\u011bt zobrazit.\nOpravdu chcete pokra\u010dovat?
+dialog.3d.useterrain=Zobrazit ter\u00e9n
+dialog.3d.terraingridsize=Rozli\u0161en\u00ed ter\u00e9nu
dialog.exportpov.baseimage=Obr\u00e1zek jako podklad
dialog.exportpov.cannotmakebaseimage=Nelze zapsat podklad
dialog.baseimage.title=Podklad
dialog.exportsvg.theta=V\u00fd\u0161kov\u00fd \u00fahel \u03b8
dialog.exportsvg.gradients=Vypl\u0148ovat body barevn\u00fdm p\u0159echodem
dialog.exportimage.noimagepossible=Aby bylo mo\u017en\u00e9 mapu ulo\u017eit jako obr\u00e1zek, je t\u0159eba st\u00e1hnout a ulo\u017eit dla\u017edice
-dialog.exportimage.drawtrack=Nakreslit stopu na mapu
+dialog.exportimage.drawtrack=Vykreslit linii stopy
+dialog.exportimage.drawtrackpoints=Vykreslit body stopy
dialog.exportimage.textscalepercent=Zv\u011bt\u0161en\u00ed fontu (%)
dialog.pointtype.desc=Ulo\u017eit body n\u00e1sleduj\u00edc\u00edch typ\u016f:
dialog.pointtype.track=Body stopy
dialog.correlate.select.photoname=N\u00e1zev fotografie
dialog.correlate.select.timediff=\u010casov\u00fd rozd\u00edl
dialog.correlate.select.photolater=Vyfoceno pozd\u011bji
-dialog.correlate.options.tip=Tip: kdy\u017e ru\u010dn\u011b slad\u00edte aspo\u0148 jednu fotografii, \u010dasov\u00fd posun bude vypo\u010d\u00edtat za v\u00e1s.
dialog.correlate.options.intro=Upravte mo\u017enosti automatick\u00e9ho slad\u011bn\u00ed
dialog.correlate.options.offsetpanel=\u010casov\u00fd posun
dialog.correlate.options.offset=Posun
dialog.compress.douglaspeucker.title=Douglasova-Peuckerova komprese
dialog.compress.douglaspeucker.paramdesc=Povolen\u00e1 odchylka
dialog.compress.summarylabel=Bod\u016f ke smaz\u00e1n\u00ed
-dialog.compress.confirm1=Bylo ozna\u010deno celkem
-dialog.compress.confirm2=bod\u016f.\nBody je mo\u017en\u00e9 smazat volbou Stopa->Smazat ozna\u010den\u00e9 body
+dialog.compress.confirm=Bylo ozna\u010deno celkem %d bod\u016f.\nStopa->Smazat ozna\u010den\u00e9 body?
dialog.compress.confirmnone=Nebyly vybr\u00e1ny \u017e\u00e1dn\u00e9 body.
dialog.deletemarked.nonefound=Nemohou b\u00fdt odstran\u011bny \u017e\u00e1dn\u00e9 body stopy
dialog.pastecoordinates.desc=Zadejte sou\u0159adnice
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Tato verze byla vyd\u00e1na
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Novou verzi m\u016f\u017eete st\u00e1hnout na adrese http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Novou verzi m\u016f\u017eete st\u00e1hnout na adrese http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=M\u00edsto my\u0161i m\u016f\u017eete pou\u017e\u00edvat n\u00e1sleduj\u00edc\u00ed kl\u00e1vesov\u00e9 zkratky
dialog.keys.keylist=<table><tr><td>\u0160ipky</td><td>Posunout mapu vlevo, vpravo, nahoru, dol\u016f</td></tr><tr><td>Ctrl + \u0161ipka vlevo, vpravo</td><td>Vybrat p\u0159edchoz\u00ed nebo n\u00e1sleduj\u00edc\u00ed bod</td></tr><tr><td>Ctrl + \u0161ipka nahoru, dol\u016f</td><td>P\u0159ibl\u00ed\u017eit, odd\u00e1lit</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Vybrat p\u0159edchoz\u00ed, n\u00e1sleduj\u00edc\u00ed \u010d\u00e1st stopy</td></tr><tr><td>Ctrl + Home, End</td><td>Vybrat prvn\u00ed, posledn\u00ed bod</td></tr><tr><td>Del</td><td>Smazat aktu\u00e1ln\u00ed bod</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.diskcache.deleteold=Smazat star\u00e9 soubory
dialog.diskcache.maximumage=Ponechat maxim\u00e1ln\u011b dn\u00ed
dialog.diskcache.deleteall=Smazat v\u0161echny soubory
-dialog.diskcache.deleted1=Smaz\u00e1no
-dialog.diskcache.deleted2=soubor\u016f z cache
+dialog.diskcache.deleted=Smaz\u00e1no %d soubor\u016f z cache
dialog.deletefieldvalues.intro=Vyberte pole, kter\u00e9 se m\u00e1 z aktu\u00e1ln\u00edho rozmez\u00ed odstranit
dialog.deletefieldvalues.nofields=V tomto rozmez\u00ed nelze smazat \u017e\u00e1dn\u00e9 pole
dialog.setlinewidth.text=Zvolte tlou\u0161\u0165ku \u010d\u00e1ry, kterou se nakresl\u00ed stopa (1-4)
dialog.downloadosm.desc=Potvr\u010fte, \u017ee se maj\u00ed k dan\u00e9 oblasti st\u00e1hnout data OSM:
dialog.searchwikipedianames.search=Vyhledat:
+dialog.weather.location=M\u00edsto
+dialog.weather.update=Posledn\u00ed aktualizace
+dialog.weather.sunrise=V\u00fdchod slunce
+dialog.weather.sunset=Z\u00e1pad slunce
+dialog.weather.temperatureunits=Stupn\u011b
+dialog.weather.currentforecast=Aktu\u00e1ln\u00ed po\u010das\u00ed
+dialog.weather.dailyforecast=P\u0159edpov\u011b\u010f co den
+dialog.weather.3hourlyforecast=P\u0159edpov\u011b\u010f co t\u0159i hodiny
+dialog.weather.day.now=Te\u010f
+dialog.weather.day.today=Dnes
+dialog.weather.day.tomorrow=Z\u00edtra
+dialog.weather.day.monday=Pond\u011bl\u00ed
+dialog.weather.day.tuesday=\u00dater\u00fd
+dialog.weather.day.wednesday=St\u0159eda
+dialog.weather.day.thursday=\u010ctvrtek
+dialog.weather.day.friday=P\u00e1tek
+dialog.weather.day.saturday=Sobota
+dialog.weather.day.sunday=Ned\u011ble
+dialog.weather.creditnotice=Data poskytuje slu\u017eba openweathermap.org, v\u00edce informac\u00ed na t\u00e9to adrese.
# 3d window
dialog.3d.title=Trojrozm\u011brn\u00e9 zobrazen\u00ed GpsPrune
confirm.addaltitudeoffset=V\u00fd\u0161kov\u00fd posun zm\u011bn\u011bn
confirm.rearrangewaypoints=Body p\u0159euspo\u0159\u00e1d\u00e1ny
confirm.rearrangephotos=Fotografie p\u0159euspo\u0159\u00e1d\u00e1ny
+confirm.splitsegments=\u00dasp\u011b\u0161n\u011b rozd\u011bleno v %d bodech
+confirm.sewsegments=\u00dasp\u011b\u0161n\u011b spojeno v %d bodech
confirm.cutandmove=V\u00fdb\u011br p\u0159esunut
confirm.interpolate=Body p\u0159id\u00e1ny
confirm.convertnamestotimes=N\u00e1zvy bod\u016f p\u0159evedeny
-confirm.saveexif.ok1=Ulo\u017eeno
-confirm.saveexif.ok2=fotografi\u00ed
+confirm.saveexif.ok=Ulo\u017eeno %d fotografi\u00ed
confirm.undo.single=operace vr\u00e1cena
confirm.undo.multi=operac\u00ed vr\u00e1ceno
confirm.jpegload.single=fotografie p\u0159id\u00e1na
confirm.createpoint=bod vytvo\u0159en
confirm.rotatephoto=fotografie oto\u010dena
confirm.running=Prob\u00edh\u00e1 ...
-confirm.lookupsrtm1=Nalezeno
-confirm.lookupsrtm2=v\u00fd\u0161kov\u00fdch hodnot
+confirm.lookupsrtm=Nalezeno %d v\u00fd\u0161kov\u00fdch hodnot
+confirm.downloadsrtm=Do cache bylo sta\u017eeno %d soubor\u016f
+confirm.downloadsrtm.none=\u017d\u00e1dn\u00fd soubor nebylo t\u0159eba stahovat, v\u0161e u\u017e je v cachi.
confirm.deletefieldvalues=Hodnoty pole smaz\u00e1ny
confirm.audioload=Audionahr\u00e1vky p\u0159id\u00e1ny
confirm.correlateaudios.single=Audionahr\u00e1vka slad\u011bna
confirm.correlateaudios.multi=Audionahr\u00e1vky slad\u011bny
+# Tips, shown just once when appropriate
+tip.title=Tip
+tip.useamapcache=Kdy\u017e nastav\u00edte odkl\u00e1dac\u00ed prostor na disku \u010dili cache (Nastaven\u00ed -> Ulo\u017eit mapy na disk),\nzrychl\u00ed se zobrazov\u00e1n\u00ed a zmen\u0161\u00ed se mno\u017estv\u00ed p\u0159en\u00e1\u0161en\u00fdch dat.
+tip.learntimeparams=V\u00fdsledky budou p\u0159esn\u011bj\u0161\u00ed, kdy\u017e na na\u010dten\u00e9 stopy pou\u017eijete funkci\nAnal\u00fdza stopy pro odhad \u010dasu.
+tip.downloadsrtm=Na\u010d\u00edt\u00e1n\u00ed nadmo\u0159sk\u00fdch v\u00fd\u0161ek bude rychlej\u0161\u00ed, pokud st\u00e1hnete dla\u017edice do cache pomoc\u00ed\nOnline -> St\u00e1hnout dla\u017edice ze SRTM.
+tip.usesrtmfor3d=Tato stopa neobsahuje informaci o nadmo\u0159sk\u00e9 v\u00fd\u0161ce.\nPro na\u010dten\u00ed p\u0159ibli\u017en\u00fdch nadmo\u0159sk\u00fdch v\u00fd\u0161ek pot\u0159ebn\u00fdch\nna trojrozm\u011brn\u00e9 zobrazen\u00ed m\u016f\u017eete pou\u017e\u00edt funkce SRTM.
+tip.manuallycorrelateone=Kdy\u017e ru\u010dn\u011b slad\u00edte aspo\u0148 jednu fotografii, \u010dasov\u00fd posun bude vypo\u010d\u00edtat za v\u00e1s.
+
# Buttons
button.ok=OK
button.back=Zp\u011bt
button.no=Ne
button.yestoall=Ano u v\u0161eho
button.notoall=Ne u v\u0161eho
+button.always=V\u017edy
button.select=Vybrat
button.selectall=Vybrat v\u0161e
button.selectnone=Zru\u0161it v\u00fdb\u011br
fieldname.custom=Vlastn\u00ed
fieldname.prefix=Pole
fieldname.distance=Vzd\u00e1lenost
-fieldname.movingdistance=Najeto
fieldname.duration=Celkov\u00fd \u010das
fieldname.speed=Rychlost
fieldname.verticalspeed=Vertik. rychlost
units.degmin=Deg-min
units.deg=Stupn\u011b
units.iso8601=ISO 8601
+units.degreescelsius=Celsia
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheita
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=a
# External urls
url.googlemaps=maps.google.cz
wikipedia.lang=cs
+openweathermap.lang=en
# Cardinals for 3d plots
cardinal.n=N
undo.insert=vlo\u017eit body
undo.reverse=obr\u00e1tit rozmez\u00ed
undo.mergetracksegments=slou\u010dit \u010d\u00e1sti stopy
+undo.splitsegments=rozd\u011blit stopu na \u010d\u00e1sti
+undo.sewsegments=spojit \u010d\u00e1sti stopy
undo.addtimeoffset=p\u0159idat \u010dasov\u00fd posun
undo.addaltitudeoffset=p\u0159idat v\u00fd\u0161kov\u00fd posun
undo.rearrangewaypoints=p\u0159euspo\u0159\u00e1dat body
error.saveexif.filenotfound=Soubor s fotografi\u00ed nenalezen
error.saveexif.cannotoverwrite1=Soubor
error.saveexif.cannotoverwrite2=je jen ke \u010dten\u00ed a nelze ho p\u0159epsat. Ulo\u017eit do kopie?
-error.saveexif.failed1=Nepoda\u0159ilo se ulo\u017eit
-error.saveexif.failed2=fotografi\u00ed
-error.saveexif.forced1=P\u0159i ukl\u00e1d\u00e1n\u00ed
-error.saveexif.forced2=fotografi\u00ed do\u0161lo k nepodstatn\u00e9 chyb\u011b
+error.saveexif.failed=Nepoda\u0159ilo se ulo\u017eit %d fotografi\u00ed
+error.saveexif.forced=P\u0159i ukl\u00e1d\u00e1n\u00ed %d fotografi\u00ed do\u0161lo k nepodstatn\u00e9 chyb\u011b
error.load.dialogtitle=Chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed
error.load.noread=Nelze na\u010d\u00edst soubor
error.load.nopoints=V souboru nenalezeny \u017e\u00e1dn\u00e9 informace o sou\u0159adnic\u00edch
error.cache.cannotdelete=Nelze smazat soubory map.
error.interpolate.invalidparameter=Po\u010det bod\u016f mus\u00ed b\u00fdt mezi 1 a 1000
error.learnestimationparams.failed=Na z\u00e1klad\u011b t\u00e9to stopy nelze vypo\u010d\u00edtat parametr.\nZkuste jin\u00e9 stopy.
+error.tracksplit.nosplit=Stopu nen\u00ed mo\u017en\u00e9 rozd\u011blit
+error.downloadsrtm.nocache=Nepoda\u0159ilo se ulo\u017eit soubory.\nPros\u00edm ov\u011b\u0159te diskovou cache.
+error.sewsegments.nothingdone=Nelze spojit \u010d\u00e1sti stopy dohromady.\nStopa se skl\u00e1d\u00e1 z %d \u010d\u00e1st\u00ed.
menu.file.recentfiles=Zuletzt verwendete Dateien
menu.file.save=Als Text speichern
menu.file.exit=Beenden
+menu.online=Online
menu.track=Track
menu.track.undo=R\u00fcckg\u00e4ngig
menu.track.clearundo=Liste der letzten \u00c4nderungen l\u00f6schen
# Alt keys for menus
altkey.menu.file=D
+altkey.menu.online=O
altkey.menu.track=T
altkey.menu.range=B
altkey.menu.point=P
function.learnestimationparams=Zeitparameter erlernen
function.setmapbg=Karte Hintergrund setzen
function.setpaths=Programmpfade setzen
+function.splitsegments=In Trackabschnitte schneiden
+function.sewsegments=Trackabschnitte zusammenf\u00fcgen
function.getgpsies=Tracks bei GPSies.com herunterladen
function.uploadgpsies=Track zu GPSies.com hochladen
-function.lookupsrtm=H\u00f6hendaten von SRTM herunterladen
+function.lookupsrtm=H\u00f6hendaten von SRTM nachschlagen
+function.downloadsrtm=SRTM Dateien herunterladen
function.getwikipedia=Wikipediaartikel in der N\u00e4he nachschlagen
function.searchwikipedianames=Wikipedia mit Name durchsuchen
function.downloadosm=OSM-Daten f\u00fcr dieses Gebiet herunterladen
function.saveconfig=Einstellungen speichern
function.diskcache=Karten auf Festplatte speichern
function.managetilecache=Kartenkacheln verwalten
+function.getweatherforecast=Wettervorhersage herunterladen
# Dialogs
dialog.exit.confirm.title=GpsPrune beenden
dialog.exportpov.ballsandsticks=B\u00e4lle und Stangen
dialog.exportpov.tubesandwalls=R\u00f6hren und W\u00e4nde
dialog.3d.warningtracksize=Dieser Track hat sehr viele Punkte, die Java3D vielleicht nicht bearbeiten kann.\nM\u00f6chten Sie den Vorgang trotzdem fortsetzen?
+dialog.3d.useterrain=Gel\u00e4nde anzeigen
+dialog.3d.terraingridsize=Gittergr\u00f6\u00dfe
dialog.exportpov.baseimage=Grundbild
dialog.exportpov.cannotmakebaseimage=Bild kann nicht gespeichert werden
dialog.baseimage.title=Kartenbild
dialog.exportsvg.gradients=Farbverl\u00e4ufe verwenden
dialog.exportimage.noimagepossible=Kartenbilder m\u00fcssen schon gespeichert werden bevor sie in einem Export verwendet werden k\u00f6nnen
dialog.exportimage.drawtrack=Track auf der Karte zeichnen
+dialog.exportimage.drawtrackpoints=Trackpunkte zeichnen
dialog.exportimage.textscalepercent=Text Skalierung (%)
dialog.pointtype.desc=Folgende Punkttypen speichern:
dialog.pointtype.track=Trackpunkte
dialog.correlate.select.photoname=Bezeichnung des Fotos
dialog.correlate.select.timediff=Zeitdifferenz
dialog.correlate.select.photolater=Foto sp\u00e4ter
-dialog.correlate.options.tip=Tipp: Mit mindestens einem manuell verbundenen Element kann die Zeitdifferenz automatisch berechnet werden.
dialog.correlate.options.intro=W\u00e4hlen Sie die Optionen f\u00fcr die Korrelation aus
dialog.correlate.options.offsetpanel=Zeitunterschied
dialog.correlate.options.offset=Unterschied
dialog.compress.douglaspeucker.title=Douglas-Peucker-Komprimierung
dialog.compress.douglaspeucker.paramdesc=Span-Faktor
dialog.compress.summarylabel=Zu entfernende Punkte
-dialog.compress.confirm1=Es wurden
-dialog.compress.confirm2=Punkte markiert.\nMit Track->Markierte Punkte l\u00f6schen werden sie gel\u00f6scht
+dialog.compress.confirm=Es wurden %d Punkte markiert.\nWollen Sie die Punkte sofort l\u00f6schen?
dialog.compress.confirmnone=es wurden keine Punkte markiert
dialog.deletemarked.nonefound=Es konnten keine Punkte entfernt werden
dialog.pastecoordinates.desc=Koordinaten eingeben oder einf\u00fcgen
dialog.pastecoordinates.coords=Koordinaten
dialog.pastecoordinates.nothingfound=Bitte pr\u00fcfen Sie die Koordinaten und versuchen Sie es nochmals
-dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n http://activityworkshop.net/software/gpsprune/
+dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n http://gpsprune.activityworkshop.net/
dialog.about.version=Version
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune ist ein Programm zum Laden, Darstellen und Editieren der Daten von GPS- Ger\u00e4ten.
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Diese neue Version ist am
dialog.checkversion.releasedate2=ver\u00f6ffentlicht worden.
-dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=Anstelle der Maus k\u00f6nnen Sie folgende Tastenkombinationen nutzen
dialog.keys.keylist=<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>
dialog.keys.normalmodifier=Strg
dialog.diskcache.deleteold=Veraltete Kacheln l\u00f6schen
dialog.diskcache.maximumage=Maximales Alter (Tage)
dialog.diskcache.deleteall=Alle Kacheln l\u00f6schen
-dialog.diskcache.deleted1=Es wurden
-dialog.diskcache.deleted2=Dateien aus dem Ordner gel\u00f6scht
+dialog.diskcache.deleted=Es wurden %d Dateien aus dem Ordner gel\u00f6scht
dialog.deletefieldvalues.intro=W\u00e4hlen Sie das Feld aus, das Sie l\u00f6schen m\u00f6chten
dialog.deletefieldvalues.nofields=Es sind keine Felder zu l\u00f6schen f\u00fcr diesen Bereich
dialog.setlinewidth.text=Geben Sie die Dicke der Linien ein (1-4)
dialog.downloadosm.desc=Die OpenStreetMap-Daten f\u00fcr das folgende Gebiet werden heruntergeladen (.osm-Datei):
dialog.searchwikipedianames.search=Suche nach:
+dialog.weather.location=Ort
+dialog.weather.update=Vorhersage aktualisiert
+dialog.weather.sunrise=Sonnenaufgang
+dialog.weather.sunset=Sonnenuntergang
+dialog.weather.temperatureunits=Temperaturen
+dialog.weather.currentforecast=Aktuell
+dialog.weather.dailyforecast=T\u00e4gliche Vorhersage
+dialog.weather.3hourlyforecast=Drei-st\u00fcndliche Vorhersage
+dialog.weather.day.now=Aktuell
+dialog.weather.day.today=Heute
+dialog.weather.day.tomorrow=Morgen
+dialog.weather.day.monday=Montag
+dialog.weather.day.tuesday=Dienstag
+dialog.weather.day.wednesday=Mittwoch
+dialog.weather.day.thursday=Donnerstag
+dialog.weather.day.friday=Freitag
+dialog.weather.day.saturday=Samstag
+dialog.weather.day.sunday=Sonntag
+dialog.weather.creditnotice=Diese Daten wurden von openweathermap.org zur Verf\u00fcgung gestellt. Die Webseite hat mehr Information.
# 3d window
dialog.3d.title=GpsPrune-3D-Ansicht
confirm.addaltitudeoffset=H\u00f6henverschiebung aufgerechnet
confirm.rearrangewaypoints=Wegpunkte neu angeordnet
confirm.rearrangephotos=Fotos neu angeordnet
+confirm.splitsegments=Es wurden %d Schnitte gemacht
+confirm.sewsegments=Es wurden %d Verbindungen gemacht
confirm.cutandmove=Bereich verschoben
confirm.interpolate=Punkte eingef\u00fcgt
confirm.convertnamestotimes=Wegpunktnamen umgewandelt
-confirm.saveexif.ok1=Es wurden
-confirm.saveexif.ok2=Fotodateien geschrieben
+confirm.saveexif.ok=Es wurden %d Fotodateien geschrieben
confirm.undo.single=Operation r\u00fcckg\u00e4ngig gemacht
confirm.undo.multi=Operationen r\u00fcckg\u00e4ngig gemacht
confirm.jpegload.single=Foto wurde geladen
confirm.createpoint=Punkt erzeugt
confirm.rotatephoto=Foto gedreht
confirm.running=In Bearbeitung ...
-confirm.lookupsrtm1=Es wurden
-confirm.lookupsrtm2=H\u00f6henwerte gefunden
+confirm.lookupsrtm=Es wurden %d H\u00f6henwerte gefunden
+confirm.downloadsrtm=Es wurden %d Dateien heruntergeladen
+confirm.downloadsrtm.none=Keine Dateien heruntergeladen, alle waren schon gespeichert.
confirm.deletefieldvalues=Feldwerte gel\u00f6scht
confirm.audioload=Audiodateien geladen
confirm.correlateaudios.single=Audio wurde korreliert
confirm.correlateaudios.multi=Audios wurden korreliert
+# Tips
+tip.title=Tipp
+tip.useamapcache=Mit lokal-gespeicherten Kartenkacheln (Einstellungen -> Karten auf Festplatte speichern)\nk\u00f6nnen Sie die Darstellung beschleunigen und Netzwerkverkehr reduzieren.
+tip.learntimeparams=Wenn Sie Track -> Zeitparameter erlernen mit Ihren Tracks benutzen\ndann werden die berechneten Werten genauer.
+tip.downloadsrtm=Sie k\u00f6nnen diese Funktion beschleunigen indem Sie\nOnline -> SRTM Dateien herunterladen aufrufen\num die Daten lokal zu speichern.
+tip.usesrtmfor3d=Dieser Track hat keine H\u00f6heninformation.\nSie k\u00f6nnen die SRTM Funktionen verwenden, um\nH\u00f6henwerte abzusch\u00e4tzen.
+tip.manuallycorrelateone=Mit mindestens einem manuell verbundenen Element kann die Zeitdifferenz automatisch berechnet werden.
+
# Buttons
button.ok=OK
button.back=Zur\u00fcck
button.no=Nein
button.yestoall=Ja f\u00fcr alle
button.notoall=Nein f\u00fcr alle
+button.always=Ja, immer
button.select=Ausw\u00e4hlen
button.selectall=Alle ausw\u00e4hlen
button.selectnone=Nichts ausw\u00e4hlen
fieldname.custom=Custom
fieldname.prefix=Feld
fieldname.distance=L\u00e4nge
-fieldname.movingdistance=Wegstrecke
fieldname.duration=Zeitdauer
fieldname.speed=Geschwindigkeit
fieldname.verticalspeed=Vertikale Geschwindigkeit
# External urls
url.googlemaps=maps.google.de
wikipedia.lang=de
+openweathermap.lang=de
# Cardinals for 3d plots
cardinal.n=N
undo.insert=Punkte hinzuf\u00fcgen
undo.reverse=Bereich umdrehen
undo.mergetracksegments=Trackabschnitte verbinden
+undo.splitsegments=in Trackabschnitte schneiden
+undo.sewsegments=Trackabschnitte zusammenf\u00fcgen
undo.addtimeoffset=Zeitverschiebung aufrechnen
undo.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
undo.rearrangewaypoints=Wegpunkte neu anordnen
error.saveexif.filenotfound=Bilddatei nicht gefunden
error.saveexif.cannotoverwrite1=Bilddatei
error.saveexif.cannotoverwrite2=ist schreibgesch\u00fctzt. Als Kopie speichern?
-error.saveexif.failed1=
-error.saveexif.failed2=Bilder konnten nicht gespeichert werden
-error.saveexif.forced1=Bei
-error.saveexif.forced2=der Bilder musste das Speichern erzwungen werden
+error.saveexif.failed=%d Bilder konnten nicht gespeichert werden
+error.saveexif.forced=Bei %d der Bilder musste das Speichern erzwungen werden
error.load.dialogtitle=Fehler beim Laden
error.load.noread=Datei konnte nicht gelesen werden
error.load.nopoints=Keine g\u00fcltigen Daten in Datei gefunden
error.cache.cannotdelete=Es konnte keine Kacheln gel\u00f6scht werden
error.interpolate.invalidparameter=Die Anzahl der Punkte muss zwischen 1 und 1000 liegen
error.learnestimationparams.failed=Mit diesem Track k\u00f6nnen die Parameter nicht berechnet werden.\nVersuchen Sie mit mehreren Tracks.
+error.tracksplit.nosplit=Der Track konnte nicht aufgesplittet werden.
+error.downloadsrtm.nocache=Die Dateien konnten nicht gespeichert werden.\nBitte pr\u00fcfen Sie den Kartenordner nach.
+error.sewsegments.nothingdone=Es wurden keine Verbindungen gemacht.\nEs gibt jetzt %d Trackabschnitte.
menu.file.recentfiles=Letzschti aagluegte Files
menu.file.save=Als Text Speichere
menu.file.exit=Be\u00e4nde
+menu.online=Online
menu.track=Track
menu.track.undo=Undo
menu.track.clearundo=Undo-Liste l\u00f6sche
menu.track.deletemarked=Markierte P\u00fcnkte l\u00f6sche
menu.track.rearrange=Waypoints reorganisiere
menu.track.rearrange.start=Alli zum Aafang
-menu.track.rearrange.end=Alli zum Ände
+menu.track.rearrange.end=Alli zum \u00c4nde
menu.track.rearrange.nearest=Jede zum n\u00f6chsti Trackpunkt
menu.range=Beriich
menu.range.all=Alles selektiere
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=O
altkey.menu.track=T
altkey.menu.range=B
altkey.menu.point=P
function.estimatetime=Ziit absch\u00e4tze
function.learnestimationparams=Ziitparameter erlerne
function.setmapbg=Karte Hintegrund setz\u00e4
+function.splitsegments=In Tracksegm\u00e4nte schniide
+function.sewsegments=Tracksegm\u00e4nte z\u00e4mef\u00fcge
function.getgpsies=Gpsies Tracks hol\u00e4
function.uploadgpsies=Date zum Gpsies uufalad\u00e4
function.lookupsrtm=H\u00f6hendate vonem SRTM hole
+function.downloadsrtm=SRTM Files abalade
function.getwikipedia=Im Wikipedia in dr N\u00f6chi naaluege
function.searchwikipedianames=Wikipedia mit Name durasueche
function.downloadosm=OSM-Date f\u00fcr dere Gebiet abalad\u00e4
function.setlanguage=Sproch setz\u00e4
function.help=Hilfe
function.showkeys=Tastekombinatione aazeige
-function.about=Ãœber GpsPrune
+function.about=\u00dcber GpsPrune
function.checkversion=Pruef nach ne noie Version
function.saveconfig=Iistellige speichere
function.diskcache=Karten uufem Disk speichere
function.managetilecache=Kartebildli verwolte
+function.getweatherforecast=W\u00e4tterprognose abalade
# Dialogs
dialog.exit.confirm.title=GpsPrune be\u00e4nde
dialog.deletephoto.title=F\u00f6teli entfern\u00e4
dialog.deletephoto.deletepoint=Punkt vonem F\u00f6teli au l\u00f6sch\u00e4?
dialog.deleteaudio.deletepoint=Punkt vonem Audio au l\u00f6sch\u00e4?
-dialog.openoptions.title=Öffne Optionen
+dialog.openoptions.title=\u00d6ffne Optionen
dialog.openoptions.filesnippet=Extrakt vom File
dialog.load.table.field=F\u00e4ld
dialog.load.table.datatype=Date Typ
dialog.save.overwrite.text=s'File existiert scho. Sind Sie sicher, Sie wend s'File \u00fcberschriibe?
dialog.save.notypesselected=Kei Punktetype sin uusgew\u00e4hlt worde
dialog.exportkml.text=Titel f\u00fcr die Date
-dialog.exportkml.altitude=Absolut H\u00f6chiinformation (f\u00fcrs Fliege)
+dialog.exportkml.altitude=Absolut H\u00f6chiinformation (f\u00fcrs Fl\u00fc\u00fcge)
dialog.exportkml.kmz=Date ins kmz File komprimier\u00e4
dialog.exportkml.exportimages=Bildli ins Kmz exportier\u00e4
dialog.exportkml.imagesize=Bildligr\u00f6sse
dialog.exportpov.ballsandsticks=B\u00e4lle und Schtange
dialog.exportpov.tubesandwalls=R\u00f6hre und W\u00e4nde
dialog.3d.warningtracksize=Dieser Track h\u00e4t mega viele P\u00fcnkte, die Java3D villiicht n\u00f6d chann bearbeite.\nSind Sie sicher, Sie wend trotzdem fortsetze?
+dialog.3d.useterrain=Gel\u00e4nde aazeige
+dialog.3d.terraingridsize=Gittergr\u00f6sse
dialog.exportpov.baseimage=Grundbild
dialog.exportpov.cannotmakebaseimage=Bild chann n\u00f6d gspeicheret werde
dialog.baseimage.title=Kartenbild
dialog.exportsvg.gradients=Farbeverl\u00e4ufe verw\u00e4nde
dialog.exportimage.noimagepossible=Kartebilder m\u00fcsset scho gspeicheret werde, bevor sie bim Export verwendet werde k\u00f6nne
dialog.exportimage.drawtrack=Track uf d Karte zeichne
+dialog.exportimage.drawtrackpoints=Trackp\u00fcnkte au zeichne
dialog.exportimage.textscalepercent=Text Skalierig (%)
dialog.pointtype.desc=Folgende Punkttype speichere:
dialog.pointtype.track=Trackp\u00fcnkte
dialog.correlate.select.photoname=F\u00f6teli Name
dialog.correlate.select.timediff=Ziitdiffer\u00e4nz
dialog.correlate.select.photolater=F\u00f6teli sp\u00f6ter
-dialog.correlate.options.tip=Tipp: Mit mindeschtens einem verbundenen Elem\u00e4nt kann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
dialog.correlate.options.intro=W\u00e4hlet Sie die Optione uus f\u00fcr die Korrelierig
dialog.correlate.options.offsetpanel=Ziitunterschied
dialog.correlate.options.offset=Unterschied
dialog.correlate.correltimes=F\u00fcrs Korreliere, folgendes verw\u00e4nde:
dialog.correlate.timestamp.beginning=Aafang
dialog.correlate.timestamp.middle=Mitti
-dialog.correlate.timestamp.end=Ände
+dialog.correlate.timestamp.end=\u00c4nde
dialog.correlate.audioselect.intro=W\u00e4hlet Sie eini vo deren Audios uus, um die Ziitdiffer\u00e4nz zu ber\u00e4chn\u00e4
dialog.correlate.select.audioname=Audio Name
dialog.correlate.select.audiolater=Audio sp\u00f6ter
dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von d P\u00fcnkte setze
dialog.rearrangephotos.tostart=zum Aafang
-dialog.rearrangephotos.toend=zum Ände
+dialog.rearrangephotos.toend=zum \u00c4nde
dialog.rearrangephotos.nosort=N\u00f6d sortiere
dialog.rearrangephotos.sortbyfilename=per Filename sortiere
dialog.rearrangephotos.sortbytime=per Ziit sortiere
dialog.compress.douglaspeucker.title=Douglas-Peucker Komprimierig
dialog.compress.douglaspeucker.paramdesc=Span Faktor
dialog.compress.summarylabel=P\u00fcnkte zu entf\u00e4rn\u00e4
-dialog.compress.confirm1=Es sin
-dialog.compress.confirm2=P\u00fcnkt markiert.\nMit Track->Markierte P\u00fcnkte l\u00f6sche werdet sie gl\u00f6scht
+dialog.compress.confirm=Es sin %s P\u00fcnkt markiert worde.\nWend Sie die jetz l\u00f6sche?
dialog.compress.confirmnone=es sin kei P\u00fcnkte markiert worde
dialog.deletemarked.nonefound=Kei P\u00fcnkte h\u00e4tte gel\u00f6scht werde k\u00f6nne
dialog.pastecoordinates.desc=G\u00e4bet Sie hier die Koordinaten inn\u00e4
dialog.pastecoordinates.coords=Koordinate
dialog.pastecoordinates.nothingfound=Pr\u00fcefet Sie die Koordinate und versuechet nomal
-dialog.help.help=Bitte lueg na\n http://activityworkshop.net/software/gpsprune/\nf\u00fcr wiitere Information und Benutzeraaleitige.
+dialog.help.help=Bitte lueg na\n http://gpsprune.activityworkshop.net/\nf\u00fcr wiitere Information und Benutzeraaleitige.
dialog.about.version=Version
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune isch s Programm f\u00fcrs Lade, Darstelle und Editiere vo Date von GPS Ger\u00e4te.
dialog.about.summarytext2=Es isch unter den Gnu GPL zur Verf\u00fcegig gstellt,f\u00fcr frei, gratis und offen Gebruuch und Wiiterentwicklig.<br>Kopiere, Wiiterverbreitig und Ver\u00e4nderige sin erlaubt und willkomme<br>unter die Bedingige im enthaltene <code>license.txt</code> File.
dialog.about.summarytext3=Bitte lueget Sie na <code style="font-weight:bold">http://activityworkshop.net/</code> f\u00fcr wiitere Informatione und Benutzeraaleitige.
dialog.about.languages=Verf\u00fcegbare Sproche
-dialog.about.translatedby=Schwiizerd\u00fc\u00fctschi Ãœbersetzig vo activityworkshop.
+dialog.about.translatedby=Schwiizerd\u00fc\u00fctschi \u00dcbersetzig vo activityworkshop.
dialog.about.systeminfo=Syschtem Info
dialog.about.systeminfo.os=Betriebsyschtem
dialog.about.systeminfo.java=Version vonem Java
dialog.about.credits.exifcode=Exif Code vo
dialog.about.credits.icons=Einigi Bilder vo
dialog.about.credits.translators=Dolm\u00e4tscher
-dialog.about.credits.translations=Ãœbersetzige mit dr Hilfe vo
+dialog.about.credits.translations=\u00dcbersetzige mit dr Hilfe vo
dialog.about.credits.devtools=Entwicklungsw\u00e4rkz\u00fc\u00fcge
dialog.about.credits.othertools=Anderi W\u00e4rkz\u00fc\u00fcge
dialog.about.credits.thanks=Danke an
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Die noii Version isch am
dialog.checkversion.releasedate2=ussecho.
-dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=Aastatt d'Muus k\u00f6nnet Sie diese Tastekombinationen nutze
-dialog.keys.keylist=<table><tr><td>Pfiil Taste</td><td>Karte verschiebe</td></tr><tr><td>Strg + links, r\u00e4chts Pfiil</td><td>Vorherigi oder n\u00f6chsti Punkt markiere</td></tr><tr><td>Strg + uuf, aba Pfiil</td><td>Ii- oder Uusezoome</td></tr><tr><td>Strg + Bild uuf, ab</td><td>Vorherigi oder n\u00f6chsti Segm\u00e4nt markiere</td></tr><tr><td>Strg + Pos1, Ände</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt l\u00f6sche</td></tr></table>
+dialog.keys.keylist=<table><tr><td>Pfiil Taste</td><td>Karte verschiebe</td></tr><tr><td>Strg + links, r\u00e4chts Pfiil</td><td>Vorherigi oder n\u00f6chsti Punkt markiere</td></tr><tr><td>Strg + uuf, aba Pfiil</td><td>Ii- oder Uusezoome</td></tr><tr><td>Strg + Bild uuf, ab</td><td>Vorherigi oder n\u00f6chsti Segm\u00e4nt markiere</td></tr><tr><td>Strg + Pos1, \u00c4nde</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt l\u00f6sche</td></tr></table>
dialog.keys.normalmodifier=Strg
dialog.keys.macmodifier=Kommando
dialog.saveconfig.desc=Die folgendi Iinstellige k\u00f6nne gspeicheret werde :
dialog.diskcache.deleteold=Uualti Kachle l\u00f6sche
dialog.diskcache.maximumage=Maximali Alter (Tag)
dialog.diskcache.deleteall=Alli Kachle l\u00f6sche
-dialog.diskcache.deleted1=Es sin
-dialog.diskcache.deleted2=Files uusem Ordner gl\u00f6scht worde
+dialog.diskcache.deleted=Es sin %d Files uusem Ordner gl\u00f6scht worde
dialog.deletefieldvalues.intro=W\u00e4hlet Sie s F\u00e4ld uus zum l\u00f6sche
dialog.deletefieldvalues.nofields=Es sin kei F\u00e4lder z'l\u00f6sche f\u00fcr dere Beriich
dialog.setlinewidth.text=G\u00e4bet Sie die Dicke vonen Linien ii (1-4)
dialog.downloadosm.desc=Best\u00e4tige um rohi OSM Date f\u00fcrn Gebiet aba zlade:
dialog.searchwikipedianames.search=Sueche na:
+dialog.weather.location=Ort
+dialog.weather.update=Prognose aktualisiert
+dialog.weather.sunrise=Sonnenufgang
+dialog.weather.sunset=Sonnenuntergang
+dialog.weather.temperatureunits=Temperature
+dialog.weather.currentforecast=Aktuell
+dialog.weather.dailyforecast=T\u00e4glichi Prognose
+dialog.weather.3hourlyforecast=Dr\u00fc\u00fc-st\u00fcndlichi Prognose
+dialog.weather.day.now=Jetz\u00e4
+dialog.weather.day.today=H\u00fc\u00fct
+dialog.weather.day.tomorrow=Morn
+dialog.weather.day.monday=M\u00e4ntig
+dialog.weather.day.tuesday=Ziischtig
+dialog.weather.day.wednesday=Mittwuch
+dialog.weather.day.thursday=Duunschtig
+dialog.weather.day.friday=Friitig
+dialog.weather.day.saturday=Samschtig
+dialog.weather.day.sunday=Sunntig
+dialog.weather.creditnotice=Diese Date sin vo openweathermap.org zur Verf\u00fcegig gestellt worde. Uf d Websiite h\u00e4ts no meh Infos.
# 3d window
dialog.3d.title=GpsPrune Dr\u00fc\u00fc-d Aasicht
confirm.addaltitudeoffset=H\u00f6chiverschiebig zutue
confirm.rearrangewaypoints=Waypoints umorganisiert
confirm.rearrangephotos=Fotos umorganisiert
+confirm.splitsegments=Es sin %d Schnitte gmacht worde
+confirm.sewsegments=Es sin %d Verbindige gemacht worde
confirm.cutandmove=Beriich gmoved
confirm.interpolate=P\u00fcnkte iigf\u00fcgt worde
confirm.convertnamestotimes=Waypointname verwondlet
-confirm.saveexif.ok1=Es sin
-confirm.saveexif.ok2=F\u00f6telis gschriebe worde
+confirm.saveexif.ok=Es sin %d F\u00f6telis gschriebe worde
confirm.undo.single=Operation r\u00fcckg\u00e4ngig gmacht worde.
confirm.undo.multi=Operatione r\u00fcckg\u00e4ngig gmacht worde.
confirm.jpegload.single=F\u00f6teli isch glade worde
confirm.createpoint=Punkt kreiert worde
confirm.rotatephoto=F\u00f6teli umgedr\u00e4it worde
confirm.running=Am Laufe ...
-confirm.lookupsrtm1=Es sin
-confirm.lookupsrtm2=H\u00f6henwerte gfunde
+confirm.lookupsrtm=Es sin %d H\u00f6henwerte gfunde
+confirm.downloadsrtm=Es sin %d Files abeglade
+confirm.downloadsrtm.none=Kei Files abeglade, die sin scho da gsi.
confirm.deletefieldvalues=Feldw\u00e4rte gl\u00f6scht worde
confirm.audioload=Audiofiles glade worde
confirm.media.removed=entf\u00e4rnt
confirm.correlateaudios.single=Audiofile isch korreliert worde
confirm.correlateaudios.multi=Audiofiles sin korreliert worde
+# Tips
+tip.title=Tipp
+tip.useamapcache=Mit lokali Kartekachle (Iistellige -> Karten uufem Disk speichere)\nk\u00f6nnet Sie d Darstellig bschleunige und Netzwerkverkehr reduziere.
+tip.learntimeparams=Wenn Sie Track -> Ziitparameter erlerne mit Ihren Tracks benutze\ndann werdet d ber\u00e4chneti Werte gnauer.
+tip.downloadsrtm=Sie k\u00f6nnet d Funktion beschleunige indem Sie\nOnline -> SRTM Files abalade uufrufe\num d Date lokal z'speichere.
+tip.usesrtmfor3d=Dere Track h\u00e4t kei H\u00f6chiinformation.\nSie k\u00f6nnet d SRTM Funktione verw\u00e4nde, um\nH\u00f6chiwerte abz'sch\u00e4tze.
+tip.manuallycorrelateone=Mit mindeschtens einem verbundenen Elem\u00e4nt kann die Ziitdiffer\u00e4nz automatisch ber\u00e4chnet werd\u00e4.
+
# Buttons
button.ok=OK
button.back=Zrugg
button.next=N\u00f6chst\u00e4
button.finish=Fertig
button.cancel=Abbr\u00e4ch\u00e4
-button.overwrite=Ãœberschriib\u00e4
+button.overwrite=\u00dcberschriib\u00e4
button.moveup=Uuf\u00e4 schieb\u00e4
button.movedown=Aba schieb\u00e4
button.edit=Editier\u00e4
button.no=Nei
button.yestoall=Ja f\u00fcr alli
button.notoall=Nei f\u00fcr alli
+button.always=Ja, immer
button.select=Uusw\u00e4hle
button.selectall=Alli uusw\u00e4hle
button.selectnone=N\u00fc\u00fct uusw\u00e4hle
fieldname.custom=Custom
fieldname.prefix=F\u00e4ld
fieldname.distance=L\u00e4ngi
-fieldname.movingdistance=Wegl\u00e4ngi
fieldname.duration=Ziitl\u00e4ngi
fieldname.speed=Gschwindikeit
fieldname.verticalspeed=Uf/Ab Gschwindikeit
# External urls
url.googlemaps=maps.google.ch
wikipedia.lang=als
+openweathermap.lang=de
# Cardinals for 3d plots
cardinal.n=N
undo.insert=P\u00fcnkte inn\u00e4tu\u00e4
undo.reverse=Beriich umdr\u00e4hie
undo.mergetracksegments=Tracksegm\u00e4nte merge
+undo.splitsegments=in Tracksegm\u00e4nte schniide
+undo.sewsegments=Tracksegm\u00e4nte z\u00e4mef\u00fcge
undo.addtimeoffset=Ziitverschiebig zutue
undo.addaltitudeoffset=H\u00f6chiverschiebig zutue
undo.rearrangewaypoints=Waypoints reorganisier\u00e4
error.saveexif.filenotfound=F\u00f6teli File n\u00f6d gfunde
error.saveexif.cannotoverwrite1=F\u00f6teli File
error.saveexif.cannotoverwrite2=isch n\u00f6d schriibbar. Speichere na einer Kopie?
-error.saveexif.failed1=
-error.saveexif.failed2=von d Bilder han i n\u00f6d k\u00f6nne speichere
-error.saveexif.forced1=
-error.saveexif.forced2=von d Bilder han i m\u00fcsse forziere
+error.saveexif.failed=%d von d Bilder han i n\u00f6d k\u00f6nne speichere
+error.saveexif.forced=%d von d Bilder han i m\u00fcsse forziere
error.load.dialogtitle=F\u00e4hle bim Lade
error.load.noread=File cha n\u00f6d glase werde
error.load.nopoints=Kei g\u00fcltigi Information inem File gfunde
error.cache.cannotdelete=Es sin kei Kachle gl\u00f6scht worde
error.interpolate.invalidparameter=D'Aazahl P\u00fcnkt muess zw\u00fcschet 1 und 1000 sii
error.learnestimationparams.failed=Mit dere Track k\u00f6nnet die Parameter n\u00f6d br\u00e4chnet werde.\nVersuechet Sie mit mehreri Tracks.
+error.tracksplit.nosplit=Es isch n\u00f6d m\u00f6glech gsi, den Track uufz'schniide.
+error.downloadsrtm.nocache=Die Files k\u00f6nnet n\u00f6d gspeicheret werde.\nBitte pr\u00fcefet Sie den Kartenordner na.
+error.sewsegments.nothingdone=Es sin kei Verbindige gmacht worde.\nEs h\u00e4t jetzt %d Tracksegm\u00e4nte.
menu.file.recentfiles=Recent files
menu.file.save=Save as text
menu.file.exit=Exit
+menu.online=Online
menu.track=Track
menu.track.undo=Undo
menu.track.clearundo=Clear undo list
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=N
altkey.menu.range=R
altkey.menu.track=T
altkey.menu.point=P
function.fullrangedetails=Full range details
function.estimatetime=Estimate time
function.learnestimationparams=Learn time estimation parameters
+function.splitsegments=Split track into segments
+function.sewsegments=Sew track segments together
function.getgpsies=Get Gpsies tracks
function.uploadgpsies=Upload track to Gpsies
function.lookupsrtm=Get altitudes from SRTM
+function.downloadsrtm=Download SRTM tiles
function.getwikipedia=Get nearby Wikipedia articles
function.searchwikipedianames=Search Wikipedia by name
function.downloadosm=Download OSM data for area
function.saveconfig=Save settings
function.diskcache=Save maps to disk
function.managetilecache=Manage tile cache
+function.getweatherforecast=Get weather forecast
# Dialogs
dialog.exit.confirm.title=Exit GpsPrune
dialog.exportpov.ballsandsticks=Balls and sticks
dialog.exportpov.tubesandwalls=Tubes and walls
dialog.3d.warningtracksize=This track has a large number of points, which Java3D might not be able to display.\nAre you sure you want to continue?
+dialog.3d.useterrain=Show terrain
+dialog.3d.terraingridsize=Grid size
dialog.exportpov.baseimage=Base image
dialog.exportpov.cannotmakebaseimage=Cannot write base image
dialog.baseimage.title=Map image
dialog.exportsvg.gradients=Use gradients for shading
dialog.exportimage.noimagepossible=Map images need to be cached to disk in order to use them for an export.
dialog.exportimage.drawtrack=Draw track on map
+dialog.exportimage.drawtrackpoints=Draw track points
dialog.exportimage.textscalepercent=Text scale factor (%)
dialog.pointtype.desc=Save the following point types:
dialog.pointtype.track=Track points
dialog.correlate.select.photoname=Photo name
dialog.correlate.select.timediff=Time difference
dialog.correlate.select.photolater=Photo later
-dialog.correlate.options.tip=Tip: By manually connecting at least one item, the time offset can be calculated for you.
dialog.correlate.options.intro=Select the options for automatic correlation
dialog.correlate.options.offsetpanel=Time offset
dialog.correlate.options.offset=Offset
dialog.compress.douglaspeucker.title=Douglas-Peucker compression
dialog.compress.douglaspeucker.paramdesc=Span factor
dialog.compress.summarylabel=Points to delete
-dialog.compress.confirm1=
-dialog.compress.confirm2=points have been marked.\nUse Track->Delete marked points to delete them
+dialog.compress.confirm=%d points have been marked.\nDelete these marked points now?
dialog.compress.confirmnone=no points have been marked
dialog.deletemarked.nonefound=No data points could be removed
dialog.pastecoordinates.desc=Enter or paste the coordinates here
dialog.diskcache.deleteold=Delete old tiles
dialog.diskcache.maximumage=Maximum age (days)
dialog.diskcache.deleteall=Delete all tiles
-dialog.diskcache.deleted1=Deleted
-dialog.diskcache.deleted2=files from the cache
+dialog.diskcache.deleted=Deleted %d files from the cache
dialog.deletefieldvalues.intro=Select the field to delete for the current range
dialog.deletefieldvalues.nofields=There are no fields to delete for this range
dialog.setlinewidth.text=Enter the thickness of lines to draw for the tracks (1-4)
dialog.downloadosm.desc=Confirm to download the raw OSM data for the specified area:
dialog.searchwikipedianames.search=Search for:
+dialog.weather.location=Location
+dialog.weather.update=Forecast updated
+dialog.weather.sunrise=Sunrise
+dialog.weather.sunset=Sunset
+dialog.weather.temperatureunits=Temperatures
+dialog.weather.currentforecast=Current weather
+dialog.weather.dailyforecast=Daily forecast
+dialog.weather.3hourlyforecast=Three-hourly forecast
+dialog.weather.day.now=Current weather
+dialog.weather.day.today=Today
+dialog.weather.day.tomorrow=Tomorrow
+dialog.weather.day.monday=Monday
+dialog.weather.day.tuesday=Tuesday
+dialog.weather.day.wednesday=Wednesday
+dialog.weather.day.thursday=Thursday
+dialog.weather.day.friday=Friday
+dialog.weather.day.saturday=Saturday
+dialog.weather.day.sunday=Sunday
+dialog.weather.creditnotice=This data is made available by openweathermap.org. Their website has more details.
# 3d window
dialog.3d.title=GpsPrune Three-d view
confirm.addaltitudeoffset=Altitude offset added
confirm.rearrangewaypoints=Waypoints rearranged
confirm.rearrangephotos=Photos rearranged
+confirm.splitsegments=%d segment splits were made
+confirm.sewsegments=%d segment joins were made
confirm.cutandmove=Selection moved
confirm.interpolate=Points added
confirm.convertnamestotimes=Waypoint names converted
-confirm.saveexif.ok1=Saved
-confirm.saveexif.ok2=photo files
+confirm.saveexif.ok=Saved %d photo files
confirm.undo.single=operation undone
confirm.undo.multi=operations undone
confirm.jpegload.single=photo was added
confirm.rotatephoto=photo rotated
confirm.createpoint=point created
confirm.running=Running ...
-confirm.lookupsrtm1=Found
-confirm.lookupsrtm2=altitude values
+confirm.lookupsrtm=Found %d altitude values
+confirm.downloadsrtm=Downloaded %d files to the cache
+confirm.downloadsrtm.none=No files downloaded, they were already in the cache.
confirm.deletefieldvalues=Field values deleted
confirm.audioload=Audio files added
confirm.correlateaudios.single=audio was correlated
confirm.correlateaudios.multi=audios were correlated
+# Tips, shown just once when appropriate
+tip.title=Tip
+tip.useamapcache=By setting up a disk cache (Settings -> Save maps to disk)\nyou can speed up the display and reduce network traffic.
+tip.learntimeparams=The results will be more accurate if you use\nTrack -> Learn time estimation parameters\non your recorded tracks.
+tip.downloadsrtm=You can speed this up by calling\nOnline -> Download SRTM tiles\nto save the data in your map cache.
+tip.usesrtmfor3d=This track doesn't have altitudes.\nYou can use the SRTM functions to get approximate\naltitudes for the 3d view.
+tip.manuallycorrelateone=By manually connecting at least one item, the time offset can be calculated for you.
+
# Buttons
button.ok=OK
button.back=Back
button.no=No
button.yestoall=Yes to all
button.notoall=No to all
+button.always=Always
button.select=Select
button.selectall=Select all
button.selectnone=Select none
fieldname.custom=Custom
fieldname.prefix=Field
fieldname.distance=Distance
-fieldname.movingdistance=Moving distance
fieldname.duration=Duration
fieldname.speed=Speed
fieldname.verticalspeed=Vertical speed
units.degmin=Deg-min
units.deg=Degrees
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=and
# External urls
url.googlemaps=maps.google.co.uk
wikipedia.lang=en
+openweathermap.lang=en
# Cardinals for 3d plots
cardinal.n=N
undo.insert=insert points
undo.reverse=reverse range
undo.mergetracksegments=merge track segments
+undo.splitsegments=split track segments
+undo.sewsegments=sew track segments
undo.addtimeoffset=add time offset
undo.addaltitudeoffset=add altitude offset
undo.rearrangewaypoints=rearrange waypoints
error.saveexif.filenotfound=Failed to find photo file
error.saveexif.cannotoverwrite1=Photo file
error.saveexif.cannotoverwrite2=is read-only and can't be overwritten. Write to copy?
-error.saveexif.failed1=Failed to save
-error.saveexif.failed2=of the images
-error.saveexif.forced1=
-error.saveexif.forced2=of the images required forcing
+error.saveexif.failed=Failed to save %d of the images
+error.saveexif.forced=%d of the images required forcing
error.load.dialogtitle=Error loading data
error.load.noread=Cannot read file
error.load.nopoints=No coordinate information found in the file
error.cache.cannotdelete=No tiles could be deleted
error.interpolate.invalidparameter=The number of points must be between 1 and 1000
error.learnestimationparams.failed=Cannot learn the parameters from this track.\nTry loading more tracks.
+error.tracksplit.nosplit=The track could not be split
+error.downloadsrtm.nocache=The files could not be saved.\nPlease check the disk cache.
+error.sewsegments.nothingdone=No segments could be sewn together.\nThere are now %d segments in the track.
menu.file.recentfiles=Archivos recientes
menu.file.save=Guardar
menu.file.exit=Salir
+menu.online=Online
menu.track=Track
menu.track.undo=Deshacer
menu.track.clearundo=Despejar la lista de deshacer
# Alt keys for menus
altkey.menu.file=A
+altkey.menu.online=O
altkey.menu.track=T
altkey.menu.range=R
altkey.menu.point=U
function.getgpsies=Bajar ruta de Gpsies
function.uploadgpsies=Subir recorrido a Gpsies
function.lookupsrtm=Obtener altitudes de SRTM
+function.downloadsrtm=Descargar datos de SRTM
function.getwikipedia=Obtener art\u00edculos de Wikipedia cercanos
function.searchwikipedianames=Buscar en Wikipedia por nombre
function.downloadosm=Descargar datos OSM del \u00e1rea
function.saveconfig=Guardar preferencias
function.diskcache=Guardar mapas en disco
function.managetilecache=Administrar cache de mapas
+function.getweatherforecast=Obtener pron\u00f3stico del tiempo
# Dialogs
dialog.exit.confirm.title=Salir de GpsPrune
dialog.gpssend.trackname=Nombre del track
dialog.gpsbabel.filters=Filtros
dialog.addfilter.title=A\u00f1adir filtro
+dialog.gpsbabel.filter.discard=Desechar
dialog.gpsbabel.filter.simplify=Simplificar
dialog.gpsbabel.filter.distance=Distancia
dialog.gpsbabel.filter.interpolate=Interpolar
+dialog.gpsbabel.filter.discard.intro=Desechar puntos si
+dialog.gpsbabel.filter.discard.hdop=Hdop >
+dialog.gpsbabel.filter.discard.vdop=Vdop >
dialog.gpsbabel.filter.discard.numsats=N\u00famero de sat\u00e9lites <
+dialog.gpsbabel.filter.simplify.maxpoints=Numero de puntos <
+dialog.gpsbabel.filter.distance.distance=Si distancia <
+dialog.gpsbabel.filter.distance.time=y differencia horaria <
+dialog.gpsbabel.filter.interpolate.distance=Si distancia >
+dialog.gpsbabel.filter.interpolate.time=o differencia horaria >
dialog.saveoptions.title=Guardar archivo
dialog.save.fieldstosave=Campos a guardar
dialog.save.table.field=Campo
dialog.exportgpx.encoding=Codificaci\u00f3n
dialog.exportgpx.encoding.system=Sistema
dialog.exportgpx.encoding.utf8=UTF-8
+dialog.3d.useterrain=Terreno
dialog.exportpov.text=Introduca los par\u00e1metros para exportar
dialog.exportpov.font=Fuente
dialog.exportpov.camerax=C\u00e1mara X
dialog.exportsvg.phi=\u00c1ngulo de azimuth \u03d5
dialog.exportsvg.theta=\u00c1ngulo de elevaci\u00f3n
dialog.exportsvg.gradients=Usar degradado para sombras
+dialog.exportimage.drawtrack=Dibujar track
+dialog.exportimage.drawtrackpoints=Dibujar puntos del track
dialog.pointtype.desc=Salvar los siguientes tipos de puntos:
dialog.pointtype.track=Puntos del track
dialog.pointtype.waypoint=Waypoints
dialog.pointedit.title=Editar punto
dialog.pointedit.intro=Seleccione cada campo para modificar el valor
dialog.pointedit.table.field=Campo
+dialog.pointedit.nofield=Ning\u00fan campo seleccionado
dialog.pointedit.table.value=Valor
dialog.pointnameedit.name=Nombre de waypoint
dialog.pointnameedit.uppercase=May\u00fasculas
dialog.correlate.select.photoname=Nombre de la foto
dialog.correlate.select.timediff=Diferencia de tiempo
dialog.correlate.select.photolater=Foto m\u00e1s adelante
-dialog.correlate.options.tip=Sugerencia: Correlacionando al menos una foto manualmente, el margen de tiempo se calcula autom\u00e1ticamente.
dialog.correlate.options.intro=Seleccionar las opciones para correlaci\u00f3n autom\u00e1tica
dialog.correlate.options.offsetpanel=Margen de tiempo
dialog.correlate.options.offset=Margen
dialog.compress.douglaspeucker.title=Compresion Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=Factor de extensi\u00f3n
dialog.compress.summarylabel=Puntos para eliminar
+dialog.compress.confirm=%d puntos marcados. \u00bfDesea eliminar los puntos?
dialog.compress.confirmnone=Ning\u00fan punto marcado
dialog.deletemarked.nonefound=Ning\u00fan punto eliminado
dialog.pastecoordinates.desc=Ingresar o pegar las coordenadas aqu\u00ed
dialog.saveconfig.prune.mapsourcelist=Proveedor de mapas
dialog.saveconfig.prune.diskcache=Memoria intermedia de mapas
dialog.saveconfig.prune.kmzimagewidth=Ancho de im\u00e1genes en KMZ
+dialog.saveconfig.prune.kmzimageheight=Alto de im\u00e1genes en KMZ
dialog.saveconfig.prune.colourscheme=Color de esquema
dialog.saveconfig.prune.linewidth=Ancho de l\u00ednea
dialog.saveconfig.prune.kmltrackcolour=Color de pista de KML
dialog.diskcache.deleteold=Borrar recuadros antiguos
dialog.diskcache.maximumage=Edad m\u00e1xima (dias)
dialog.diskcache.deleteall=Borrar todos los recuadros
-dialog.diskcache.deleted1=Borrado
-dialog.diskcache.deleted2=Archivos del cache
+dialog.diskcache.deleted=Borrado %d archivos del cache
dialog.deletefieldvalues.intro=Seleccionar el campo a eliminar para el rango actual
dialog.deletefieldvalues.nofields=No hay campos a eliminar para el rango actual
dialog.setlinewidth.text=Introduzca la anchura de las l\u00edneas a dibujar para los recorridos (1-4)
dialog.downloadosm.desc=Confirmar la descarga de datos en bruto de OSM para el \u00e1rea especificada.
dialog.searchwikipedianames.search=Buscar:
+dialog.weather.day.now=Tiempo actual
+dialog.weather.day.today=Hoy
+dialog.weather.day.tomorrow=Ma\u00f1ana
+dialog.weather.day.monday=Lunes
+dialog.weather.day.tuesday=Martes
+dialog.weather.day.wednesday=Mi\u00e9rcoles
+dialog.weather.day.thursday=Jueves
+dialog.weather.day.friday=Viernes
+dialog.weather.day.saturday=S\u00e1bado
+dialog.weather.day.sunday=Domingo
# 3d window
dialog.3d.title=GpsPrune vista 3-D
confirm.cutandmove=Mover Selecci\u00f3n
confirm.interpolate=Puntos insertados
confirm.convertnamestotimes=Nombres de "waypoint" convertidos
-confirm.saveexif.ok1=Guardado
-confirm.saveexif.ok2=fotos
+confirm.saveexif.ok=Guardado %d fotos
confirm.undo.single=operaci\u00f3n deshecha
confirm.undo.multi=operaci\u00f3n(es) deshechas(s)
confirm.jpegload.single=Foto incluida
confirm.createpoint=punto creado
confirm.rotatephoto=foto rotada
confirm.running=Trabajando ...
-confirm.lookupsrtm1=Encontrados
-confirm.lookupsrtm2=valor de altitud para la funci\u00f3n de b\u00fasqueda SRTM
+confirm.lookupsrtm=Encontrados %d valor de altitud para la funci\u00f3n de b\u00fasqueda SRTM
confirm.deletefieldvalues=Valores del campo eliminados
confirm.audioload=A\u00f1adidos archivos de audio
confirm.correlateaudios.single=El audio fue correlacionado
confirm.correlateaudios.multi=Los audios fueron correlacionados
+# Tips
+tip.title=Sugerencia
+tip.manuallycorrelateone=Correlacionando al menos una foto manualmente, el margen de tiempo se calcula autom\u00e1ticamente.
+
# Buttons
button.ok=Aceptar
button.back=Anterior
fieldname.custom=Personalizado
fieldname.prefix=Campo
fieldname.distance=Distancia
-fieldname.movingdistance=Distancia en movimiento
fieldname.duration=Duraci\u00f3n
fieldname.speed=Velocidad
fieldname.verticalspeed=Velocidad vertical
units.degmin=Gra-min
units.deg=Grados
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=y
# External urls
url.googlemaps=maps.google.es
wikipedia.lang=es
+openweathermap.lang=sp
# Cardinals for 3d plots
cardinal.n=N
error.saveexif.filenotfound=Archivo no encontrado
error.saveexif.cannotoverwrite1=No se puede guardar
error.saveexif.cannotoverwrite2=es s\u00f3lo-lectura y no se puede sobreescribir. Guardar a una copia?
-error.saveexif.failed1=Fall\u00f3 al guardar
-error.saveexif.failed2=de las im\u00e1genes
-error.saveexif.forced1=
-error.saveexif.forced2=de las im\u00e1genes requiere forzar
+error.saveexif.failed=Fall\u00f3 al guardar %d de las im\u00e1genes
+error.saveexif.forced=%d de las im\u00e1genes requiere forzar
error.load.dialogtitle=Fallo al cargar datos
error.load.noread=No se puede leer el fichero
error.load.nopoints=No se encuentra ninguna informaci\u00f3n de coordenadas en el archivo
function.saveconfig=Enregistrer les pr\u00e9f\u00e9rences
function.diskcache=Enregistrer les cartes sur le disque
function.managetilecache=Gestion du cache des dalles de cartes
+function.getweatherforecast=Obtenir une pr\u00e9vision m\u00e9t\u00e9orologique
# Dialogs
dialog.exit.confirm.title=Quitter GpsPrune
dialog.correlate.select.photoname=Nom de la photo
dialog.correlate.select.timediff=Diff\u00e9rence de temps
dialog.correlate.select.photolater=Photo prise plus tard
-dialog.correlate.options.tip=Astuce : En corr\u00e9lant manuellement au moins une photo, le d\u00e9calage de temps peut \u00eatre calcul\u00e9 pour vous.
dialog.correlate.options.intro=S\u00e9lectionner les options pour la corr\u00e9lation automatique
dialog.correlate.options.offsetpanel=D\u00e9calage de temps
dialog.correlate.options.offset=D\u00e9calage
dialog.compress.douglaspeucker.title=Compression Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=Taille du voisinage
dialog.compress.summarylabel=Points \u00e0 supprimer
-dialog.compress.confirm1=
-dialog.compress.confirm2=point(s) marqu\u00e9(s).\nTrace->Supprimer les points marqu\u00e9s pour les supprimer
+dialog.compress.confirm=%d point(s) marqu\u00e9(s).\nSupprimer les points?
dialog.compress.confirmnone=Pas de points marqu\u00e9s
dialog.deletemarked.nonefound=Pas de donn\u00e9es \u00e0 effacer
dialog.pastecoordinates.desc=Entrez ou collez les coordonn\u00e9es ici
dialog.saveconfig.prune.mapsourcelist=Sources de cartes
dialog.saveconfig.prune.diskcache=Cache de carte
dialog.saveconfig.prune.kmzimagewidth=Largeur de l'image KMZ
+dialog.saveconfig.prune.kmzimageheight=Hauteur de l'image KMZ
dialog.saveconfig.prune.colourscheme=Mod\u00e8le de couleurs
dialog.saveconfig.prune.linewidth=Largeur de ligne
dialog.saveconfig.prune.kmltrackcolour=Couleur de la trace KML
dialog.diskcache.deleteold=Efface vieilles dalles
dialog.diskcache.maximumage=\u00e2ge maxi (jours)
dialog.diskcache.deleteall=Efface toutes les dalles
-dialog.diskcache.deleted1=Effac\u00e9
-dialog.diskcache.deleted2=dalles en cache
+dialog.diskcache.deleted=Effac\u00e9 %d dalles en cache
dialog.deletefieldvalues.intro=Choisir le champ \u00e0 effacer pour l'\u00e9tendue actuelle
dialog.deletefieldvalues.nofields=L'\u00e9tendue actuelle n'a pas de champs \u00e0 effacer
dialog.setlinewidth.text=Entrer l'\u00e9paisseur des lignes des traces (1-4)
dialog.downloadosm.desc=Confirmer le t\u00e9l\u00e9chargement des donn\u00e9es OSM brutes pour la zone indiqu\u00e9e :
dialog.searchwikipedianames.search=Chercher :
+dialog.weather.location=Location
+dialog.weather.update=Mise \u00e0 jour
+dialog.weather.sunrise=Lever du soleil
+dialog.weather.sunset=Coucher du soleil
+dialog.weather.temperatureunits=Temp\u00e9ratures
+dialog.weather.currentforecast=Temps actuel
+dialog.weather.dailyforecast=Pr\u00e9vision par jour
+dialog.weather.3hourlyforecast=Pr\u00e9vision par 3 heures
+dialog.weather.day.now=Temps actuel
+dialog.weather.day.today=Aujourd'hui
+dialog.weather.day.tomorrow=Demain
+dialog.weather.day.monday=Lundi
+dialog.weather.day.tuesday=Mardi
+dialog.weather.day.wednesday=Mercredi
+dialog.weather.day.thursday=Jeudi
+dialog.weather.day.friday=Vendredi
+dialog.weather.day.saturday=Samedi
+dialog.weather.day.sunday=Dimanche
# 3d window
dialog.3d.title=Vue 3D de GpsPrune
confirm.cutandmove=S\u00e9lection d\u00e9plac\u00e9e
confirm.interpolate=Points ajout\u00e9s
confirm.convertnamestotimes=Noms de waypoints convertis
-confirm.saveexif.ok1=Enregistr\u00e9
-confirm.saveexif.ok2=fichiers photo
+confirm.saveexif.ok=Enregistr\u00e9 %d fichiers photo
confirm.undo.single=op\u00e9ration annul\u00e9e
confirm.undo.multi=op\u00e9rations annul\u00e9es
confirm.jpegload.single=la photo a \u00e9t\u00e9 ajout\u00e9e
confirm.createpoint=Point cr\u00e9\u00e9
confirm.rotatephoto=Photo tourn\u00e9e
confirm.running=En cours...
-confirm.lookupsrtm1=Trouv\u00e9
-confirm.lookupsrtm2=valeurs d'altitude
+confirm.lookupsrtm=Trouv\u00e9 %d valeurs d'altitude
confirm.deletefieldvalues=Valeurs effac\u00e9es
confirm.audioload=Fichiers audio ajout\u00e9s
confirm.correlateaudios.single=fichier audio a \u00e9t\u00e9 corr\u00e9l\u00e9
confirm.correlateaudios.multi=fichiers audio ont \u00e9t\u00e9 corr\u00e9l\u00e9s
+# Tips
+tip.title=Astuce
+tip.manuallycorrelateone=En corr\u00e9lant manuellement au moins une photo, le d\u00e9calage de temps peut \u00eatre calcul\u00e9 pour vous.
+
# Buttons
button.ok=OK
button.back=Retour
fieldname.custom=Personnalis\u00e9
fieldname.prefix=Champ
fieldname.distance=Distance
-fieldname.movingdistance=Distance continue
fieldname.duration=Dur\u00e9e
fieldname.speed=Vitesse
fieldname.verticalspeed=Vitesse verticale
# External urls
url.googlemaps=maps.google.fr
wikipedia.lang=fr
+openweathermap.lang=fr
# Cardinals for 3d plots
cardinal.n=N
error.saveexif.filenotfound=Fichier photo introuvable
error.saveexif.cannotoverwrite1=Le fichier photo
error.saveexif.cannotoverwrite2=est en lecture seule et ne peut pas \u00eatre \u00e9craser. Enregistrer sur une copie ?
-error.saveexif.failed1=\u00c9chec de la sauvegarde de
-error.saveexif.failed2=images
-error.saveexif.forced1=Enregistrement forc\u00e9 pour
-error.saveexif.forced2=images
+error.saveexif.failed=\u00c9chec de la sauvegarde de %d images
+error.saveexif.forced=Enregistrement forc\u00e9 pour %d images
error.load.dialogtitle=Erreur au chargement des donn\u00e9es
error.load.noread=Fichier illisible
error.load.nopoints=Aucune coordonn\u00e9e trouv\u00e9e dans le fichier
menu.file.recentfiles=Legut\u00f3bbi f\u00e1jlok
menu.file.save=Ment\u00e9s sz\u00f6vegk\u00e9nt
menu.file.exit=Kil\u00e9p\u00e9s
+menu.online=Online
menu.track=Nyomvonal
menu.track.undo=Visszavon\u00e1s
menu.track.clearundo=Visszavon\u00e1si lista t\u00f6rl\u00e9se
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=O
altkey.menu.track=V
altkey.menu.range=T
altkey.menu.point=P
function.exportgpx=Export\u00e1l\u00e1s GPX-be
function.exportpov=Export\u00e1l\u00e1s POV-ba
function.exportsvg=Export\u00e1l\u00e1s SVG-be
+function.exportimage=Export\u00e1l\u00e1s k\u00e9pbe
function.editwaypointname=\u00datpont nev\u00e9nek szerkeszt\u00e9se
function.compress=Nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se
function.deleterange=Tartom\u00e1ny t\u00f6rl\u00e9se
function.show3d=3D n\u00e9zet
function.distances=T\u00e1vols\u00e1gok
function.fullrangedetails=Teljes tartom\u00e1ny r\u00e9szletei
+function.estimatetime=Becs\u00fclt id\u0151
+function.learnestimationparams=Id\u0151becsl\u00e9s tanul\u00e1s\u00e1nak param\u00e9terei
function.setmapbg=H\u00e1tt\u00e9rk\u00e9p be\u00e1ll\u00edt\u00e1sa
function.setpaths=Program\u00fatvonalak be\u00e1ll\u00edt\u00e1sa
+function.splitsegments=Nyomvonal kett\u00e9v\u00e1g\u00e1sa szakaszokk\u00e1
+function.sewsegments=Nyomvonalszakaszok \u00f6sszevon\u00e1sa
function.getgpsies=Gpsies nyomvonalak let\u00f6lt\u00e9se
function.uploadgpsies=Nyomvonal felt\u00f6lt\u00e9se Gpsiesra
function.lookupsrtm=Magass\u00e1gok let\u00f6lt\u00e9se SRTM-r\u0151l
+function.downloadsrtm=SRTM csemp\u00e9k let\u00f6lt\u00e9se
function.getwikipedia=K\u00f6zeli Wikip\u00e9dia sz\u00f3cikkek let\u00f6lt\u00e9se
function.searchwikipedianames=Keres\u00e9s a Wikip\u00e9di\u00e1ban n\u00e9v szerint
function.downloadosm=OSM adatok let\u00f6lt\u00e9se a ter\u00fcletr\u0151l
function.saveconfig=Be\u00e1ll\u00edt\u00e1sok ment\u00e9se
function.diskcache=T\u00e9rk\u00e9pek ment\u00e9se lemezre
function.managetilecache=Csempegyors\u00edt\u00f3t\u00e1r kezel\u00e9se
+function.getweatherforecast=Id\u0151j\u00e1r\u00e1s el\u0151rejelz\u00e9s
# Dialogs
dialog.exit.confirm.title=Kil\u00e9p\u00e9s a GpsPrune-b\u00f3l
dialog.openappend.title=Hozz\u00e1f\u0171z\u00e9s a megl\u00e9v\u0151 adatokhoz
dialog.openappend.text=Hozz\u00e1f\u0171zi ezeket az adatokat a m\u00e1r bet\u00f6lt\u00f6tt adatokhoz?
dialog.deletepoint.title=Pont t\u00f6rl\u00e9se
-dialog.deletepoint.deletephoto=T\u00f6rli a f\u00e9nyk\u00e9pet, amely ehhez a ponthoz tartozik?
+dialog.deletepoint.deletephoto=T\u00f6rli a ponthoz tartoz\u00f3 f\u00e9nyk\u00e9pet?
dialog.deletephoto.title=F\u00e9nyk\u00e9p t\u00f6rl\u00e9se
-dialog.deletephoto.deletepoint=T\u00f6rli a pontot, amely ehhez a f\u00e9nyk\u00e9phez tartozik?
-dialog.deleteaudio.deletepoint=T\u00f6rli a pontot, amely ehhez a hangf\u00e1jlhoz tartozik?
+dialog.deletephoto.deletepoint=T\u00f6rli a f\u00e9nyk\u00e9phez tartoz\u00f3 pontot?
+dialog.deleteaudio.deletepoint=T\u00f6rli a hangf\u00e1jlhoz tartoz\u00f3 pontot?
dialog.openoptions.title=Be\u00e1ll\u00edt\u00e1sok megnyit\u00e1sa
dialog.openoptions.filesnippet=F\u00e1jl kivonata
dialog.load.table.field=Mez\u0151
dialog.openoptions.deliminfo.records=rekord
dialog.openoptions.deliminfo.fields=mez\u0151vel
dialog.openoptions.deliminfo.norecords=Nincsenek rekordok
-dialog.openoptions.altitudeunits=Magass\u00e1g egys\u00e9ge
+dialog.openoptions.altitudeunits=Magass\u00e1g m\u00e9rt\u00e9kegys\u00e9ge
+dialog.openoptions.speedunits=Sebess\u00e9g m\u00e9rt\u00e9kegys\u00e9ge
+dialog.openoptions.vertspeedunits=F\u00fcgg\u0151leges sebess\u00e9g m\u00e9rt\u00e9kegys\u00e9ge
+dialog.openoptions.vspeed.positiveup=Pozit\u00edv sebess\u00e9g m\u00e9rt\u00e9kegys\u00e9ge
+dialog.openoptions.vspeed.positivedown=Pozit\u00edv sebess\u00e9g lefel\u00e9
dialog.open.contentsdoubled=Ez a f\u00e1jl minden egyes pont k\u00e9t p\u00e9ld\u00e1ny\u00e1t tartalmazza,\negyszer mint \u00fatpont, m\u00e1sodszor mint nyompont.
dialog.selecttracks.intro=Nyomvonal vagy nyomvonalak kiv\u00e1laszt\u00e1sa bet\u00f6lt\u00e9shez
dialog.selecttracks.noname=N\u00e9vtelen
dialog.gpssend.sendwaypoints=\u00datpontok k\u00fcld\u00e9se
dialog.gpssend.sendtracks=Nyomvonalak k\u00fcld\u00e9se
dialog.gpssend.trackname=Nyomvonal neve
+dialog.gpsbabel.filters=Sz\u0171r\u0151k
+dialog.addfilter.title=Sz\u0171r\u0151 hozz\u00e1ad\u00e1sa
+dialog.gpsbabel.filter.discard=Mell\u0151z\u00e9s
+dialog.gpsbabel.filter.simplify=Egyszer\u0171s\u00edt\u00e9s
+dialog.gpsbabel.filter.distance=T\u00e1vols\u00e1g
+dialog.gpsbabel.filter.interpolate=Interpol\u00e1l\u00e1s
+dialog.gpsbabel.filter.discard.intro=Pontok kihagy\u00e1sa, ha
+dialog.gpsbabel.filter.discard.hdop=Hdop >
+dialog.gpsbabel.filter.discard.vdop=Hdop >
+dialog.gpsbabel.filter.discard.numsats=M\u0171holdak sz\u00e1ma <
+dialog.gpsbabel.filter.discard.nofix=Fix n\u00e9lk\u00fcli pontok
+dialog.gpsbabel.filter.discard.unknownfix=Ismeretlen fix-es pontok
+dialog.gpsbabel.filter.simplify.intro=Pontok elt\u00e1vol\u00edt\u00e1sa am\u00edg
+dialog.gpsbabel.filter.simplify.maxpoints=Pontok sz\u00e1ma <
+dialog.gpsbabel.filter.simplify.maxerror=vagy hiba m\u00e9rete <
+dialog.gpsbabel.filter.simplify.crosstrack=lesodr\u00f3d\u00e1s
+dialog.gpsbabel.filter.simplify.length=t\u00e1vols\u00e1gk\u00fcl\u00f6nbs\u00e9g
+dialog.gpsbabel.filter.simplify.relative=relat\u00edv hdop
+dialog.gpsbabel.filter.distance.intro=Pont elt\u00e1vol\u00edt\u00e1sa, ha k\u00f6zel van egy kor\u00e1bbi ponthoz
+dialog.gpsbabel.filter.distance.distance=Ha a t\u00e1vols\u00e1g <
+dialog.gpsbabel.filter.distance.time=\u00e9s az id\u0151elt\u00e9r\u00e9s <
+dialog.gpsbabel.filter.interpolate.intro=Extra pontok beilleszt\u00e9se az \u00fatpontok k\u00f6z\u00e9
+dialog.gpsbabel.filter.interpolate.distance=Ha a t\u00e1vols\u00e1g >
+dialog.gpsbabel.filter.interpolate.time=vagy az id\u0151elt\u00e9r\u00e9s >
dialog.saveoptions.title=F\u00e1jl ment\u00e9se
dialog.save.fieldstosave=Mentend\u0151 mez\u0151k
dialog.save.table.field=Mez\u0151
dialog.save.table.hasdata=Tartalmaz adatot
dialog.save.table.save=Ment\u00e9s
dialog.save.headerrow=Fejl\u00e9csor a kimenetbe
-dialog.save.coordinateunits=Koordin\u00e1ta egys\u00e9ge
-dialog.save.altitudeunits=Magass\u00e1g egys\u00e9ge
+dialog.save.coordinateunits=Koordin\u00e1ta form\u00e1tuma
+dialog.save.altitudeunits=Magass\u00e1g m\u00e9rt\u00e9kegys\u00e9ge
dialog.save.timestampformat=Id\u0151b\u00e9lyeg form\u00e1tuma
dialog.save.overwrite.title=A f\u00e1jl m\u00e1r l\u00e9tezik
dialog.save.overwrite.text=Ez a f\u00e1jl m\u00e1r l\u00e9tezik. Biztos benne, hogy fel\u00fcl\u00edrja a f\u00e1jlt?
dialog.exportkml.altitude=Abszol\u00fat magass\u00e1gok (rep\u00fcl\u00e9shez)
dialog.exportkml.kmz=T\u00f6m\u00f6r\u00edt\u00e9s kmz f\u00e1jl k\u00e9sz\u00edt\u00e9s\u00e9hez
dialog.exportkml.exportimages=K\u00e9pminiat\u0171r\u00f6k export\u00e1l\u00e1sa kmz-be
+dialog.exportkml.imagesize=K\u00e9pm\u00e9ret
dialog.exportkml.trackcolour=Nyomvonal sz\u00edne
+dialog.exportkml.standardkml=Szabv\u00e1nyos KML
+dialog.exportkml.extendedkml=KML kib\u0151v\u00edt\u00e9se id\u0151b\u00e9lyegekkel
dialog.exportgpx.name=N\u00e9v
dialog.exportgpx.desc=Le\u00edr\u00e1s
dialog.exportgpx.includetimestamps=Id\u0151b\u00e9lyegek is
dialog.exportpov.ballsandsticks=Goly\u00f3k \u00e9s botok
dialog.exportpov.tubesandwalls=Cs\u00f6vek \u00e9s falak
dialog.3d.warningtracksize=Ez a nyomvonal nagy sz\u00e1m\u00fa pontot tartalmaz, amelyet a Java3D nem biztos, hogy meg tud jelen\u00edteni.\nBiztos benne, hogy folytatni szeretn\u00e9?
+dialog.3d.useterrain=Terep megjelen\u00edt\u00e9se
+dialog.3d.terraingridsize=R\u00e1csm\u00e9ret
+dialog.exportpov.baseimage=Alapk\u00e9p
+dialog.exportpov.cannotmakebaseimage=Az alapk\u00e9p nem \u00edrhat\u00f3
+dialog.baseimage.title=T\u00e9rk\u00e9p k\u00e9p
+dialog.baseimage.useimage=K\u00e9p haszn\u00e1lata
+dialog.baseimage.mapsource=T\u00e9rk\u00e9pforr\u00e1s
+dialog.baseimage.zoom=Zoom szint
+dialog.baseimage.incomplete=Hi\u00e1nyos k\u00e9p
+dialog.baseimage.tiles=Csemp\u00e9k
+dialog.baseimage.size=K\u00e9pm\u00e9ret
dialog.exportsvg.text=Param\u00e9terek kiv\u00e1laszt\u00e1sa az SVG exporthoz
dialog.exportsvg.phi=Ir\u00e1nysz\u00f6g \u03d5
dialog.exportsvg.theta=Emel\u00e9s sz\u00f6ge \u03b8
dialog.exportsvg.gradients=\u00c1tmenetek haszn\u00e1lata az \u00e1rny\u00e9kol\u00e1shoz
+dialog.exportimage.noimagepossible=A t\u00e9rk\u00e9p k\u00e9peit az export\u00e1l\u00e1shoz el\u0151bb lemezre kell menteni.
+dialog.exportimage.drawtrack=Nyomvonal rajzol\u00e1sa a t\u00e9rk\u00e9pen
+dialog.exportimage.drawtrackpoints=A nyomvonal pontjainak kirajzol\u00e1sa
+dialog.exportimage.textscalepercent=Sz\u00f6vegnagy\u00edt\u00e1si faktor (%)
dialog.pointtype.desc=A k\u00f6vetkez\u0151 pontt\u00edpusok ment\u00e9se:
dialog.pointtype.track=Nyompontok
dialog.pointtype.waypoint=\u00datpontok
dialog.clearundo.title=Visszavon\u00e1si lista t\u00f6rl\u00e9se
dialog.clearundo.text=Biztos benne, hogy t\u00f6r\u00f6lni szeretn\u00e9 a visszavon\u00e1si list\u00e1t?\nMinden visszavon\u00e1si inform\u00e1ci\u00f3 el fog veszni!
dialog.pointedit.title=Pont szerkeszt\u00e9se
-dialog.pointedit.text=V\u00e1lassza ki egyenk\u00e9nt a mez\u0151ket, amelyeket szerkeszteni szeretne, majd az \u00e9rt\u00e9k m\u00f3dos\u00edt\u00e1s\u00e1hoz haszn\u00e1lja a "Szerkeszt\u00e9s" gombot
+dialog.pointedit.intro=V\u00e1lassz ki egy mez\u0151t, hogy megn\u00e9zd \u00e9s szerkeszd az \u00e9rt\u00e9k\u00e9t
dialog.pointedit.table.field=Mez\u0151
+dialog.pointedit.nofield=Nincs kiv\u00e1lasztott mez\u0151
dialog.pointedit.table.value=\u00c9rt\u00e9k
dialog.pointnameedit.name=\u00datpont neve
dialog.pointnameedit.uppercase=NAGYBET\u0170S
dialog.fullrangedetails.intro=Itt vannak a r\u00e9szletei a kiv\u00e1lasztott tartom\u00e1nynak
dialog.fullrangedetails.coltotal=R\u00e9sekkel egy\u00fctt
dialog.fullrangedetails.colsegments=R\u00e9sek n\u00e9lk\u00fcl
+dialog.estimatetime.details=R\u00e9szletek
+dialog.estimatetime.gentle=Lank\u00e1s
+dialog.estimatetime.steep=Meredek
+dialog.estimatetime.climb=M\u00e1sz\u00e1s
+dialog.estimatetime.descent=Ereszked\u00e9s
+dialog.estimatetime.parameters=Param\u00e9terek
+dialog.estimatetime.parameters.timefor=Sz\u00fcks\u00e9ges id\u0151:
+dialog.estimatetime.results=Eredm\u00e9nyek
+dialog.estimatetime.results.estimatedtime=Becs\u00fclt id\u0151
+dialog.estimatetime.results.actualtime=Aktu\u00e1lis id\u0151
+dialog.estimatetime.error.nodistance=Az id\u0151becsl\u00e9shez \u00f6sszek\u00f6t\u00f6tt nyomvonalpontokra van sz\u00fcks\u00e9g a t\u00e1vols\u00e1g meghat\u00e1roz\u00e1s\u00e1hoz
+dialog.estimatetime.error.noaltitudes=A kijel\u00f6l\u00e9s nem tartalmaz magass\u00e1g inform\u00e1ci\u00f3t
+dialog.learnestimationparams.intro=A nyomvonalb\u00f3l sz\u00e1m\u00edtott param\u00e9terek
+dialog.learnestimationparams.averageerror=\u00c1tlagos hiba
+dialog.learnestimationparams.combine=A param\u00e9terek kombin\u00e1lhat\u00f3k a jelenlegi \u00e9rt\u00e9kekkel
+dialog.learnestimationparams.combinedresults=Kombin\u00e1lt eredm\u00e9nyek
+dialog.learnestimationparams.weight.100pccurrent=Jelenlegi \u00e9rt\u00e9kek megtart\u00e1sa
+dialog.learnestimationparams.weight.current=jelenlegi
+dialog.learnestimationparams.weight.calculated=sz\u00e1m\u00edtott
+dialog.learnestimationparams.weight.50pc=A jelenlegi \u00e9s a sz\u00e1m\u00edtott \u00e9rt\u00e9kek \u00e1tlaga
+dialog.learnestimationparams.weight.100pccalculated=Az \u00faj sz\u00e1m\u00edtott \u00e9rt\u00e9kek haszn\u00e1lata
dialog.setmapbg.intro=V\u00e1lassza ki az egyik t\u00e9rk\u00e9pforr\u00e1st, vagy adjon hozz\u00e1 egy \u00fajat
dialog.addmapsource.title=\u00daj t\u00e9rk\u00e9pforr\u00e1s hozz\u00e1ad\u00e1sa
dialog.addmapsource.sourcename=Forr\u00e1s neve
dialog.correlate.select.photoname=F\u00e9nyk\u00e9p neve
dialog.correlate.select.timediff=Id\u0151k\u00fcl\u00f6nbs\u00e9g
dialog.correlate.select.photolater=K\u00e9s\u0151bbi f\u00e9nyk\u00e9p
-dialog.correlate.options.tip=Tipp: legal\u00e1bb egy elem k\u00e9zzel t\u00f6rt\u00e9n\u0151 \u00f6sszekapcsol\u00e1s\u00e1val az id\u0151eltol\u00e1s kisz\u00e1m\u00edthat\u00f3.
dialog.correlate.options.intro=V\u00e1lassza ki az opci\u00f3kat az automatikus megfeleltet\u00e9shez
dialog.correlate.options.offsetpanel=Id\u0151eltol\u00e1s
dialog.correlate.options.offset=Eltol\u00e1s
dialog.rearrangephotos.nosort=Ne rendezze
dialog.rearrangephotos.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint
dialog.rearrangephotos.sortbytime=Rendez\u00e9s id\u0151 szerint
-dialog.deletemarked.nonefound=Nem t\u00e1vol\u00edthat\u00f3 el adatpont
dialog.compress.closepoints.title=K\u00f6zeli pontok elt\u00e1vol\u00edt\u00e1sa
dialog.compress.closepoints.paramdesc=Hat\u00f3t\u00e1vols\u00e1g
dialog.compress.wackypoints.title=Kisz\u00e1m\u00edthatatlan pontok elt\u00e1vol\u00edt\u00e1sa
dialog.compress.douglaspeucker.title=Douglas-Peucker t\u00f6m\u00f6r\u00edt\u00e9s
dialog.compress.douglaspeucker.paramdesc=T\u00f6m\u00f6r\u00edt\u00e9si t\u00e9nyez\u0151
dialog.compress.summarylabel=T\u00f6rlend\u0151 pontok
+dialog.compress.confirm=%d a pontok meg lettek jel\u00f6lve.\nJel\u00f6lt pontok t\u00f6rl\u00e9se?
dialog.compress.confirmnone=egy pont sem lett megjel\u00f6lve
dialog.deletemarked.nonefound=Nem t\u00e1vol\u00edthat\u00f3 el adatpont
dialog.pastecoordinates.desc=Adja meg vagy illessze be a koordin\u00e1t\u00e1kat ide
dialog.pastecoordinates.coords=Koordin\u00e1t\u00e1k
dialog.pastecoordinates.nothingfound=Ellen\u0151rizze a koordin\u00e1t\u00e1kat, \u00e9s pr\u00f3b\u00e1lja \u00fajra
-dialog.help.help=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a \n http://activityworkshop.net/software/gpsprune/\nwebhelyet.
+dialog.help.help=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a \n http://gpsprune.activityworkshop.net/\nwebhelyet.
dialog.about.version=Verzi\u00f3
dialog.about.build=Build
dialog.about.summarytext1=A GpsPrune egy program GPS vev\u0151kr\u0151l sz\u00e1rmaz\u00f3 adatok bet\u00f6lt\u00e9s\u00e9re, megjelen\u00edt\u00e9s\u00e9re \u00e9s szerkeszt\u00e9s\u00e9re.
dialog.about.summarytext2=Gnu GPL licenc alatt ker\u00fclt kiad\u00e1sra a szabad, ny\u00edlt, vil\u00e1gm\u00e9ret\u0171 haszn\u00e1lathoz \u00e9s fejleszt\u00e9shez.<br>M\u00e1sol\u00e1sa, terjeszt\u00e9se \u00e9s m\u00f3dos\u00edt\u00e1sa megengedett \u00e9s \u00f6szt\u00f6nz\u00f6tt<br>a mell\u00e9kelt <code>license.txt</code> f\u00e1jlban r\u00f6gz\u00edtett felt\u00e9telek szerint
dialog.about.summarytext3=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a <code style="font-weight:bold">http://activityworkshop.net/</code> webhelyet.
dialog.about.languages=El\u00e9rhet\u0151 nyelvek
-dialog.about.translatedby=Magyar sz\u00f6veg: Ball\u00f3 Gy\u00f6rgy
+dialog.about.translatedby=Magyar sz\u00f6veg: Ball\u00f3 Gy\u00f6rgy \u00e9s B\u00e1thory P\u00e9ter
dialog.about.systeminfo=Rendszerinform\u00e1ci\u00f3
dialog.about.systeminfo.os=Oper\u00e1ci\u00f3s rendszer
dialog.about.systeminfo.java=Java futtat\u00f3k\u00f6rnyezet
dialog.about.yes=Igen
dialog.about.no=Nem
dialog.about.credits=K\u00e9sz\u00edt\u0151k
-dialog.about.credits.code=GpsPrune k\u00f3dj\u00e1t \u00edrta:
+dialog.about.credits.code=A GpsPrune k\u00f3dj\u00e1t \u00edrta:
dialog.about.credits.exifcode=Exif k\u00f3d:
dialog.about.credits.icons=N\u00e9h\u00e1ny ikon sz\u00e1rmazik:
dialog.about.credits.translators=Ford\u00edt\u00f3k
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Az \u00faj verzi\u00f3 ekkor lett kiadva:
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Az \u00faj verzi\u00f3 let\u00f6lt\u00e9s\u00e9hez keresse fel a http://activityworkshop.net/software/gpsprune/download.html webhelyet.
+dialog.checkversion.download=Az \u00faj verzi\u00f3 let\u00f6lt\u00e9s\u00e9hez keresse fel a http://gpsprune.activityworkshop.net/download.html webhelyet.
dialog.keys.intro=A k\u00f6vetkez\u0151 gyorsbillenty\u0171k haszn\u00e1lhat\u00f3k az eg\u00e9r haszn\u00e1lata helyett
dialog.keys.keylist=<table><tr><td>Ny\u00edlbillenty\u0171k</td><td>T\u00e9rk\u00e9p mozgat\u00e1sa balra, jobbra, fel, le</td></tr><tr><td>Ctrl + bal, jobb ny\u00edl</td><td>El\u0151z\u0151 vagy k\u00f6vetkez\u0151 pont kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Ctrl + fel, le ny\u00edl</td><td>Nagy\u00edt\u00e1s vagy kicsiny\u00edt\u00e9s</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>El\u0151z\u0151, k\u00f6vetkez\u0151 szakasz kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Ctrl + Home, End</td><td>Els\u0151, utols\u00f3 pont kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Del</td><td>Jelenlegi pont t\u00f6rl\u00e9se</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.setpaths.intro=Ha sz\u00fcks\u00e9ges, kiv\u00e1laszthatja a k\u00fcls\u0151 alkalmaz\u00e1sok \u00fatvonalait:
dialog.setpaths.found=\u00datvonal megtal\u00e1lhat\u00f3?
dialog.addaltitude.noaltitudes=A kiv\u00e1lasztott tartom\u00e1ny nem tartalmaz magass\u00e1gi \u00e9rt\u00e9keket
-dialog.addaltitude.desc=Magass\u00e1ki eltol\u00e1s, amely hozz\u00e1adand\u00f3
+dialog.addaltitude.desc=Magass\u00e1gi eltol\u00e1s, amely hozz\u00e1adand\u00f3
dialog.lookupsrtm.overwritezeros=Fel\u00fcl\u00edrja a nulla magass\u00e1g \u00e9rt\u00e9ket?
dialog.setcolours.intro=A sz\u00edn m\u00f3dos\u00edt\u00e1s\u00e1hoz kattintson egy sz\u00ednfoltra
dialog.setcolours.background=H\u00e1tt\u00e9r
dialog.diskcache.deleteold=R\u00e9gi csemp\u00e9k t\u00f6rl\u00e9se
dialog.diskcache.maximumage=Maxim\u00e1lis kor (nap)
dialog.diskcache.deleteall=Az \u00f6sszes csempe t\u00f6rl\u00e9se
-dialog.diskcache.deleted1=
-dialog.diskcache.deleted2=f\u00e1jl t\u00f6r\u00f6lve a gyors\u00edt\u00f3t\u00e1rb\u00f3l
+dialog.diskcache.deleted=%d f\u00e1jl t\u00f6r\u00f6lve a gyors\u00edt\u00f3t\u00e1rb\u00f3l
dialog.deletefieldvalues.intro=V\u00e1lassza ki a t\u00f6rlend\u0151 mez\u0151t a jelenlegi tartom\u00e1nyban
+dialog.deletefieldvalues.nofields=Nincs t\u00f6r\u00f6lhet\u0151 mez\u0151 a tartom\u00e1nyban
dialog.setlinewidth.text=Adja meg a rajzoland\u00f3 vonalak vastags\u00e1g\u00e1t a nyomvonalak sz\u00e1m\u00e1ra (1-4)
dialog.downloadosm.desc=Nyers OSM adatok let\u00f6lt\u00e9s\u00e9nek meger\u0151s\u00edt\u00e9se a megadott ter\u00fcletre:
dialog.searchwikipedianames.search=Keres\u00e9s erre:
+dialog.weather.location=Helysz\u00edn
+dialog.weather.update=El\u0151rejelz\u00e9s friss\u00edtve
+dialog.weather.sunrise=Napkelte
+dialog.weather.sunset=Napnyugta
+dialog.weather.temperatureunits=H\u0151m\u00e9rs\u00e9klet
+dialog.weather.currentforecast=Jelenlegi id\u0151j\u00e1r\u00e1s
+dialog.weather.dailyforecast=Napi el\u0151rejelz\u00e9s
+dialog.weather.3hourlyforecast=3 \u00f3r\u00e1s el\u0151rejelz\u00e9s
+dialog.weather.day.now=Jelenlegi id\u0151j\u00e1r\u00e1s
+dialog.weather.day.today=Ma
+dialog.weather.day.tomorrow=Holnap
+dialog.weather.day.monday=H\u00e9tf\u0151
+dialog.weather.day.tuesday=Kedd
+dialog.weather.day.wednesday=Szerda
+dialog.weather.day.thursday=Cs\u00fct\u00f6rt\u00f6k
+dialog.weather.day.friday=P\u00e9ntek
+dialog.weather.day.saturday=Szombat
+dialog.weather.day.sunday=Vas\u00e1rnap
+dialog.weather.creditnotice=Az adatok az openweathermap.org-r\u00f3l sz\u00e1rmaznak. N\u00e1luk tov\u00e1bbi inform\u00e1ci\u00f3t tal\u00e1lsz.
# 3d window
dialog.3d.title=GpsPrune 3D n\u00e9zet
confirm.addaltitudeoffset=Magass\u00e1geltol\u00e1s hozz\u00e1adva
confirm.rearrangewaypoints=\u00datpontok \u00fajrarendezve
confirm.rearrangephotos=F\u00e9nyk\u00e9pek \u00fajrarendezve
+confirm.splitsegments=%d szakasz v\u00e1g\u00e1s elv\u00e9gezve
+confirm.sewsegments=%d szakasz egyes\u00edt\u00e9se elv\u00e9gezve
confirm.cutandmove=Kijel\u00f6l\u00e9s \u00e1thelyezve
confirm.interpolate=Pontok hozz\u00e1adva
confirm.convertnamestotimes=\u00datpont nevei konvert\u00e1lva
-confirm.saveexif.ok1=Mentve
-confirm.saveexif.ok2=k\u00e9pf\u00e1jl
+confirm.saveexif.ok=Mentve %d k\u00e9pf\u00e1jl
confirm.undo.single=m\u0171velet visszavonva
confirm.undo.multi=m\u0171velet visszavonva
confirm.jpegload.single=f\u00e9nyk\u00e9p hozz\u00e1adva
confirm.createpoint=pont l\u00e9trehozva
confirm.rotatephoto=f\u00e9nyk\u00e9p elforgatva
confirm.running=Futtat\u00e1s...
-confirm.lookupsrtm1=
-confirm.lookupsrtm2=magass\u00e1gi \u00e9rt\u00e9k tal\u00e1lhat\u00f3
+confirm.lookupsrtm=%d magass\u00e1gi \u00e9rt\u00e9k tal\u00e1lhat\u00f3
+confirm.downloadsrtm=%d f\u00e1jl let\u00f6lt\u00e9se gyors\u00edt\u00f3t\u00e1rba
+confirm.downloadsrtm.none=Nem kellett f\u00e1jlokat let\u00f6lteni, m\u00e1r gyors\u00edt\u00f3t\u00e1rban voltak.
confirm.deletefieldvalues=Mez\u0151 \u00e9rt\u00e9kei t\u00f6r\u00f6lve
confirm.audioload=Hangf\u00e1jl hozz\u00e1adva
confirm.correlateaudios.single=hangf\u00e1jl megfeleltetve
confirm.correlateaudios.multi=hangf\u00e1jl megfeleltetve
+# Tips, shown just once when appropriate
+tip.title=Tipp
+tip.useamapcache=Gyors\u00edt\u00f3t\u00e1r be\u00e1ll\u00edt\u00e1s\u00e1val (Be\u00e1ll\u00edt\u00e1sok -> T\u00e9rk\u00e9pek lemezre ment\u00e9se) gyors\u00edthatod a megjelen\u00edt\u00e9st \u00e9s cs\u00f6kkentheted az adatforgalmat.
+tip.learntimeparams=Az eredm\u00e9ny sokkal pontosabb lesz, ha be\u00e1ll\u00edtod a\n Nyomvonal -> Id\u0151becsl\u00e9s tanul\u00e1s\u00e1nak param\u00e9terei\n \u00e9rt\u00e9keit a r\u00f6gz\u00edtett nyomvonalhoz.
+tip.downloadsrtm=Gyors\u00edthatod a folyamatot, ha az adatokat lemezre mented:\n Online -> SRTM csemp\u00e9k let\u00f6lt\u00e9se
+tip.usesrtmfor3d=A nyomvonal nem tartalmaz magass\u00e1gadatokat.\nHaszn\u00e1lhatod az SRTM funkci\u00f3kat, hogy k\u00f6zel\u00edt\u0151 magass\u00e1gi \u00e9rt\u00e9keket kapj\na 3D n\u00e9zethez
+tip.manuallycorrelateone=Legal\u00e1bb egy elem k\u00e9zzel t\u00f6rt\u00e9n\u0151 \u00f6sszekapcsol\u00e1s\u00e1val az id\u0151eltol\u00e1s kisz\u00e1m\u00edthat\u00f3.
+
# Buttons
button.ok=OK
button.back=El\u0151z\u0151
button.no=Nem
button.yestoall=Igen, mindet
button.notoall=Nem, egyiket se
+button.always=Mindig
button.select=Kijel\u00f6l\u00e9s
button.selectall=Mindent kijel\u00f6l
button.selectnone=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
button.addnew=\u00daj hozz\u00e1ad\u00e1sa
button.delete=T\u00f6rl\u00e9s
button.manage=Kezel\u00e9s
+button.combine=Egyes\u00edt\u00e9s
# File types
filetype.txt=TXT f\u00e1jlok
display.nodata=Nincs adat bet\u00f6ltve
display.noaltitudes=A nyomvonal nem tartalmaz magass\u00e1gi adatot
display.notimestamps=A nyomvonal nem tartalmaz id\u0151b\u00e9lyegeket
+display.novalues=A nyomvonal nem tartalmaz \u00e9rt\u00e9keket ehhez a mez\u0151h\u00f6z
details.trackdetails=Nyomvonal r\u00e9szletei
details.notrack=Nincs adat bet\u00f6ltve
details.track.points=Pontok
fieldname.latitude=Sz\u00e9less\u00e9g
fieldname.longitude=Hossz\u00fas\u00e1g
fieldname.altitude=Magass\u00e1g
-fieldname.timestamp=Id\u00f5
+fieldname.timestamp=Id\u0151
fieldname.time=Id\u0151
fieldname.waypointname=N\u00e9v
fieldname.waypointtype=T\u00edpus
fieldname.custom=Egy\u00e9ni
fieldname.prefix=Mez\u0151
fieldname.distance=T\u00e1vols\u00e1g
-fieldname.movingdistance=Mozg\u00e1si t\u00e1vols\u00e1g
fieldname.duration=Id\u0151tartam
fieldname.speed=Sebess\u00e9g
fieldname.verticalspeed=F\u00fcgg\u0151leges sebess\u00e9g
units.feet.short=ft
units.kilometres=kilom\u00e9ter
units.kilometres.short=km
+units.kilometresperhour=km / \u00f3ra
units.kilometresperhour.short=km/h
units.miles=m\u00e9rf\u00f6ld
units.miles.short=mi
+units.milesperhour=m\u00e9rf\u00f6ld / \u00f3ra
units.milesperhour.short=mph
units.nauticalmiles=Tengeri m\u00e9rf\u00f6ld
units.nauticalmiles.short=nmi
units.nauticalmilesperhour.short=csom\u00f3
+units.metrespersec=m\u00e9ter / sec
units.metrespersec.short=m/s
+units.feetpersec=l\u00e1b / sec
units.feetpersec.short=ft/s
units.hours=\u00f3ra
+units.minutes=perc
+units.seconds=m\u00e1sodperc
units.degminsec=Sz\u00f6g-sz\u00f6gperc-sz\u00f6gm\u00e1sodperc
units.degmin=Sz\u00f6g-sz\u00f6gperc
units.deg=Sz\u00f6g
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
+
+# How to combine conditions, such as filters
+logic.and=\u00e9s
+logic.or=vagy
# External urls
url.googlemaps=maps.google.hu
undo.insert=pontok besz\u00far\u00e1sa
undo.reverse=tartom\u00e1ny megford\u00edt\u00e1sa
undo.mergetracksegments=nyomvonalszakaszok egyes\u00edt\u00e9se
+undo.splitsegments=nyomvonal szakaszokra v\u00e1g\u00e1sa
+undo.sewsegments=nyomvonalszakaszok egyes\u00edt\u00e9se
undo.addtimeoffset=id\u0151eltol\u00e1s hozz\u00e1ad\u00e1sa
undo.addaltitudeoffset=magass\u00e1geltol\u00e1s hozz\u00e1ad\u00e1sa
undo.rearrangewaypoints=\u00fatpontok \u00fajrarendez\u00e9se
error.saveexif.filenotfound=F\u00e9nyk\u00e9pf\u00e1jl keres\u00e9se nem siker\u00fclt
error.saveexif.cannotoverwrite1=A(z)
error.saveexif.cannotoverwrite2=f\u00e9nyk\u00e9pf\u00e1jl csak olvashat\u00f3, \u00e9s nem \u00edrhat\u00f3 fel\u00fcl. \u00cdrjuk m\u00e1solatba?
-error.saveexif.failed1=
-error.saveexif.failed2=k\u00e9p ment\u00e9se nem siker\u00fclt
-error.saveexif.forced1=
-error.saveexif.forced2=k\u00e9p er\u00f6ltet\u00e9st ig\u00e9nyelt
+error.saveexif.failed=%d k\u00e9p ment\u00e9se nem siker\u00fclt
+error.saveexif.forced=%d k\u00e9p er\u0151ltet\u00e9st ig\u00e9nyelt
error.load.dialogtitle=Hiba az adatok bet\u00f6lt\u00e9sekor
error.load.noread=A f\u00e1jl nem olvashat\u00f3
error.load.nopoints=Nem tal\u00e1lhat\u00f3 koordin\u00e1tainform\u00e1ci\u00f3 a f\u00e1jlban
error.jpegload.nofilesfound=Nem tal\u00e1lhat\u00f3 f\u00e1jl
error.jpegload.nojpegsfound=Nem tal\u00e1lhat\u00f3 jpeg f\u00e1jl
error.jpegload.nogpsfound=Nem tal\u00e1lhat\u00f3 GPS inform\u00e1ci\u00f3
-error.jpegload.exifreadfailed=Az Exif inform\u00e1ci\u00f3 olvas\u00e1sa nem siker\u00fclt. Nem olvasat\u00f3 Exif inform\u00e1ci\u00f3\nbe\u00e9p\u00edtett vagy k\u00fcls\u0151 f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r n\u00e9lk\u00fcl.
+error.jpegload.exifreadfailed=Az Exif inform\u00e1ci\u00f3 olvas\u00e1sa nem siker\u00fclt. Nem olvashat\u00f3 Exif inform\u00e1ci\u00f3\nbe\u00e9p\u00edtett vagy k\u00fcls\u0151 f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r n\u00e9lk\u00fcl.
error.audioload.nofilesfound=Nem tal\u00e1lhat\u00f3 hangf\u00e1jl
error.gpsload.unknown=Ismeretlen hiba
error.undofailed.title=A visszavon\u00e1s nem siker\u00fclt
error.cache.empty=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra \u00fcres
error.cache.cannotdelete=Nincs t\u00f6r\u00f6lhet\u0151 csempe
error.interpolate.invalidparameter=A pontok sz\u00e1ma 1 \u00e9s 1000 k\u00f6z\u00f6tt kell legyen
+error.learnestimationparams.failed=Nem lehet param\u00e9tereket tanulni ebb\u0151l a nyomvonalb\u00f3l.\nT\u00f6lts be t\u00f6bb nyomvonalat.
+error.tracksplit.nosplit=A nyomvonal nem elv\u00e1ghat\u00f3
+error.downloadsrtm.nocache=A f\u00e1jlokat nem siker\u00fclt meneni.\nK\u00e9rlek, ellen\u0151rizd a gyors\u00edt\u00f3t\u00e1rat.
+error.sewsegments.nothingdone=A szakaszokat nem lehet \u00f6sszef\u0171zni.\nA nyomvonal jelenleg %d szakaszt tartalmaz.
menu.file.recentfiles=Files recenti
menu.file.save=Salva
menu.file.exit=Esci
+menu.online=Online
menu.track=Traccia
menu.track.undo=Annulla
menu.track.clearundo=Cancella lista annulla
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=N
altkey.menu.track=T
altkey.menu.range=S
altkey.menu.point=P
function.distances=Mostra distanze
function.fullrangedetails=Mostra dettagli
function.estimatetime=Stima durata
-function.learnestimationparams=Apprendi paramet
+function.learnestimationparams=Apprendi parametri di stima
function.setmapbg=Configura sfondo mappa
function.setpaths=Configura percorsi programmi
+function.splitsegments=Dividi traccia in segmenti
+function.sewsegments=Riorganizza segmenti insieme
function.getgpsies=Ottieni traccie da Gpsies
function.uploadgpsies=Carica traccia su Gpsies
function.lookupsrtm=Ottieni quote da SRTM
+function.downloadsrtm=Scarica file da SRTM
function.getwikipedia=Ottieni informazioni da Wikipedia
function.searchwikipedianames=Cerca il nome in Wikipedia
function.downloadosm=Scarica dati OSM dell'area
function.saveconfig=Salva configurazione
function.diskcache=Salva mappe su disco
function.managetilecache=Gestione del cache di tasselli
+function.getweatherforecast=Ottieni previsioni del tempo
# Dialogs
dialog.exit.confirm.title=Esci da GpsPrune
dialog.exportgpx.encoding=Codifica caratteri
dialog.exportgpx.encoding.system=Impostazione di diffetto
dialog.exportgpx.encoding.utf8=UTF-8
+dialog.3d.useterrain=Mostra terreno
+dialog.3d.terraingridsize=Dimensione della griglia
dialog.exportpov.text=Per favore inserisci i parametri per l'esportazione in POV
dialog.exportpov.font=Font
dialog.exportpov.camerax=Camera X
dialog.exportsvg.gradients=Usa il gradiente per le ombre
dialog.exportimage.noimagepossible=Le mappe devono essere memorizzate su disco prima di poter essere esportate.
dialog.exportimage.drawtrack=Disegna traccia sulla mappa
+dialog.exportimage.drawtrackpoints=Disegna punti
dialog.exportimage.textscalepercent=Fattore di scala testo (%)
dialog.pointtype.desc=Salva i tipi di punti seguenti:
dialog.pointtype.track=Punti traccia
dialog.correlate.select.photoname=Nome della foto
dialog.correlate.select.timediff=Differenza di orario
dialog.correlate.select.photolater=Foto scattata dopo il punto
-dialog.correlate.options.tip=Consiglio: Con il collegamento manuale di almeno una foto, lo scarto di orario viene calcolato per te
dialog.correlate.options.intro=Selezione le opzioni per la correlazione automatica
dialog.correlate.options.offsetpanel=Scarto di orario
dialog.correlate.options.offset=Scarto
dialog.compress.douglaspeucker.title=Compressione con algoritmo Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=Fattore di apertura
dialog.compress.summarylabel=Punti da cancellare
-dialog.compress.confirm1=
-dialog.compress.confirm2=punti sono stati contrassegnati.\nUsa traccia-> Elimina i punti segnati per eliminarle
+dialog.compress.confirm=%d punti sono stati contrassegnati.\nElimina i punti?
dialog.compress.confirmnone=I punti non sono stati marcati
dialog.deletemarked.nonefound=Nessun punto rimosso
dialog.pastecoordinates.desc=Inserisci o incolla qui le coordinate
dialog.saveconfig.prune.mapsourcelist=Fonte delle mappe
dialog.saveconfig.prune.diskcache=Cache delle mappe
dialog.saveconfig.prune.kmzimagewidth=larghezza immagine KMZ
+dialog.saveconfig.prune.kmzimageheight=altezza immagine KMZ
dialog.saveconfig.prune.colourscheme=Schema colori
dialog.saveconfig.prune.linewidth=Spessore linea
dialog.saveconfig.prune.kmltrackcolour=Colore della traccia KML
dialog.diskcache.deleteold=Cancellare tasselli vecchi
dialog.diskcache.maximumage=Et\u00e0 massima (giorni)
dialog.diskcache.deleteall=Cancellare tutti tasselli
-dialog.diskcache.deleted1=Cancellati
-dialog.diskcache.deleted2=files dal cache
+dialog.diskcache.deleted=Cancellati %d files dal cache
dialog.deletefieldvalues.intro=Selezione il campo da cancellare dall'intervallo corrente
dialog.deletefieldvalues.nofields=Non ci sono campi da cancellare nell'intervallo corrente
dialog.setlinewidth.text=Specifica il tratteggio delle linee per disegnare la traccia (1-4)
dialog.downloadosm.desc=Conferma lo scarico dei dati raw OSM per l'area specificata:
dialog.searchwikipedianames.search=Cerca per:
+dialog.weather.location=Localit\u00e0
+dialog.weather.update=Aggiornata
+dialog.weather.sunrise=Levata del sole
+dialog.weather.sunset=Tramonto del sole
+dialog.weather.temperatureunits=Temperature
+dialog.weather.currentforecast=Tempo attuale
+dialog.weather.dailyforecast=Previsione quotidiano
+dialog.weather.3hourlyforecast=Previsione ogni 3 ore
+dialog.weather.day.now=Tempo attuale
+dialog.weather.day.today=Oggi
+dialog.weather.day.tomorrow=Domani
+dialog.weather.day.monday=Luned\u00ec
+dialog.weather.day.tuesday=Marted\u00ec
+dialog.weather.day.wednesday=Mercoled\u00ec
+dialog.weather.day.thursday=Gioved\u00ec
+dialog.weather.day.friday=Venerd\u00ec
+dialog.weather.day.saturday=Sabato
+dialog.weather.day.sunday=Domenica
# 3d window
dialog.3d.title=Visione GpsPrune in 3D
confirm.deletepoint.single=punto \u00e8 stato rimosso
confirm.deletepoint.multi=punti sono stati rimossi
confirm.point.edit=punto editato
-confirm.mergetracksegments=segmeni di traccia uniti
+confirm.mergetracksegments=segmenti di traccia uniti
confirm.reverserange=Intervallo invertito
confirm.addtimeoffset=Scarto temporale aggiunto
confirm.addaltitudeoffset=Scarto altitudine aggiunto
confirm.cutandmove=Selezione spostata
confirm.interpolate=Aggiungi punto
confirm.convertnamestotimes=Nome del waypoint convertito
-confirm.saveexif.ok1=Salvato
-confirm.saveexif.ok2=foto
+confirm.saveexif.ok=Salvato %d foto
confirm.undo.single=operazione annullate
confirm.undo.multi=operazioni annullate
confirm.jpegload.single=foto \u00e8 stata aggiunta
confirm.createpoint=punto creato
confirm.rotatephoto=foto ruotata
confirm.running=Operazione in corso...
-confirm.lookupsrtm1=Trovato
-confirm.lookupsrtm2=valori di quota
+confirm.lookupsrtm=Trovato %d valori di quota
confirm.deletefieldvalues=Valori del campo cancellati
confirm.audioload=Ripresa audio aggiunta
confirm.correlateaudios.single=la ripresa audio era correlata
confirm.correlateaudios.multi=le riprese audio erano correlate
+# Tips
+tip.title=Consiglio
+tip.manuallycorrelateone=Con il collegamento manuale di almeno una foto, lo scarto di orario viene calcolato per te
+
# Buttons
button.ok=OK
button.back=Precedente
button.no=No
button.yestoall=Si a tutto
button.notoall=No a tutto
+button.always=Sempre
button.select=Seleziona
button.selectall=Seleziona tutto
button.selectnone=Deseleziona tutto
units.degmin=Deg-min
units.deg=Degrees
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=e
# External urls
url.googlemaps=maps.google.it
wikipedia.lang=it
+openweathermap.lang=it
# Cardinals for 3d plots
cardinal.n=N
undo.insert=inserisci punti
undo.reverse=inverti l'intervallo
undo.mergetracksegments=unisci segmenti traccia
+undo.splitsegments=dividi traccia
+undo.sewsegments=riorganizza segmenti traccia
undo.addtimeoffset=aggiungi scarto temporale
undo.addaltitudeoffset=aggiungi scarto altitudine
undo.rearrangewaypoints=riorganizza waypoint
error.saveexif.filenotfound=Non trovato un file di foto
error.saveexif.cannotoverwrite1=File di foto
error.saveexif.cannotoverwrite2=\u00e8 in sola lettura e non pu\u00f2 essere sovrascritto. Creo una copia?
-error.saveexif.failed1=Salvataggio fallito
-error.saveexif.failed2=delle immagini
-error.saveexif.forced1=
-error.saveexif.forced2=delle immagini richiede forzatura
+error.saveexif.failed=Salvataggio fallito%d delle immagini
+error.saveexif.forced=%d delle immagini richiede forzatura
error.load.dialogtitle=Errore nel caricamento dati
error.load.noread=Non posso leggere il file
error.load.nopoints=Non ci sono coordinate nel file
dialog.correlate.select.photoname=\u5199\u771f\u540d
dialog.correlate.select.timediff=\u6642\u9593\u5dee
dialog.correlate.select.photolater=\u5199\u771f\u304c\u5f8c
-dialog.correlate.options.tip=\u63d0\u793a\uff1a\u5c11\u306a\u304f\u3068\u3082\u4e00\u3064\u306e\u5199\u771f\u3092\u624b\u52d5\u3067\u95a2\u9023\u4ed8\u3051\u308b\u3068\u3001\u6642\u9593\u504f\u4f4d\u3092\u8a08\u7b97\u3059\u308b\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002
+tip.manuallycorrelateone=\u63d0\u793a\uff1a\u5c11\u306a\u304f\u3068\u3082\u4e00\u3064\u306e\u5199\u771f\u3092\u624b\u52d5\u3067\u95a2\u9023\u4ed8\u3051\u308b\u3068\u3001\u6642\u9593\u504f\u4f4d\u3092\u8a08\u7b97\u3059\u308b\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002
dialog.correlate.options.intro=\u81ea\u52d5\u95a2\u9023\u4ed8\u3051\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002
dialog.correlate.options.offsetpanel=\u6642\u9593\u504f\u4f4d
dialog.correlate.options.offset=\u504f\u4f4d
dialog.pastecoordinates.desc=\u5ea7\u6a19\u3092\u3053\u3053\u306b\u5165\u529b\u3059\u308b\u304b\u30da\u30fc\u30b9\u30c8\u3057\u3066\u304f\u3060\u3055\u3044\u3002
dialog.pastecoordinates.coords=\u5ea7\u6a19
dialog.pastecoordinates.nothingfound=\u5ea7\u6a19\u306e\u78ba\u8a8d\u3092\u3057\u3066\u3001\u3082\u3046\u4e00\u5ea6\u8a66\u3057\u3066\u304f\u3060\u3055\u3044\u3002
-dialog.help.help=\u8a73\u3057\u3044\u60c5\u5831\u3084\u3001\u30e6\u30fc\u30b6\u30fc\u30ac\u30a4\u30c9\u306a\u3069\u306f\u3001\nhttp://activityworkshop.net/software/gpsprune/ \u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.help.help=\u8a73\u3057\u3044\u60c5\u5831\u3084\u3001\u30e6\u30fc\u30b6\u30fc\u30ac\u30a4\u30c9\u306a\u3069\u306f\u3001\nhttp://gpsprune.activityworkshop.net/ \u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002
dialog.about.version=\u30d0\u30fc\u30b8\u30e7\u30f3
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune \u306f\u3001GPS\u53d7\u4fe1\u6a5f\u304b\u3089\u306e\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u307f\u3001\u8868\u793a\u3057\u3001\u7de8\u96c6\u3059\u308b\u305f\u3081\u306e\u30d7\u30ed\u30b0\u30e9\u30e0\u3067\u3059\u3002
dialog.checkversion.newversion2=\u3067\u3059\u3002
dialog.checkversion.releasedate1=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306f\u3001
dialog.checkversion.releasedate2=\u306b\u30ea\u30ea\u30fc\u30b9\u3057\u307e\u3057\u305f\u3002
-dialog.checkversion.download=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f\u3001 http://activityworkshop.net/software/gpsprune/download.html \u3078\u884c\u3063\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.checkversion.download=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f\u3001 http://gpsprune.activityworkshop.net/download.html \u3078\u884c\u3063\u3066\u304f\u3060\u3055\u3044\u3002
dialog.keys.intro=\u30de\u30a6\u30b9\u306e\u4ee3\u308f\u308a\u306b\u6b21\u306e\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\u30ad\u30fc\u3092\u4f7f\u3046\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002
dialog.keys.keylist=<table><tr><td>\u77e2\u5370\u30ad\u30fc</td><td>\u5730\u56f3\u3092\u4e0a\u4e0b\u5de6\u53f3\u306b\u79fb\u52d5</td></tr><tr><td>Ctrl + \u5de6\u30fb\u53f3\u77e2\u5370</td><td>\u524d\u30fb\u6b21\u306e\u70b9\u3092\u9078\u629e</td></tr><tr><td>Ctrl + \u4e0a\u30fb\u4e0b\u77e2\u5370</td><td>\u62e1\u5927\u30fb\u7e2e\u5c0f</td></tr><tr><td>Del</td><td>\u73fe\u5728\u306e\u70b9\u3092\u524a\u9664</td></tr></table>
dialog.keys.normalmodifier=Ctrl\u30ad\u30fc
dialog.saveconfig.prune.mapsource=\u30de\u30c3\u30d7\u30fb\u30bd\u30fc\u30b9\u3092\u9078\u629e
dialog.saveconfig.prune.mapsourcelist=\u30de\u30c3\u30d7\u30fb\u30bd\u30fc\u30b9
dialog.saveconfig.prune.diskcache=\u30de\u30c3\u30d7\u306e\u30ad\u30e3\u30c3\u30b7\u30e5
-dialog.saveconfig.prune.kmzimagewidth=KMZ \u753b\u50cf\u5e45
+dialog.saveconfig.prune.kmzimagewidth=KML \u753b\u50cf\u5e45
+dialog.saveconfig.prune.kmzimageheight=KML \u753b\u50cf\u9ad8
dialog.saveconfig.prune.colourscheme=\u8272\u306e\u30b9\u30ad\u30fc\u30e0
dialog.saveconfig.prune.linewidth=\u7dda\u306e\u5e45
dialog.saveconfig.prune.kmltrackcolour=KML \u30c8\u30e9\u30c3\u30af\u306e\u8272
confirm.rearrangephotos=\u5199\u771f\u304c\u4e26\u3079\u66ff\u3048\u3089\u308c\u305f
confirm.cutandmove=\u9078\u629e\u304c\u52d5\u304b\u3055\u308c\u305f
confirm.convertnamestotimes=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u306e\u540d\u524d\u304c\u5909\u63db\u3055\u308c\u305f
-confirm.saveexif.ok1=\u4fdd\u5b58\u3055\u308c\u305f
-confirm.saveexif.ok2=\u5199\u771f\u30d5\u30a1\u30a4\u30eb
+confirm.saveexif.ok=\u4fdd\u5b58\u3055\u308c\u305f %d \u5199\u771f\u30d5\u30a1\u30a4\u30eb
confirm.undo.single=\u64cd\u4f5c\u306f\u30a2\u30f3\u30c9\u30a5\u3055\u308c\u305f
confirm.undo.multi=\u64cd\u4f5c\u306f\u30a2\u30f3\u30c9\u30a5\u3055\u308c\u305f
confirm.jpegload.single=\u5199\u771f\u304c\u52a0\u3048\u3089\u308c\u305f
confirm.createpoint=\u70b9\u304c\u4f5c\u3089\u308c\u305f
confirm.rotatephoto=\u5199\u771f\u3092\u56de\u8ee2\u3057\u305f
confirm.running=\u5b9f\u884c\u4e2d...
-confirm.lookupsrtm1=
-confirm.lookupsrtm2=\u6a19\u9ad8\u5024
+confirm.lookupsrtm=%d \u6a19\u9ad8\u5024
confirm.deletefieldvalues=\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u5024\u304c\u524a\u9664\u3055\u308c\u305f
confirm.audioload=\u30aa\u30fc\u30c7\u30a3\u30aa\u30d5\u30a1\u30a4\u30eb\u304c\u8ffd\u52a0\u3055\u308c\u305f
confirm.correlateaudios.single=\u30aa\u30fc\u30c7\u30a3\u30aa\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
fieldname.custom=\u30ab\u30b9\u30bf\u30e0
fieldname.prefix=\u30d5\u30a3\u30fc\u30eb\u30c9
fieldname.distance=\u8ddd\u96e2
-fieldname.movingdistance=\u79fb\u52d5\u8ddd\u96e2
fieldname.duration=\u9593\u9694
fieldname.speed=\u901f\u5ea6
fieldname.verticalspeed=\u5782\u76f4\u901f\u5ea6
error.saveexif.filenotfound=\u5199\u771f\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f
error.saveexif.cannotoverwrite1=\u5199\u771f\u30d5\u30a1\u30a4\u30eb
error.saveexif.cannotoverwrite2=\u8aad\u307f\u8fbc\u307f\u5c02\u7528\u3067\u4e0a\u66f8\u304d\u3067\u304d\u307e\u305b\u3093\u3002\u8907\u88fd\u3092\u4f5c\u308a\u307e\u3059\u304b\uff1f
-error.saveexif.failed1=
-error.saveexif.failed2=\u679a\u306e\u753b\u50cf\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f
-error.saveexif.forced1=
-error.saveexif.forced2=\u679a\u306e\u753b\u50cf\u304c\u5f37\u884c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002
+error.saveexif.failed=%d \u679a\u306e\u753b\u50cf\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+error.saveexif.forced=%d \u679a\u306e\u753b\u50cf\u304c\u5f37\u884c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002
error.load.dialogtitle=\u30c7\u30fc\u30bf\u306e\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc
error.load.noread=\u30d5\u30a1\u30a4\u30eb\u304c\u8aad\u3081\u307e\u305b\u3093
error.load.nopoints=\u30d5\u30a1\u30a4\u30eb\u306e\u4e2d\u306b\u5ea7\u6a19\u60c5\u5831\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
dialog.correlate.select.photoname=\uc0ac\uc9c4\uc774\ub984
dialog.correlate.select.timediff=\uc0ac\uae34 \ucc28\uc774
dialog.correlate.select.photolater=\uc0ac\uc9c4 \uc2dc\uac04\uc774 \uc9c0\uc810\uc2dc\uac04\ubcf4\ub2e4 \ub290\ub9bc
-dialog.correlate.options.tip=\ub3c4\uc6c0: \ucd5c\uc18c\ud55c \ud55c\uac1c\uc758 \uc544\uc774\ud0ec(\uc0ac\uc9c4,\uc18c\ub9ac)\uc744 \uc9c1\uc811 \uc5f0\uacb0\ud558\uba74, \ud0c0\uc784 \uc624\ud504\uc14b\uc744 \uacc4\uc0b0\ud560 \uc218 \uc788\uc5b4\uc694.
dialog.correlate.options.intro=\uc790\ub3d9 \uc5f0\uacb0\uc744 \uc704\ud574 \uc635\uc158\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694.
dialog.correlate.options.offsetpanel=\uc2dc\uac04 \uc624\ud504\uc14b
dialog.correlate.options.offset=\uc624\ud504\uc14b
dialog.pastecoordinates.desc=\uc790\ud45c\ub97c \ub123\uc73c\uc138\uc694
dialog.pastecoordinates.coords=\uc88c\ud45c
dialog.pastecoordinates.nothingfound=\uc88c\ud45c\ub97c \ud655\uc778\ud558\uc2dc\uace0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574 \ubcf4\uc138\uc694.
-dialog.help.help=\ub354 \uc790\uc138\ud55c \uc815\ubcf4\ub098 \uc0ac\uc6a9\uc790\uc124\uba85\uc740 \uc774\uacf3\uc744 \ucc38\uace0\ud574\uc8fc\uc138\uc694.\n http://activityworkshop.net/software/gpsprune/
+dialog.help.help=\ub354 \uc790\uc138\ud55c \uc815\ubcf4\ub098 \uc0ac\uc6a9\uc790\uc124\uba85\uc740 \uc774\uacf3\uc744 \ucc38\uace0\ud574\uc8fc\uc138\uc694.\n http://gpsprune.activityworkshop.net/
dialog.about.version=\ubc84\uc804
dialog.about.build=\ube4c\ub4dc
dialog.about.summarytext1=GpsPrune\uc740 GPS\uc218\uc2e0\uae30\uc5d0\uc11c \uc704\uce58 \uc815\ubcf4\ub97c \ubc1b\uace0, \ud654\uba74\uc5d0 \ubcf4\uc5ec\uc8fc\uace0, \uc218\uc815\ud558\uac8c \ud574\uc8fc\ub294 \ud504\ub85c\uadf8\ub7a8\uc785\ub2c8\ub2e4.
dialog.checkversion.newversion2=\ubc84\uc804\uc785\ub2c8\ub2e4.
dialog.checkversion.releasedate1=\uc0c8 \ubc84\uc804\uc740
dialog.checkversion.releasedate2=\uc5d0 \ubc30\ud3ec\ub418\uc5c8\uc2b5\ub2c8\ub2e4.
-dialog.checkversion.download=\uc0c8 \ubc84\uc804\uc744 \ub2e4\uc6b4\ubc1b\uace0 \uc2f6\uc73c\uc138\uc694? \uadf8\ub7fc \uc544\ub798 URL\ub85c \uc640\uc8fc\uc138\uc694. /n http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=\uc0c8 \ubc84\uc804\uc744 \ub2e4\uc6b4\ubc1b\uace0 \uc2f6\uc73c\uc138\uc694? \uadf8\ub7fc \uc544\ub798 URL\ub85c \uc640\uc8fc\uc138\uc694. /n http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=\ub9c8\uc6b0\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \ub9c8\uc2dc\uace0 \uc544\ub798 \ub2e8\ucd95\ud0a4\ub97c \uc0ac\uc6a9\ud574\ubcf4\uc138\uc694.
dialog.keys.keylist=<table><tr><td>\ud654\uc0b4\ud45c \ud0a4</td><td>\uc88c,\uc6b0,\uc544\ub798,\uc704\ub85c \uc9c0\ub3c4 \uc774\ub3d9</td></tr><tr><td>Ctrl + \uc67c\ucabd, \uc624\ub978\ucabd \ud654\uc0b4\ud45c</td><td>\uc9c0\uc810 \uc120\ud0dd \uc774\ub3d9</td></tr><tr><td>Ctrl + \uc704, \uc544\ub798 \ud654\uc0b4\ud45c</td><td>\uc9c0\ub3c4 \ud655\ub300 \ucd95\uc18c</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>\uc774\uc804 \uc774\ud6c4 \ubd80\ubd84 \uc120\ud0dd</td></tr><tr><td>Ctrl + Home, End</td><td>\ucc98\uc74c \uc9c0\uc810, \ub9c8\uc9c0\ub9c9 \uc9c0\uc810 \uc120\ud0dd</td></tr><tr><td>Del</td><td>\ud604\uc7ac \uc9c0\uc810 \uc0ad\uc81c</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.saveconfig.prune.mapsourcelist=\uc9c0\ub3c4 \uc18c\uc2a4
dialog.saveconfig.prune.diskcache=\uc9c0\ub3c4 \uce90\uc2dc
dialog.saveconfig.prune.kmzimagewidth=KMZ \uc774\ubbf8\uc9c0 \ub113\uc774
+dialog.saveconfig.prune.kmzimageheight=KMZ \uc774\ubbf8\uc9c0 \ub192\uc774
dialog.saveconfig.prune.colourscheme=\uc0c9 \uad6c\uc131
dialog.saveconfig.prune.linewidth=\ud2b8\ub799\uc120 \ub450\uaed8
dialog.saveconfig.prune.kmltrackcolour=KML \ud2b8\ub799 \uc0c9
confirm.rearrangephotos=\uc0ac\uc9c4\ub4e4\uc774 \uc7ac\uc815\ub82c\ub418\uc5c8\uc5b4\uc694.
confirm.cutandmove=\uc62e\uaca8\uc84c\uc5b4\uc694.
confirm.convertnamestotimes=\uacbd\uc720\uc9c0 \uc774\ub984\ub4e4\uc774 \ubcc0\ud658\ub418\uc5c8\uc5b4\uc694.
-confirm.saveexif.ok1=\uc800\uc7a5\ub428
-confirm.saveexif.ok2=\uc0ac\uc9c4 \ud30c\uc77c\ub4e4
+confirm.saveexif.ok=\uc800\uc7a5\ub428 %d \uc0ac\uc9c4 \ud30c\uc77c\ub4e4
confirm.undo.single=\uac1c \uc2e4\ud589\ucde8\uc18c \ub3d9\uc791
confirm.undo.multi=\uac1c \uc2e4\ud589\ucde8\uc18c \ub3d9\uc791
confirm.jpegload.single=\uc0ac\uc9c4\uc774 \ucd94\uac00\ub428
confirm.createpoint=\uc9c0\uc810 \uc0dd\uc131\ub428
confirm.rotatephoto=\uc0ac\uc9c4 \ub3cc\ub824\uc9d0
confirm.running=\uc2e4\ud589\uc911
-confirm.lookupsrtm1=
-confirm.lookupsrtm2=\uace0\ub3c4\uac12 \ubc1c\uacac
+confirm.lookupsrtm=%d \uace0\ub3c4\uac12 \ubc1c\uacac
confirm.deletefieldvalues=\ud544\ub4dc\uac12 \uc9c0\uc6cc\uc9d0
confirm.audioload=\uc18c\ub9ac\ud30c\uc77c \ucd94\uac00\ub428
confirm.correlateaudios.single=\uc18c\ub9ac \uc5f0\uacb0\ub428
confirm.correlateaudios.multi=\uc18c\ub9ac\ub4e4 \uc5f0\uacb0\ub428
+# Tips
+tip.title=\ub3c4\uc6c0
+tip.manuallycorrelateone=\ucd5c\uc18c\ud55c \ud55c\uac1c\uc758 \uc544\uc774\ud0ec(\uc0ac\uc9c4,\uc18c\ub9ac)\uc744 \uc9c1\uc811 \uc5f0\uacb0\ud558\uba74, \ud0c0\uc784 \uc624\ud504\uc14b\uc744 \uacc4\uc0b0\ud560 \uc218 \uc788\uc5b4\uc694.
+
# Buttons
button.ok=\ud655\uc778
button.back=\ub4a4\ub85c
fieldname.custom=\ucee4\uc2a4\ud140
fieldname.prefix=\ud544\ub4dc
fieldname.distance=\uac70\ub9ac
-fieldname.movingdistance=\uc6c0\uc9c0\uc778\uac70\ub9ac
fieldname.duration=\uae30\uac04
fieldname.speed=\uc18d\ub3c4
fieldname.verticalspeed=\uc218\uc9c1\uc18d\ub3c4
error.saveexif.filenotfound=\uc0ac\uc9c4\ud30c\uc77c \ucc3e\uae30 \uc2e4\ud328
error.saveexif.cannotoverwrite1=\uc0ac\uc9c4\ud30c\uc77c
error.saveexif.cannotoverwrite2=\uc774 \uc77d\uae30\uc804\uc6a9\uc774\ub124\uc694, \ub36e\uc5b4\uc4f8\uc218 \uc5c6\uc5b4\uc694 \ubcf5\uc0ac\ud574\uc11c \uc4f8\uae4c\uc694?
-error.saveexif.failed1=
-error.saveexif.failed2=\uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc2e4\ud328
-error.saveexif.forced1=
-error.saveexif.forced2=\uc774\ubbf8\uc9c0\uac00 \uac15\uc81c\ub97c \uc694\uad6c\ud569\ub2c8\ub2e4.
+error.saveexif.failed=%d \uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc2e4\ud328
+error.saveexif.forced=%d \uc774\ubbf8\uc9c0\uac00 \uac15\uc81c\ub97c \uc694\uad6c\ud569\ub2c8\ub2e4.
error.load.dialogtitle=\uc790\ub8cc \ubd88\ub7ec\uc624\ub2e4\uac00 \uc5d0\ub7ec
error.load.noread=\ud30c\uc77c\uc744 \uc77d\uc744 \uc218 \uc5c6\ub124\uc694.
error.load.nopoints=\ud30c\uc77c\uc5d0 \uc88c\ud45c \uc815\ubcf4\uac00 \uc5c6\uc5b4\uc694.
menu.file.recentfiles=Onlangs geopend
menu.file.save=Opslaan als tekst
menu.file.exit=Afsluiten
+menu.online=Online
menu.track=Route
menu.track.undo=Ongedaan maken
menu.track.clearundo=Ongedaan-maken lijst wissen
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=O
altkey.menu.track=R
altkey.menu.range=E
altkey.menu.point=P
function.learnestimationparams=Parameters voor geschatte tijd
function.setmapbg=Instellen kaart achtergrond
function.setpaths=Instellen programmapaden
+function.splitsegments=Splits route in segmenten
+function.sewsegments=Voeg segmenten samen
function.getgpsies=Routes van Gpsies ophalen
function.uploadgpsies=Upload routes naar Gpsies
function.lookupsrtm=Hoogtes van SRTM ophalen
+function.downloadsrtm=Downloaden SRTM tegels
function.getwikipedia=Wikipedia artikelen uit de buurt ophalen
function.searchwikipedianames=Wikipedia zoeken op naam
function.downloadosm=Downloaden OSM data voor gebied
function.saveconfig=Instellingen opslaan
function.diskcache=Kaart opslaan op schijf
function.managetilecache=Beheer tegelcache
+function.getweatherforecast=Ophalen weersvoorspelling
# Dialogs
dialog.exit.confirm.title=GpsPrune afsluiten
dialog.exportpov.ballsandsticks=Balletjes en stokjes
dialog.exportpov.tubesandwalls=Buizen en muren
dialog.3d.warningtracksize=Deze route heeft een groot aantal punten. Java3D kan deze mogelijk niet tonen.\nWeet u zeker dat u door wilt gaan?
+dialog.3d.useterrain=Toon reli\u00ebf
+dialog.3d.terraingridsize=Rasterformaat
dialog.exportpov.baseimage=Basisafbeelding
dialog.exportpov.cannotmakebaseimage=Kan basisafbeelding niet opslaan
dialog.baseimage.title=Basisafbeelding
dialog.exportsvg.gradients=Gebruik gradaties voor schaduw
dialog.exportimage.noimagepossible=Kaartafbeeldingen dienen gecached te worden naar disk om ze te kunnen exporteren.
dialog.exportimage.drawtrack=Teken route op kaart
+dialog.exportimage.drawtrackpoints=Teken routepunten
dialog.exportimage.textscalepercent=Tekstschaal factor (%)
dialog.pointtype.desc=Sla de volgende punttypen op:
dialog.pointtype.track=Routepunten
dialog.correlate.select.photoname=Fotonaam
dialog.correlate.select.timediff=Tijdsverschil
dialog.correlate.select.photolater=Foto later
-dialog.correlate.options.tip=Tip: Door handmatig een foto te koppelen kan het tijdsverschil voor u berekend worden.
dialog.correlate.options.intro=Selecteer de opties voor automatisch koppelen
dialog.correlate.options.offsetpanel=Tijdverschil
dialog.correlate.options.offset=Verschil
dialog.compress.douglaspeucker.title=Douglas-Peucker compressie
dialog.compress.douglaspeucker.paramdesc=Span factor
dialog.compress.summarylabel=Te verwijderen punten
-dialog.compress.confirm1=Er zijn
-dialog.compress.confirm2=punten zijn gemarkeerd.\nGebruik Track - Verwijder gemarkeerde punten om ze te verwijderen
+dialog.compress.confirm=Er zijn %d punten zijn gemarkeerd.\nVerwijder gemarkeerde punten?
dialog.compress.confirmnone=er zijn geen punten gemarkeerd
dialog.deletemarked.nonefound=Er konden geen punten verwijderd worden
dialog.pastecoordinates.desc=Geef co\u00f6rdinaten in
dialog.pastecoordinates.coords=Co\u00f6rdinaten
dialog.pastecoordinates.nothingfound=Controleer de co\u00f6rdinaten en probeer het nogmaals
-dialog.help.help=Ga naar\n http://activityworkshop.net/software/gpsprune/\nvoor meer informatie en handleidingen.
+dialog.help.help=Ga naar\n http://gpsprune.activityworkshop.net/\nvoor meer informatie en handleidingen.
dialog.about.version=Versie
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune is een programma voor het laden, tonen en wijzigen van data uit GPS ontvangers.
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Deze nieuwe versie was vrijgegeven op
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Om de nieuwst versie te downloaden, ga naar http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Om de nieuwst versie te downloaden, ga naar http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=De volgende sneltoetsen vervangen muisacties
dialog.keys.keylist=<table><tr><td>Pijltjestoetsen</td><td>verschuif de kaart links, rechts, omhoog, omlaag</td></tr><tr><td>Ctrl + pijltje naar links, rechts</td><td>Selecteer volgende, vorige punt</td></tr><tr><td>Ctrl + pijltje omhoog, omlaag</td><td>Zoom in of uit</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Selecteer vorig, volgend segment</td></tr><tr><td>Ctrl + Home, End</td><td>Select eerste, laatste punt</td></tr><tr><td>Del</td><td>Verwijder huidige punt</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.diskcache.deleteold=Verwijder oude tegels
dialog.diskcache.maximumage=Maximum leeftijd (dagen)
dialog.diskcache.deleteall=Verwijder alle tegels
-dialog.diskcache.deleted1=
-dialog.diskcache.deleted2=bestanden uit de cache verwijderd
+dialog.diskcache.deleted=%d bestanden uit de cache verwijderd
dialog.deletefieldvalues.intro=Selecteer het te verwijderen veld voor de huidige reeks
dialog.deletefieldvalues.nofields=Er zijn geen velden in deze reeks om te verwijderen
dialog.setlinewidth.text=Geef lijndikte voor routes (1-4)
dialog.downloadosm.desc=Bevestig het downloaden van ruwe OSM data voor dit gebied:
dialog.searchwikipedianames.search=Zoeken naar:
+dialog.weather.location=Locatie
+dialog.weather.update=Verwachting bijgewerkt
+dialog.weather.sunrise=Zonsopkomst
+dialog.weather.sunset=Zonsondergang
+dialog.weather.temperatureunits=Temperaturen
+dialog.weather.currentforecast=Huidig weer
+dialog.weather.dailyforecast=Dagelijkse voorspelling
+dialog.weather.3hourlyforecast=3-uurse voorspelling
+dialog.weather.day.now=Huidig weer
+dialog.weather.day.today=Vandaag
+dialog.weather.day.tomorrow=Morgen
+dialog.weather.day.monday=Maandag
+dialog.weather.day.tuesday=Dinsdag
+dialog.weather.day.wednesday=Woensdag
+dialog.weather.day.thursday=Donderdag
+dialog.weather.day.friday=Vrijdag
+dialog.weather.day.saturday=Zaterdag
+dialog.weather.day.sunday=Zondag
+dialog.weather.creditnotice=Deze gegevens worden beschikbaar gesteld door openweathermap.org. Hun website heeft meer details.
# 3d window
dialog.3d.title=GpsPrune in 3D
confirm.addaltitudeoffset=Hoogteverschil toegevoegd
confirm.rearrangewaypoints=Waypoints herschikt
confirm.rearrangephotos=Foto herschikt
+confirm.splitsegments=Er zijn %d opdelingen gemaakt
+confirm.sewsegments=Er zijn %d samenvoegingen gemaakt
confirm.cutandmove=Selectie verplaatst
confirm.interpolate=Punten toegevoegd
confirm.convertnamestotimes=Namen waypoint geconverteerd
-confirm.saveexif.ok1=Opgeslagen
-confirm.saveexif.ok2=foto bestanden
+confirm.saveexif.ok=Opgeslagen %d foto bestanden
confirm.undo.single=Actie geannuleerd
confirm.undo.multi=Acties geannuleerd
confirm.jpegload.single=foto was toegevoegd
confirm.createpoint=punt aangemaakt
confirm.rotatephoto=foto geroteerd
confirm.running=Bezig...
-confirm.lookupsrtm1=Gevonden
-confirm.lookupsrtm2=hoote waarden
+confirm.lookupsrtm=Gevonden %d hoote waarden
+confirm.downloadsrtm=Er zijn %d bestanden gedownload nar de cache
+confirm.downloadsrtm.none=Geen bestanden gedownload, waren al aanwezig in de cache.
confirm.deletefieldvalues=Veldwaarden gewist
confirm.audioload=Audiobestanden toegevoegd
confirm.correlateaudios.single=audiobestand gecorreleerd
confirm.correlateaudios.multi=audiobestanden gecorreleerd
+# Tips, shown just once when appropriate
+tip.title=Tip
+tip.useamapcache=Door het instellen van een schijfcache (Instellingen -> Kaart opslaan op schijf<span style="color: #ff0000;">)\nkan je de afbeeldsnelheid verbeteren en het netwerkverkeer verminderen.</span>
+tip.learntimeparams=De resultaten zullen nauwkeuriger zijn als je \nRoute -> Parameters voor geschatte tijd\ngebruikt op je opgenomen routes.
+tip.downloadsrtm=Je kan dit versnellen door hier\nOnline -> Download SRTM tegels\nde data in je kaartcache op te slaan.
+tip.usesrtmfor3d=Deze route heeft geen hoogten.\nJe kan de SRTM functies gebruiken om een geschatte hoogte\nop te halen voor het 3d beeld.
+tip.manuallycorrelateone=Door handmatig een foto te koppelen kan het tijdsverschil voor u berekend worden.
+
# Buttons
button.ok=OK
button.back=Terug
button.no=Nee
button.yestoall=Ja op alles
button.notoall=Nee op alles
+button.always=Altijd
button.select=Selecteer
button.selectall=Selecteer alles
button.selectnone=Selecteer niets
fieldname.custom=Custom
fieldname.prefix=Veld
fieldname.distance=Afstand
-fieldname.movingdistance=Snelheid in beweging
fieldname.duration=Duur
fieldname.speed=Snelheid
fieldname.verticalspeed=Verticale snelheid
units.degmin=Grd-min
units.deg=Graden
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreesfahrenheit=Fahrenheit
# How to combine conditions, such as filters
logic.and=en
# External urls
url.googlemaps=maps.google.nl
wikipedia.lang=nl
+openweathermap.lang=nl
# Cardinals for 3d plots
cardinal.n=N
undo.insert=punten invoegen
undo.reverse=reeks omkeren
undo.mergetracksegments=samenvoegen route segmenten
+undo.splitsegments=opdelen routesegmenten
+undo.sewsegments=verbinden routesegmenten
undo.addtimeoffset=tijdsverschil toevoegen
undo.addaltitudeoffset=hoogteverschil toevoegen
undo.rearrangewaypoints=herschikken waypoint
error.saveexif.filenotfound=Fotobestand niet gevonden
error.saveexif.cannotoverwrite1=Fotobestand
error.saveexif.cannotoverwrite2=is alleen-lezen en kan niet worden overschreven. Wegschrijven naar kopie?
-error.saveexif.failed1=Kon
-error.saveexif.failed2=van de foto's niet wegschrijven
-error.saveexif.forced1=
-error.saveexif.forced2=van de foto's hadden "forcing" nodig
+error.saveexif.failed=Kon %d van de foto's niet wegschrijven
+error.saveexif.forced=%d van de foto's hadden "forcing" nodig
error.load.dialogtitle=Fout gegevens laden
error.load.noread=Kan bestand niet lezen
error.load.nopoints=Geen co\u00f6rdinaat informatie gevonden in bestand
error.cache.cannotdelete=Er konden geen tegels verwijderd worden
error.interpolate.invalidparameter=Aantal punten moet tussen 1 en 1000 liggen
error.learnestimationparams.failed=Kan geen parameters bepalen van deze route.\nProbeer meer routes te laden.
+error.tracksplit.nosplit=Deze route kon niet opgedeeld worden
+error.downloadsrtm.nocache=De bestanden konden niet worden opgeslagen.\nControleer de schijfcache.
+error.sewsegments.nothingdone=Er konden geen segmenten worden samengevoegd.\nEr zijn nu %d segmenten in de route.
function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies
function.uploadgpsies=Wy\u015blij \u015bcie\u017cki do Gpsies
function.lookupsrtm=Pobierz wysoko\u015bci z SRTM
+function.downloadsrtm=Zapisz dane z SRTM
function.getwikipedia=Szukaj w Wikipedii o okolicy
function.searchwikipedianames=Szukaj nazwy w Wikipedii
function.downloadosm=Za\u0142aduj dane obszaru z OSM
function.saveconfig=Zapisz ustawienia
function.diskcache=Zapisz mapy na dysk
function.managetilecache=Zarz\u0105dzaj keszem p\u0142ytek
+function.getweatherforecast=Pobierz prognoza pogody
# Dialogs
dialog.exit.confirm.title=Zako\u0144cz GpsPrune
dialog.gpsbabel.filter.simplify.crosstrack=skrzy\u017cowane \u015bcie\u017cki
dialog.gpsbabel.filter.simplify.length=d\u0142ugo\u015b\u0107 r\u00f3\u017cnicy
dialog.gpsbabel.filter.simplify.relative=powi\u0105zan z Hdop
-dialog.gpsbabel.filter.distance.intro=Usu\u0144 punkty je\u015bli poprzednie punkt
+dialog.gpsbabel.filter.distance.intro=Usu\u0144 punkty je\u015bli poprzednie punkty wystarczaj\u0105co blisko
dialog.gpsbabel.filter.distance.distance=Je\u015bli odleg\u0142o\u015b\u0107 <
dialog.gpsbabel.filter.distance.time=i r\u00f3\u017cnica w czasie <
dialog.gpsbabel.filter.interpolate.intro=Dodaj ekstra punkty pomi\u0119dzy punktami \u015bcie\u017cki
dialog.exportkml.imagesize=Rozmiar zdj\u0119\u0107
dialog.exportkml.trackcolour=Kolor \u015bcie\u017cki
dialog.exportkml.standardkml=Standardowy KML
-dialog.exportkml.extendedkml=KML ze znacznikami czasu
+dialog.exportkml.extendedkml=Standardowy KML ze znacznikami czasu
dialog.exportgpx.name=Nazwa
dialog.exportgpx.desc=Opis
dialog.exportgpx.includetimestamps=Do\u0142\u0105cz znaczniki czasu
dialog.exportsvg.gradients=U\u017cyj gradientu jako wype\u0142nienia
dialog.exportimage.noimagepossible=Obrazy map musz\u0105 zosta\u0107 zapisane na dysku przed ich eksportem
dialog.exportimage.drawtrack=Rysuj \u015bcie\u017ck\u0119 na mapie
+dialog.exportimage.drawtrackpoints=Rysuj punkty
dialog.exportimage.textscalepercent=Wsp\u00f3\u0142czynnik skali tekstu (%)
dialog.pointtype.desc=Zapisz punkty nast\u0119puj\u0105cych typ\u00f3w:
dialog.pointtype.track=punkty \u015bcie\u017cki
dialog.correlate.select.photoname=Nazwa zdj\u0119cia
dialog.correlate.select.timediff=R\u00f3\u017cnica czasowa
dialog.correlate.select.photolater=P\u00f3\u017aniejsze zdj\u0119cie
-dialog.correlate.options.tip=Porada: Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie.
dialog.correlate.options.intro=Wybierz opcje dla automatycznej korelacji
dialog.correlate.options.offsetpanel=Przesuni\u0119cie czasowe
dialog.correlate.options.offset=Przesuni\u0119cie
dialog.compress.douglaspeucker.title=kompresja Douglasa-Peuckera
dialog.compress.douglaspeucker.paramdesc=wsp\u00f3\u0142czynnik rozpi\u0119to\u015bci (szeroko\u015bci korytarza)
dialog.compress.summarylabel=Punkty do usuni\u0119cia
-dialog.compress.confirm1=
-dialog.compress.confirm2=punkt\u00f3w zosta\u0142o zaznaczonych\nU\u017cyj \u015acie\u017cka->Usu\u0144 zaznaczone punkty, by je usun\u0105\u0107
+dialog.compress.confirm=%d punkt\u00f3w zosta\u0142o zaznaczonych\nUsu\u0144 zaznaczone punkty?
dialog.compress.confirmnone=\u017cadne punkty nie zosta\u0142y zaznaczone
dialog.deletemarked.nonefound=Nie mo\u017cna usun\u0105\u0107 \u017cadnych punkt\u00f3w
dialog.pastecoordinates.desc=Wprowad\u017a lub wklej wsp\u00f3\u0142rz\u0119dne
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Nowa wersja zosta\u0142a opublikowana
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Aby \u015bci\u0105gn\u0105\u0107 now\u0105 wersj\u0119 przejd\u017a na http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Aby \u015bci\u0105gn\u0105\u0107 now\u0105 wersj\u0119 przejd\u017a na http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=U\u017cuwaj nast\u0119puj\u0105cych klawiszy skr\u00f3t\u00f3w zamiast myszki
dialog.keys.keylist=<table><tr><td>klawisze strza\u0142ek</td><td>Przesuwa map\u0119 w lewo, w prawo, w g\u00f3r\u0119, w d\u00f3\u0142</td></tr><tr><td>Ctrl + lewa, prawa strza\u0142ka</td><td>Wybierz punkt poprzedni lub nast\u0119pny</td></tr><tr><td>Ctrl + strza\u0142ka w g\u00f3r\u0119, w d\u00f3\u0142</td><td>Powi\u0119ksz, pomniejsz</td></tr><tr><td>Del</td><td>Usun bie\u017c\u0105cy punkt</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.diskcache.deleteold=Usu\u0144 stare p\u0142ytki
dialog.diskcache.maximumage=Maksymalny wiek (w dniach)
dialog.diskcache.deleteall=Usu\u0144 wszystkie p\u0142ytki
-dialog.diskcache.deleted1=Usuni\u0119to
-dialog.diskcache.deleted2=plik\u00f3w z kesza
+dialog.diskcache.deleted=Usuni\u0119to %d plik\u00f3w z kesza
dialog.deletefieldvalues.intro=Wybierz pola do skasowania z wybranego zakresu
dialog.deletefieldvalues.nofields=Brak p\u00f3l do skasowania dla tego zakresu
dialog.setlinewidth.text=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek
dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM:
dialog.searchwikipedianames.search=Szukaj
+dialog.weather.day.now=Aktualny
+dialog.weather.day.today=Dzisiaj
+dialog.weather.day.tomorrow=Jutro
+dialog.weather.day.monday=Poniedzia\u0142ek
+dialog.weather.day.tuesday=Wtorek
+dialog.weather.day.wednesday=\u015Aroda
+dialog.weather.day.thursday=Czwartek
+dialog.weather.day.friday=Pi\u0105tek
+dialog.weather.day.saturday=Sobota
+dialog.weather.day.sunday=Niedziela
# 3d window
dialog.3d.title=GpsPrune widok tr\u00f3jwymiarowy
confirm.cutandmove=Przesuni\u0119to zaznaczenie
confirm.interpolate=Dodano punkty
confirm.convertnamestotimes=Zmieniono nazwy punkt\u00f3w po\u015brednich
-confirm.saveexif.ok1=Zapisano
-confirm.saveexif.ok2=plik(\u00f3w) zdj\u0119\u0107
+confirm.saveexif.ok=Zapisano %d plik(\u00f3w) zdj\u0119\u0107
confirm.undo.single=cofni\u0119to operacj\u0119
confirm.undo.multi=operacje zosta\u0142y cofni\u0119te
confirm.jpegload.single=dodano zdj\u0119cie
confirm.createpoint=stworzono punkt
confirm.rotatephoto=obr\u00f3cono zdj\u0119cie
confirm.running=Przetwarzam dane ...
-confirm.lookupsrtm1=Znaleziono
-confirm.lookupsrtm2=warto\u015bci wysoko\u015bci
+confirm.lookupsrtm=Znaleziono %d warto\u015bci wysoko\u015bci
confirm.deletefieldvalues=Warto\u015bci p\u00f3l usuni\u0119to
confirm.audioload=dodano pliki audio
confirm.correlateaudios.single=audio zosta\u0142o po\u0142\u0105czone
confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone
+# Tips
+tip.title=Porada
+tip.manuallycorrelateone=Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie.
+
# Buttons
button.ok=OK
button.back=Poprzedni
button.no=Nie
button.yestoall=Tak na wszystko
button.notoall=Nie na wszystko
+button.always=Zawsze
button.select=Zaznacz
button.selectall=Zaznacz wszystko
button.selectnone=Odznacz
fieldname.custom=U\u017cytkownika
fieldname.prefix=Pole
fieldname.distance=Odleg\u0142o\u015b\u0107
-fieldname.movingdistance=Odleg\u0142o\u015b\u0107 przesuni\u0119cia
fieldname.duration=Czas trwania
fieldname.speed=Pr\u0119dko\u015b\u0107
fieldname.verticalspeed=Pr\u0119dko\u015b\u0107 pionowa
units.degmin=Sto-min
units.deg=Stopnie
units.iso8601=ISO 8601
+units.degreescelsius=Celsjusza
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheita
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=i
# External urls
url.googlemaps=maps.google.pl
wikipedia.lang=pl
+openweathermap.lang=pl
# Cardinals for 3d plots
cardinal.n=N
error.saveexif.filenotfound=Nie znaleziono pliku ze zdj\u0119ciem
error.saveexif.cannotoverwrite1=Plik ze zdj\u0119ciem
error.saveexif.cannotoverwrite2=jest w trybie tylko do odczytu i nie mo\u017ce zosta\u0107 nadpisany. Zapisa\u0107 kopi\u0119?
-error.saveexif.failed1=B\u0142\u0105d zapisu
-error.saveexif.failed2=zdj\u0119\u0107
-error.saveexif.forced1=
-error.saveexif.forced2=zdj\u0119\u0107 z wymuszonym zapisem
+error.saveexif.failed=B\u0142\u0105d zapisu %d zdj\u0119\u0107
+error.saveexif.forced=%d zdj\u0119\u0107 z wymuszonym zapisem
error.load.dialogtitle=B\u0142\u0105d \u0142adowania danych
error.load.noread=Nie mo\u017cna przeczyta\u0107 pliku
error.load.nopoints=Nie znaleziono informacji o wsp\u00f3\u0142rz\u0119dnych w pliku
menu.file.recentfiles=Arquivos recentes
menu.file.save=Salvar
menu.file.exit=Sair
+menu.online=Online
menu.track=Rota
menu.track.undo=Desfazer
menu.track.clearundo=Limpar lista de desfazer
+menu.track.markrectangle=Marcar pontos no ret\u00e2ngulo
menu.track.deletemarked=Remover pontos marcados
menu.track.rearrange=Rearrumar pontos
menu.track.rearrange.start=Tudo para o in\u00edcio do arquivo
menu.range.none=N\u00e3o selecionar nenhuns
menu.range.start=Definir in\u00edcio do intervalo
menu.range.end=Definir fim do intervalo
-function.deleterange=Remover intervalo
-function.interpolate=Interpolar pontos
menu.range.average=Sele\u00e7\u00e3o m\u00e9dia
menu.range.reverse=Reverter intervalo
menu.range.mergetracksegments=Mesclar trechos da rota
menu.map.autopan=Auto-ajustar
menu.map.showmap=Mostrar o mapa
menu.map.showscalebar=Mostrar barra de escala
+menu.map.editmode=Modo de edi\u00e7\u00e3o
# Alt keys for menus
altkey.menu.file=A
+altkey.menu.online=N
altkey.menu.track=R
altkey.menu.range=I
altkey.menu.point=P
function.exportgpx=Exportar para GPX
function.exportpov=Exportar para POV
function.exportsvg=Exportar para SVG
+function.exportimage=Exportar imagem
function.editwaypointname=Editar nome do ponto
function.compress=Comprimir rota
+function.deleterange=Remover intervalo
+function.croptrack=Cortar rota
+function.interpolate=Interpolar pontos
function.addtimeoffset=Adicionar diferen\u00e7a de tempo
function.addaltitudeoffset=Adicionar diferen\u00e7a de altitude
function.convertnamestotimes=Converter nomes dos pontos para tempos
function.show3d=Visualizar 3D
function.distances=Dist\u00e2ncias
function.fullrangedetails=Todos os detalhes
+function.estimatetime=Tempo estimado
+function.learnestimationparams=Aprender os par\u00e2metros para estimativa de tempo
function.setmapbg=Definir como fundo do mapa
function.setpaths=Definir caminhos do programa
+function.splitsegments=Dividir rota em segmentos
+function.sewsegments=Reunir segmentos em rota
function.getgpsies=Obter rotas Gpsies
function.uploadgpsies=Enviar rotas para o Gpsies
function.lookupsrtm=Obter altitudes a partir do SRTM
+function.downloadsrtm=Baixar arquivos SRTM
function.getwikipedia=Obter artigos da Wikipedia das redondezas
function.searchwikipedianames=Procurar na Wikipedia por nome
function.downloadosm=Baixar dados OSM para a \u00e1rea
function.saveconfig=Salvar configura\u00e7\u00f5es
function.diskcache=Salvar mapas para o disco
function.managetilecache=Gerenciar cache de fundos
+function.getweatherforecast=Baixar a previs\u00e3o do tempo para a \u00e1rea atual
# Dialogs
dialog.exit.confirm.title=Sair do GpsPrune
dialog.deletepoint.deletephoto=Remover foto anexada a este ponto?
dialog.deletephoto.title=Remover Foto
dialog.deletephoto.deletepoint=Remover ponto anexado a esta foto?
+dialog.deleteaudio.deletepoint=Remover ponto anexado a este clipe de \u00e1udio?
dialog.openoptions.title=Op\u00e7\u00f5es de abertura
dialog.openoptions.filesnippet=Extrair do arquivo
dialog.load.table.field=Campo
dialog.openoptions.deliminfo.fields=campos
dialog.openoptions.deliminfo.norecords=Sem registros
dialog.openoptions.altitudeunits=Unidades de altitude
+dialog.openoptions.speedunits=Unidades de velocidade
+dialog.openoptions.vertspeedunits=Unidades de velocidade vertical
+dialog.openoptions.vspeed.positiveup=Velocidades de subida positivas
+dialog.openoptions.vspeed.positivedown=Velocidades de descida positivas
dialog.open.contentsdoubled=Este arquivo cont\u00e9m duas c\u00f3pias de cada ponto,\nsendo uma como ponto normal e uma como ponto de rota.
dialog.selecttracks.intro=Selecione a rota ou rotas para carregar
dialog.selecttracks.noname=Sem nome
dialog.gpssend.sendwaypoints=Enviar pontos
dialog.gpssend.sendtracks=Enviar rotas
dialog.gpssend.trackname=Nome da rota
+dialog.gpsbabel.filters=Filtros
+dialog.addfilter.title=Adicionar filtro
+dialog.gpsbabel.filter.discard=Descartar
+dialog.gpsbabel.filter.simplify=Simplificar
+dialog.gpsbabel.filter.distance=Dist\u00e2ncia
+dialog.gpsbabel.filter.interpolate=Interpolar
+dialog.gpsbabel.filter.discard.intro=Descartar pontos se
+dialog.gpsbabel.filter.discard.hdop=Hdop >
+dialog.gpsbabel.filter.discard.vdop=Vdop >
+dialog.gpsbabel.filter.discard.numsats=N\u00famero de sat\u00e9lites <
+dialog.gpsbabel.filter.discard.nofix=Ponto n\u00e3o possui fixo
+dialog.gpsbabel.filter.discard.unknownfix=Ponto possui fixo desconhecido
+dialog.gpsbabel.filter.simplify.intro=Remover pontos at\u00e9
+dialog.gpsbabel.filter.simplify.maxpoints=N\u00famero de pontos <
+dialog.gpsbabel.filter.simplify.maxerror=ou erro de dist\u00e2ncia <
+dialog.gpsbabel.filter.simplify.crosstrack=cruzamento de rota
+dialog.gpsbabel.filter.simplify.length=diferen\u00e7a de comprimento
+dialog.gpsbabel.filter.simplify.relative=relativo ao hdop
+dialog.gpsbabel.filter.distance.intro=Remover pontos se pr\u00f3ximos a qualquer ponto anterior
+dialog.gpsbabel.filter.distance.distance=Se dist\u00e2ncia <
+dialog.gpsbabel.filter.distance.time=e diferen\u00e7a de tempo <
+dialog.gpsbabel.filter.interpolate.intro=Adicionar pontos entre os pontos da rota
+dialog.gpsbabel.filter.interpolate.distance=Se dist\u00e2ncia >
+dialog.gpsbabel.filter.interpolate.time=ou diferen\u00e7a de tempo >
dialog.saveoptions.title=Salvar arquivo
dialog.save.fieldstosave=Campos a salvar
dialog.save.table.field=Campo
dialog.exportkml.altitude=Incluir altitudes (para avia\u00e7\u00e3o)
dialog.exportkml.kmz=Comprimir para criar arquivo kmz
dialog.exportkml.exportimages=Exportar miniaturas de imagem para o kmz
+dialog.exportkml.imagesize=Tamanho da imagem
dialog.exportkml.trackcolour=Cor da rota
+dialog.exportkml.standardkml=KML plano
+dialog.exportkml.extendedkml=KML extendido com estampa de tempo
dialog.exportgpx.name=Nome
dialog.exportgpx.desc=Descri\u00e7\u00e3o
dialog.exportgpx.includetimestamps=Incluir data-hora
dialog.exportpov.ballsandsticks=Bolas e galhos
dialog.exportpov.tubesandwalls=Tubos e muros
dialog.3d.warningtracksize=Esta rota possui um grande n\u00famero de pontos, os quais o Java3D n\u00e3o ser\u00e1 capaz de exibir.\n Voc\u00ea tem certeza que deseja continuar?
-dialog.exportkml.imagesize=Tamanho da imagem
+dialog.3d.useterrain=Mostrar terreno
+dialog.3d.terraingridsize=Tamanho da grade
+dialog.exportpov.baseimage=Imagem base
+dialog.exportpov.cannotmakebaseimage=N\u00e3o foi poss\u00edvel gravar imagem base
+dialog.baseimage.title=Imagem do mapa
+dialog.baseimage.useimage=Usar imagem
+dialog.baseimage.mapsource=Fonte do mapa
+dialog.baseimage.zoom=N\u00edvel de amplia\u00e7\u00e3o
+dialog.baseimage.incomplete=Imagem incompleta
+dialog.baseimage.tiles=Ladrilhos
+dialog.baseimage.size=Tamanho da imagem
dialog.exportsvg.text=Selecione os par\u00e2metros para a exporta\u00e7\u00e3o para o SVG
dialog.exportsvg.phi=\u00c2ngulo do azimute \u03d5
dialog.exportsvg.theta=\u00c2ngulo da eleva\u00e7\u00e3o \u03b8
dialog.exportsvg.gradients=Usar gradientes para sombreamento
+dialog.exportimage.noimagepossible=Imagens de mapa precisam estar em cache no disco para serem usados por uma exporta\u00e7\u00e3o.
+dialog.exportimage.drawtrack=Desenhar rota no mapa
+dialog.exportimage.drawtrackpoints=Desenhar pontos da rota
+dialog.exportimage.textscalepercent=Fator de escala do texto (%)
dialog.pointtype.desc=Salvar os seguintes tipos de ponto:
dialog.pointtype.track=Pontos de rotas
dialog.pointtype.waypoint=Pontos
dialog.confirmcutandmove.title=Confirmar cortar e mover
dialog.confirmcutandmove.text=A rota cont\u00e9m informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s o movimento.\n Voc\u00ea tem certeza que deseja mover esta se\u00e7\u00e3o?
dialog.interpolate.parameter.text=N\u00famero de pontos para inserir entre os pontos selecionados
+dialog.interpolate.betweenwaypoints=Interpolar entre os pontos do caminho?
dialog.undo.title=A\u00e7\u00e3o(\u00f5es) de desfazer
dialog.undo.pretext=Por favor, selecione a a\u00e7\u00e3o(\u00f5es) a desfazer
dialog.undo.none.title=N\u00e3o foi poss\u00edvel desfazer
dialog.pointedit.title=Editar ponto
dialog.pointedit.intro=Selecionar cada campo para mudar o valor
dialog.pointedit.table.field=Campo
+dialog.pointedit.nofield=Nenhum campo selecionado
dialog.pointedit.table.value=Valor
dialog.pointnameedit.name=Nome do ponto
dialog.pointnameedit.uppercase=MAI\u00daSCULAS
dialog.distances.currentpoint=Ponto atual
dialog.distances.toofewpoints=Esta fun\u00e7\u00e3o precisa de pontos para calcular a dist\u00e3ncia entre eles
dialog.fullrangedetails.intro=Aqui est\u00e3o os detalhes para o intervalo selecionado
+dialog.fullrangedetails.coltotal=Incluindo intervalos
+dialog.fullrangedetails.colsegments=Sem intervalos
+dialog.estimatetime.details=Detalhes
+dialog.estimatetime.gentle=Suave
+dialog.estimatetime.steep=\u00cdngreme
+dialog.estimatetime.climb=Subida
+dialog.estimatetime.descent=Descida
+dialog.estimatetime.parameters=Par\u00e2metros
+dialog.estimatetime.parameters.timefor=Tempo para
+dialog.estimatetime.results=Resultados
+dialog.estimatetime.results.estimatedtime=Tempo estimado
+dialog.estimatetime.results.actualtime=Tempo real
+dialog.estimatetime.error.nodistance=Para estimar o tempo \u00e9 necess\u00e1rio conectar os pontos da rota, para obter uma dist\u00e2ncia
+dialog.estimatetime.error.noaltitudes=A sele\u00e7\u00e3o n\u00e3o inclui nenhuma informa\u00e7\u00e3o de altitude
+dialog.learnestimationparams.intro=Estes s\u00e3o os par\u00e2metros calculados desta rota
+dialog.learnestimationparams.averageerror=Erro m\u00e9dio
+dialog.learnestimationparams.combine=Estes par\u00e2metros podem ser combinados com os valores atuais
+dialog.learnestimationparams.combinedresults=Resultados combinados
+dialog.learnestimationparams.weight.100pccurrent=Manter os valores atuais
+dialog.learnestimationparams.weight.current=atual
+dialog.learnestimationparams.weight.calculated=calculado
+dialog.learnestimationparams.weight.50pc=M\u00e9dia dos valores atuais e calculados
+dialog.learnestimationparams.weight.100pccalculated=Usar novos valores calculados
dialog.setmapbg.intro=Selecione uma das fontes de mapas ou adicione uma nova
dialog.addmapsource.title=Adicionar nova fonte de mapas
dialog.addmapsource.sourcename=Nome da fonte
dialog.wikipedia.column.distance=Dist\u00e2ncia
dialog.correlate.notimestamps=N\u00e3o existem data-hora nos dados dos pontos, assim n\u00e3o h\u00e1 nada para correlacionar com as fotos
dialog.correlate.nouncorrelatedphotos=Existem fotos n\u00e3o correlacionadas.\nVoc\u00ea tem certeza que deseja continuar?
+dialog.correlate.nouncorrelatedaudios=Existem \u00e1udios n\u00e3o correlacionados.\nVoc\u00ea tem certeza que deseja continuar?
dialog.correlate.photoselect.intro=Selecione uma destas fotos correlacionadas para usar como diferen\u00e7a de tempo
dialog.correlate.select.photoname=Nome da foto
dialog.correlate.select.timediff=Diferen\u00e7a de tempo
dialog.correlate.select.photolater=Foto \u00e9 mais recente
-dialog.correlate.options.tip=Dica: Correlacionando pelo menos uma foto manualmente, a diferen\u00e7a de tempo pode ser calculada para voc\u00ea.
dialog.correlate.options.intro=Selecione as op\u00e7\u00f5es para correla\u00e7\u00e3o autom\u00e1tica
dialog.correlate.options.offsetpanel=Diferen\u00e7a de tempo
dialog.correlate.options.offset=Diferen\u00e7a
dialog.rearrangephotos.nosort=N\u00e3o ordenar
dialog.rearrangephotos.sortbyfilename=Ordenar pelo nome do arquivo
dialog.rearrangephotos.sortbytime=Ordenar pela hora
-dialog.deletemarked.nonefound=Nenhum dado dos pontos pode ser removido
dialog.compress.closepoints.title=Remo\u00e7\u00e3o de ponto pr\u00f3ximo
dialog.compress.closepoints.paramdesc=Fator de deslocamento
dialog.compress.wackypoints.title=Remo\u00e7\u00e3o de ponto exc\u00eantrica
dialog.compress.douglaspeucker.title=Compress\u00e3o Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=Fator de deslocamento
dialog.compress.summarylabel=Pontos para remover
+dialog.compress.confirm=%d pontos foram marcados.\nRemover estes pontos marcados agora?
+dialog.compress.confirmnone=nenhum ponto foi marcado
+dialog.deletemarked.nonefound=Nenhum dado dos pontos pode ser removido
dialog.pastecoordinates.desc=Insira ou cole as coordenadas aqui
dialog.pastecoordinates.coords=Coordenadas
dialog.pastecoordinates.nothingfound=Por favor, verifique as coordenadas novamente
-dialog.help.help=Por favor, veja\n http://activityworkshop.net/software/gpsprune/\npara mais informa\u00e7\u00f5es e guia do usu\u00e1rio.
+dialog.help.help=Por favor, veja\n http://gpsprune.activityworkshop.net/\npara mais informa\u00e7\u00f5es e guia do usu\u00e1rio.
dialog.about.version=Vers\u00e3o
dialog.about.build=Compila\u00e7\u00e3o
dialog.about.summarytext1=GpsPrune \u00e9 um programa para carregar, exibir e editar dados de receptores de GPS.
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Esta nova vers\u00e3o foi lan\u00e7ada em
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Para baixar a nova vers\u00e3o, v\u00e1 para http://activityworkshop.net/software/gpsprune/download.html.
+dialog.checkversion.download=Para baixar a nova vers\u00e3o, v\u00e1 para http://gpsprune.activityworkshop.net/download.html.
dialog.keys.intro=Voc\u00ea pode usar os seguintes atalhos de teclado ao inv\u00e9s de usar o mouse
dialog.keys.keylist=<table><tr><td>Cursores</td><td>Move o mapa para esquerda, direita, acima e abaixo</td></tr><tr><td>Ctrl + cursores esquerdo e direito</td><td>Seleciona o pr\u00f3ximo ponto ou o anterior</td></tr><tr><td>Ctrl + cursores acima e abaixo</td><td>Amplia ou reduz</td></tr><tr><td>Del</td><td>Remove o ponto atual</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.diskcache.dir=Diret\u00f3rio da cache
dialog.diskcache.createdir=Criar diret\u00f3rio
dialog.diskcache.nocreate=Diret\u00f3rio da cache n\u00e3o foi criado
+dialog.diskcache.cannotwrite=Ladrilhos de mapa n\u00e3o puderam ser salvos na pasta selecionada
dialog.diskcache.table.path=Caminho
dialog.diskcache.table.usedby=Usado por
dialog.diskcache.table.zoom=Zoom
dialog.diskcache.deleteold=Apagar fundos antigos
dialog.diskcache.maximumage=Idade m\u00e1xima (dias)
dialog.diskcache.deleteall=Apagar todos os fundos
-dialog.diskcache.deleted1=Removidos
-dialog.diskcache.deleted2=arquivos do cache
+dialog.diskcache.deleted=Removidos %d arquivos do cache
dialog.deletefieldvalues.intro=Selecione o campo a remover para o intervalo atual
+dialog.deletefieldvalues.nofields=N\u00e3o existem campos a remover para este intervalo
dialog.setlinewidth.text=Insira a espessura das linhas para desenhar as rotas (1-4)
dialog.downloadosm.desc=Confirmar a transfer\u00eancia de dados OSM brutos para a \u00e1rea especificada:
dialog.searchwikipedianames.search=Procurar por:
+dialog.weather.location=Localiza\u00e7\u00e3o
+dialog.weather.update=Previs\u00e3o atualizada
+dialog.weather.sunrise=Nascer do sol
+dialog.weather.sunset=P\u00f4r do sol
+dialog.weather.temperatureunits=Temperaturas
+dialog.weather.currentforecast=Clima atual
+dialog.weather.dailyforecast=Previs\u00e3o para o dia
+dialog.weather.3hourlyforecast=Previs\u00e3o para tr\u00eas horas
+dialog.weather.day.now=Clima atual
+dialog.weather.day.today=Hoje
+dialog.weather.day.tomorrow=Amanh\u00e3
+dialog.weather.day.monday=Segunda
+dialog.weather.day.tuesday=Ter\u00e7a
+dialog.weather.day.wednesday=Quarta
+dialog.weather.day.thursday=Quinta
+dialog.weather.day.friday=Sexta
+dialog.weather.day.saturday=S\u00e1bado
+dialog.weather.day.sunday=Domingo
+dialog.weather.creditnotice=Estes dados foram disponibilizados por openweathermap.org. A p\u00e1gina Web possui mais detalhes.
# 3d window
dialog.3d.title=Vista 3D do GpsPrune
confirm.addaltitudeoffset=Diferen\u00e7a de altitude adicionadas
confirm.rearrangewaypoints=Pontos rearrumados
confirm.rearrangephotos=Fotos rearrumadas
+confirm.splitsegments=%d divis\u00f5es de segmentos feitas
+confirm.sewsegments=%d reuni\u00f5es de segmentos feitas
confirm.cutandmove=Sele\u00e7\u00e3o movida
+confirm.interpolate=Pontos adicionados
confirm.convertnamestotimes=Nomes dos pontos convertidos
-confirm.saveexif.ok1=Salvo
-confirm.saveexif.ok2=arquivos de foto
+confirm.saveexif.ok=Salvo %d arquivos de foto
confirm.undo.single=opera\u00e7\u00e3o desfeita
confirm.undo.multi=opera\u00e7\u00f5es desfeitas
confirm.jpegload.single=foto foi adicionada
confirm.createpoint=ponto criado
confirm.rotatephoto=foto rotacionada
confirm.running=Rodando...
-confirm.lookupsrtm1=Encontrado
-confirm.lookupsrtm2=valores de altitude
+confirm.lookupsrtm=Encontrado %d valores de altitude
+confirm.downloadsrtm=%d arquivos baixados para a cache
+confirm.downloadsrtm.none=Nenhum arquivo baixado, pois j\u00e1 est\u00e3o na cache.
confirm.deletefieldvalues=Valores do campo removidos
confirm.audioload=Arquivos de \u00e1udio adicionados
confirm.correlateaudios.single=\u00e1udio foi correlacionado
confirm.correlateaudios.multi=\u00e1udios foram correlacionados
+# Tips, shown just once when appropriate
+tip.title=Dica
+tip.useamapcache=Configurando a cache de disco (Configura\u00e7\u00f5es -> Salvar mapas para disco)\nvoc\u00ea pode acelerar a exibi\u00e7\u00e3o e reduzir o tr\u00e1fego de rede.
+tip.learntimeparams=Os resultados ser\u00e3o mais precisos se voc\u00ea usar\nRota -> Aprender os par\u00e2metros para estimativa de tempo\nde suas rotas gravadas.
+tip.downloadsrtm=Voc\u00ea pode acelerar chamando\nOnline -> Baixar ladrilhos SRTM\npara obter as altitudes\naproximadas para a vis\u00e3o 3D.
+tip.usesrtmfor3d=Esta rota n\u00e3o possui altitudes.\nVoc\u00ea pode usar as fun\u00e7\u00f5es SRTM para obter as altitudes\naproximadas para a vis\u00e3o 3D.
+tip.manuallycorrelateone=Correlacionando pelo menos uma foto manualmente, a diferen\u00e7a de tempo pode ser calculada para voc\u00ea.
+
# Buttons
button.ok=Ok
button.back=Voltar
button.no=N\u00e3o
button.yestoall=Sim para todos
button.notoall=N\u00e3o para todos
+button.always=Sempre
button.select=Selecionar
button.selectall=Selecionar todos
button.selectnone=Selecionar nenhum
button.addnew=Adicionar novo
button.delete=Remover
button.manage=Gerenciar
+button.combine=Combinar
# File types
filetype.txt=Arquivos TXT
filetype.gpx=Arquivos GPX
filetype.pov=Arquivos POV
filetype.svg=Arquivos SVG
+filetype.png=Arquivos PNG
filetype.audio=Arquivos MP3, OGG, WAV
# Display components
display.nodata=Nenhum dado carregado
display.noaltitudes=Dados da rota n\u00e3o incluem altitudes
display.notimestamps=Dados da rota n\u00e3o incluem data-hora
+display.novalues=Dados da rota n\u00e3o incluem valores para este campo
details.trackdetails=Detalhes da track
details.notrack=Nenhuma rota carrgeada
details.track.points=Pontos
details.photo.loading=Carregando
details.photo.bearing=Apontando
details.media.connected=Conectada
+details.media.fullpath=Caminho completo
details.audiodetails=Detalhes do \u00e1udio
details.noaudio=Nenhum arquivo de \u00e1udio selecionado
details.audio.file=Arquivo de \u00e1udio
fieldname.custom=Personalizado
fieldname.prefix=Campo
fieldname.distance=Dist\u00e2ncia
-fieldname.movingdistance=Dist\u00e2ncia de movimento
fieldname.duration=Dura\u00e7\u00e3o
fieldname.speed=Velocidade
fieldname.verticalspeed=Velocidade vertical
units.feet.short=ft
units.kilometres=Quil\u00f4metros
units.kilometres.short=km
+units.kilometresperhour=km por hora
units.kilometresperhour.short=km/h
units.miles=Milhas
units.miles.short=mi
+units.milesperhour=milhas por hora
units.milesperhour.short=mph
+units.nauticalmiles=milhas n\u00e1uticas
+units.nauticalmiles.short=m.n.
+units.nauticalmilesperhour.short=n\u00f3s
+units.metrespersec=metros por segundo
units.metrespersec.short=m/s
+units.feetpersec=p\u00e9s por segundo
units.feetpersec.short=ft/s
units.hours=horas
+units.minutes=minutos
+units.seconds=segundos
units.degminsec=Graus-min-seg
units.degmin=Graus-min
units.deg=Graus
units.iso8601=ISO 8601
+units.degreescelsius=Celsius
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=Fahrenheit
+units.degreesfahrenheit.short=\u00b0F
+
+# How to combine conditions, such as filters
+logic.and=e
+logic.or=ou
# External urls
url.googlemaps=maps.google.com.br
wikipedia.lang=pt
+openweathermap.lang=pt
# Cardinals for 3d plots
cardinal.n=N
undo.removephoto=remover foto
undo.removeaudio=remover arquivo de \u00e1udio
undo.deleterange=remover intervalo
+undo.croptrack=recortar rota
undo.deletemarked=remover pontos
undo.insert=inserir pontos
undo.reverse=inverter intervalo
undo.mergetracksegments=mesclar segmentos de rota
+undo.splitsegments=dividir segmentos de rota
+undo.sewsegments=reunir segmentos de rota
undo.addtimeoffset=adicionar diferen\u00e7a de tempo
undo.addaltitudeoffset=adicionar diferen\u00e7a de altitude
undo.rearrangewaypoints=rearrumar pontos
error.saveexif.filenotfound=Falha ao procurar o arquivo de foto
error.saveexif.cannotoverwrite1=Arquivo de foto
error.saveexif.cannotoverwrite2=\u00e9 somente leitura e n\u00e3o pode ser sobrescrito. Gravar para c\u00f3pia?
-error.saveexif.failed1=Falha ao salvar
-error.saveexif.failed2=das imagens
-error.saveexif.forced1=
-error.saveexif.forced2=das imagens for\u00e7adas por solicita\u00e7\u00e3o
+error.saveexif.failed=Falha ao salvar %d das imagens
+error.saveexif.forced=%d das imagens for\u00e7adas por solicita\u00e7\u00e3o
error.load.dialogtitle=Erro ao carregar dados
error.load.noread=N\u00e3o foi poss\u00edvel ler arquivo
error.load.nopoints=Nenhuma informa\u00e7\u00e3o de coordenadas encontrada no arquivo
error.lookupsrtm.nonerequired=Todos os pontos j\u00e1 possuem altitude, assim n\u00e3o h\u00e1 nada a procurar
error.gpsies.uploadnotok=O servidor Gpsies retornou a mensagem
error.gpsies.uploadfailed=O envio falhou com o erro
+error.showphoto.failed=Falha ao carregar foto
error.playaudiofailed=Falha ao reproduzir arquivo de \u00e1udio
error.cache.notthere=A paste de cache de fundos n\u00e3o foi encontrada
error.cache.empty=A pasta de cache de fundos est\u00e1 vazia
error.cache.cannotdelete=Nenhum fundo pode ser removido
+error.interpolate.invalidparameter=O n\u00famero de pontos deve estar entre 1 e 1000
+error.learnestimationparams.failed=N\u00e3o foi poss\u00edvel aprender par\u00e2metros desta rota.\nTente baixar mais rotas.
+error.tracksplit.nosplit=A rota n\u00e3o pode ser dividida.
+error.downloadsrtm.nocache=Os arquivos n\u00e3o puderam ser salvos.\nPor favor, verifique a cache do disco.
+error.sewsegments.nothingdone=Os segmentos n\u00e3o puderam ser reunidos.\nExistem agora %d segmentos na rota.
# Menu entries
menu.file=Fi\u015fier
-menu.file.addphotos=Adaugare foto
+menu.file.addphotos=Adaug\u0103 foto
menu.file.recentfiles=Fi\u015fiere recente
menu.file.save=Salvare
menu.file.exit=Iesire
menu.track.clearundo=\u015etergere lista de anulari
menu.track.deletemarked=\u015etergere puncte marcate
menu.track.rearrange=Rearanjare waypoint
-menu.track.rearrange.start=Toate la inceputul fisierului
-menu.track.rearrange.end=Toate la sfarsitul fisierului
+menu.track.rearrange.start=Toate la inceputul fi\u015fierului
+menu.track.rearrange.end=Toate la sfarsitul fi\u015fierului
menu.track.rearrange.nearest=Fiecare la punctul cel mai apropiat al traseului
menu.range=Interval
menu.range.all=Selectare toate
menu.view.browser.openstreetmap=Openstreetmap
menu.view.browser.mapquest=Mapquest
menu.view.browser.yahoo=Harti Yahoo
+menu.view.browser.bing=Harti Bing
menu.settings=Set\u0103ri
menu.help=Ajutor
# Popup menu for map
menu.map.zoomin=Apropie
menu.map.zoomout=Departeaza
menu.map.zoomfull=Departeaza la maxim
-menu.map.newpoint=Adaug\u0103 punct
+menu.map.newpoint=Creaz\u0103 punct
+menu.map.drawpoints=Creaz\u0103 serie de puncte
menu.map.connect=Traseaz\u0103 linii \u00eentre puncte
menu.map.autopan=Autovizualizare punct ales
-menu.map.showmap=Arata harta
+menu.map.showmap=Arat\u0103 harta
+menu.map.showscalebar=Arat\u0103 scar\u0103
+menu.map.editmode=Mod de editare
# Alt keys for menus
altkey.menu.file=F
function.compress=Comprima traseu
function.deleterange=\u015etergere gama
function.interpolate=Interpolare
+function.addtimeoffset=Adaug\u0103 decalaj timp
+function.addaltitudeoffset=Adaug\u0103 decalaj altitudine
function.findwaypoint=Gasire waypoint
function.charts=Grafice
function.show3d=Vizualizare arborescenta
function.distances=Distan\u0163e
+function.fullrangedetails=Informa\u0163ie complet
+function.loadaudio=Adaug\u0103 audio
function.setmapbg=Fundal
function.setcolours=Selectare culorile
function.setlanguage=Selectare limba
function.about=Despre GpsPrune
function.checkversion=Verific\u0103 pentru o versiune noua
function.saveconfig=Salvare set\u0103ri
+function.getweatherforecast=Prognoz\u0103 meteo
# Dialogs
dialog.exit.confirm.title=Ie\u015fire din programul GpsPrune
dialog.openoptions.deliminfo.records=inregistrari, cu
dialog.openoptions.deliminfo.fields=cimpuri
dialog.openoptions.deliminfo.norecords=Nu sunt inregistrari
+dialog.selecttracks.noname=F\u0103r\u0103 nume
dialog.jpegload.subdirectories=Include subdirectori
dialog.jpegload.loadjpegswithoutcoords=Include fotografii fara coordonate
dialog.jpegload.loadjpegsoutsidearea=Include fotografii din afara zonei curente
dialog.gpsload.getwaypoints=Incarcare waypoints
dialog.gpssend.trackname=Nume traseu
dialog.gpsbabel.filters=Filtre
+dialog.gpsbabel.filter.simplify=Simplifica
dialog.gpsbabel.filter.distance=Distan\u0163\u0103
+dialog.gpsbabel.filter.discard.numsats=Num\u0103r de sateli\u0163i <
dialog.saveoptions.title=Salvare fi\u015fier
dialog.save.table.field=Cimp
dialog.save.table.save=Salvare
dialog.save.overwrite.title=Fi\u015fierul exist\u0103
dialog.save.overwrite.text=Fi\u015fierul exist\u0103. \u00cel suprascriu?
+dialog.exportkml.text=Titlu
+dialog.exportkml.trackcolour=Culoarea liniei
dialog.exportgpx.name=Nume
dialog.exportgpx.desc=Descriere
+dialog.exportgpx.encoding.system=Sistem
+dialog.exportgpx.encoding.utf8=UTF-8
+dialog.exportpov.font=Fontul
+dialog.exportpov.camerax=Vedere X
+dialog.exportpov.cameray=Vedere Y
+dialog.exportpov.cameraz=Vedere Z
+dialog.exportpov.modelstyle=Stilul
+dialog.3d.useterrain=Arat\u0103 teren
+dialog.3d.terraingridsize=Dimensiune a grilei
+dialog.exportpov.baseimage=Imagine cartografice
+dialog.baseimage.title=Imagine cartografice
+dialog.baseimage.tiles=Tigla
+dialog.exportsvg.phi=Azimut \u03D5
+dialog.exportsvg.theta=\u00cenclina\u0163ie \u03B8
+dialog.undo.title=Anulare
dialog.pointedit.intro=V\u0103 rog selecta\u0163i r\u00e2ndul care va fi editat
dialog.pointedit.table.field=Cimp
dialog.pointedit.table.value=Valoare
dialog.saveexif.photostatus.connected=Conectat
dialog.saveexif.photostatus.disconnected=Deconectat
dialog.saveexif.photostatus.modified=Modificat
-dialog.saveexif.overwrite=Suprascrie fisiere
+dialog.saveexif.overwrite=Suprascrie fi\u015fiere
dialog.charts.xaxis=Axa X
dialog.charts.yaxis=Axa Y
dialog.distances.currentpoint=Punct curent
+dialog.addmapsource.noname=F\u0103r\u0103 nume
dialog.gpsies.column.name=Nume
dialog.gpsies.column.length=Lungime
dialog.gpsies.description=Descriere
-dialog.gpsies.nodescription=Fara descriere
+dialog.gpsies.nodescription=F\u0103r\u0103 descriere
dialog.wikipedia.column.name=Nume
dialog.wikipedia.column.distance=Distan\u0163\u0103
-dialog.correlate.options.tip=Indiciu: By manually correlating at least one photo, the time offset can be calculated for you.
+dialog.correlate.options.offset.hours=ore,
+dialog.correlate.options.offset.minutes=minute,
+dialog.correlate.options.offset.seconds=secunde
+dialog.pastecoordinates.coords=Coordonate
dialog.about.version=Versiunea
+dialog.about.systeminfo=Informa\u0163ii a sistemului
dialog.about.systeminfo.os=Sistem de operare
+dialog.about.systeminfo.exiflib=Bibliotec\u0103 Exif
+dialog.about.systeminfo.exiflib.internal=Intern
+dialog.about.systeminfo.exiflib.internal.failed=Intern (absent)
+dialog.about.systeminfo.exiflib.external=Extern
+dialog.about.systeminfo.exiflib.external.failed=Extern (absent)
dialog.about.yes=Da
dialog.about.no=Nu
dialog.about.readme=Cite\u015fte-m\u0103
dialog.colourchooser.red=Ro\u0219u
dialog.colourchooser.green=Verde
dialog.colourchooser.blue=Albastru
+dialog.weather.day.today=Ast\u0103zi
+dialog.weather.day.tomorrow=M\u00e2ine
+dialog.weather.day.monday=Luni
+dialog.weather.day.tuesday=Mar\u0163i
+dialog.weather.day.wednesday=Miercuri
+dialog.weather.day.thursday=Joi
+dialog.weather.day.friday=Vineri
+dialog.weather.day.saturday=S\u00e2mb\u0103t\u0103
+dialog.weather.day.sunday=Duminic\u0103
# Confirm messages
-confirm.loadfile=Date incarcate din fisier
+confirm.loadfile=Date incarcate din fi\u015fier
confirm.save.ok1=Salvat cu succes
+confirm.save.ok2=puncte \u00een
+
+# Tips
+tip.title=Indiciu
# Buttons
button.ok=OK
button.combine=Combina
# File types
-filetype.txt=Fisier text
+filetype.txt=Fi\u015fiere text
filetype.jpeg=Imagine JPEG (*.jpg)
-filetype.kmlkmz=Fisier KML, KMZ
-filetype.kml=Fisier KML
-filetype.kmz=Fisier KMZ
-filetype.gpx=Fisier GPX
-filetype.pov=Fisier POV
-filetype.svg=Fisier SVG
-filetype.png=Fisier PNG
-filetype.audio=Fisier MP3, OGG, WAV
+filetype.kmlkmz=Fi\u015fiere KML, KMZ
+filetype.kml=Fi\u015fiere KML
+filetype.kmz=Fi\u015fiere KMZ
+filetype.gpx=Fi\u015fiere GPX
+filetype.pov=Fi\u015fiere POV
+filetype.svg=Fi\u015fiere SVG
+filetype.png=Fi\u015fiere PNG
+filetype.audio=Fi\u015fiere MP3, OGG, WAV
# Display components
+details.trackdetails=Detalii traseul
details.track.points=Puncte
-details.pointdetails=Punct
+details.pointdetails=Detalii punctul
+details.rangedetails=Detalii intervalul
details.range.selected=Selectat
details.range.to=la
details.altitude.to=la
details.range.avespeed=Viteza medie
details.range.maxspeed=Viteza maxim\u0103
details.lists.photos=Foto-uri
+details.lists.audio=Audio
+details.audiodetails=Detalii audio
# Field names
fieldname.latitude=Latitudine
cardinal.s=S
cardinal.e=E
cardinal.w=V
+
+wikipedia.lang=ro
+openweathermap.lang=ro
dialog.correlate.select.photoname=\u0418\u043c\u044f \u0444\u043e\u0442\u043e
dialog.correlate.select.timediff=\u0420\u0430\u0437\u043d\u0438\u0446\u0430 \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438
dialog.correlate.select.photolater=\u0424\u043e\u0442\u043e \u043f\u043e\u0437\u0434\u043d\u0435\u0435
-dialog.correlate.options.tip=\u0421\u043e\u0432\u0435\u0442: \u041f\u0440\u0438 \u0440\u0443\u0447\u043d\u043e\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043a\u0440\u0430\u0439\u043d\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043c\u0435\u0442\u043a\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438.
dialog.correlate.options.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f
dialog.correlate.options.offsetpanel=\u041e\u0442\u043c\u0435\u0442\u043a\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438
dialog.correlate.options.offset=\u0421\u043c\u0435\u0449\u0435\u043d\u0438\u0435
dialog.compress.douglaspeucker.title=\u0421\u0436\u0430\u0442\u0438\u0435 \u043f\u043e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0443 Douglas-Peucker
dialog.compress.douglaspeucker.paramdesc=\u0420\u0430\u0437\u043c\u0430\u0445
dialog.compress.summarylabel=\u0422\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f
-dialog.compress.confirm1=
-dialog.compress.confirm2=\u0442\u043e\u0447\u043a\u0438 \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u044b.\n\u0427\u0442\u043e\u0431\u044b \u0438\u0445 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043c\u0435\u043d\u044e \u0422\u0440\u0435\u043a->\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438
+dialog.compress.confirm=%d \u0442\u043e\u0447\u043a\u0438 \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u044b.\n\u0427\u0442\u043e\u0431\u044b \u0438\u0445 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043c\u0435\u043d\u044e \u0422\u0440\u0435\u043a->\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438
dialog.compress.confirmnone=\u043d\u0435\u0442 \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a
dialog.deletemarked.nonefound=\u041d\u0435\u0442 \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f
dialog.pastecoordinates.desc=\u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0437\u0434\u0435\u0441\u044c
dialog.diskcache.deleteold=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0440\u044b\u0445 \u0442\u0430\u0439\u043b\u043e\u0432
dialog.diskcache.maximumage=\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0432\u043e\u0437\u0440\u0430\u0441\u0442 (\u0432 \u0434\u043d\u044f\u0445)
dialog.diskcache.deleteall=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u0442\u0430\u0439\u043b\u044b
-dialog.diskcache.deleted1=\u0423\u0434\u0430\u043b\u0435\u043d\u043e
-dialog.diskcache.deleted2=\u0444\u0430\u0439\u043b\u043e\u0432 \u0438\u0437 \u043a\u044d\u0448\u0430
+dialog.diskcache.deleted=\u0423\u0434\u0430\u043b\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u043e\u0432 \u0438\u0437 \u043a\u044d\u0448\u0430
dialog.deletefieldvalues.intro=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430
dialog.deletefieldvalues.nofields=\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 \u043d\u0435\u0442 \u043f\u043e\u043b\u0435\u0439 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f
dialog.setlinewidth.text=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u043e\u043b\u0449\u0438\u043d\u0443 \u043b\u0438\u043d\u0438\u0439 \u0434\u043b\u044f \u0442\u0440\u0435\u043a\u043e\u0432 (1-4)
confirm.cutandmove=\u041e\u0442\u043e\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u043e
confirm.interpolate=\u0422\u043e\u0447\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b
confirm.convertnamestotimes=\u0418\u043c\u044f \u043f\u0443\u0442\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u043e
-confirm.saveexif.ok1=\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e
-confirm.saveexif.ok2=\u0444\u0430\u0439\u043b\u044b \u0441 \u0444\u043e\u0442\u043e
+confirm.saveexif.ok=\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e %d \u0444\u0430\u0439\u043b\u044b \u0441 \u0444\u043e\u0442\u043e
confirm.undo.single=\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043e\u0442\u043c\u0435\u043d\u044b
confirm.undo.multi=\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043e\u0442\u043c\u0435\u043d\u044b
confirm.jpegload.single=\u0444\u043e\u0442\u043e \u0431\u044b\u043b\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e
confirm.createpoint=\u0442\u043e\u0447\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0430
confirm.rotatephoto=\u0444\u043e\u0442\u043e \u043f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u043e
confirm.running=\u0420\u0430\u0431\u043e\u0442\u0430\u044e...
-confirm.lookupsrtm1=\u041d\u0430\u0439\u0434\u0435\u043d\u043e
-confirm.lookupsrtm2=\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b
+confirm.lookupsrtm=\u041d\u0430\u0439\u0434\u0435\u043d\u043e %d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b
confirm.deletefieldvalues=\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u044b
confirm.audioload=\u0424\u0430\u0439\u043b\u044b \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b
confirm.correlateaudios.single=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u044c \u0431\u044b\u043b\u0438 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430
confirm.correlateaudios.multi=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u0431\u044b\u043b\u0438 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b
+# Tips
+tip.title=\u0421\u043e\u0432\u0435\u0442
+tip.manuallycorrelateone=\u041f\u0440\u0438 \u0440\u0443\u0447\u043d\u043e\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043a\u0440\u0430\u0439\u043d\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043c\u0435\u0442\u043a\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438.
+
# Buttons
button.ok=OK
button.back=\u041d\u0430\u0437\u0430\u0434
fieldname.custom=\u041e\u0431\u044b\u0447\u043d\u043e
fieldname.prefix=\u041f\u043e\u043b\u0435
fieldname.distance=\u0420\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435
-fieldname.movingdistance=\u0420\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u0435\u0437 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043a\u043e\u0432
fieldname.duration=\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c
fieldname.speed=\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c
fieldname.verticalspeed=\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c
error.saveexif.filenotfound=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0444\u0430\u0439\u043b \u0441 \u0444\u043e\u0442\u043e
error.saveexif.cannotoverwrite1=\u0444\u0430\u0439\u043b \u0441 \u0444\u043e\u0442\u043e
error.saveexif.cannotoverwrite2=\u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f, \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u043d! \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u043a \u043a\u043e\u043f\u0438\u044e?
-error.saveexif.failed1=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c
-error.saveexif.failed2=\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435(-\u0438\u0439)
-error.saveexif.forced1=
-error.saveexif.forced2=\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435(-\u0438\u0439) \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e
+error.saveexif.failed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c %d \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435(-\u0438\u0439)
+error.saveexif.forced=%d \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435(-\u0438\u0439) \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e
error.load.dialogtitle=\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0434\u0430\u043d\u043d\u044b\u0445
error.load.noread=\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0444\u0430\u0439\u043b
error.load.nopoints=\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445
--- /dev/null
+# Text entries for the GpsPrune application
+# Swedish entries
+
+# Menu entries
+menu.file=Fil
+menu.file.addphotos=L\u00e4gg till foto
+menu.file.recentfiles=Senaste filer
+menu.file.save=Spara som text
+menu.file.exit=Avsluta
+menu.track=Sp\u00e5r
+menu.track.undo=\u00c5ngra
+menu.track.clearundo=Rensa \u00e5ngra
+menu.track.markrectangle=Markera punkter i rektangel
+menu.track.deletemarked=Radera markerade punkter
+menu.track.rearrange=Arrangera om ruttpunkter
+menu.track.rearrange.start=Alla till b\u00f6rjan av fil
+menu.track.rearrange.end=Alla till slut av fil
+menu.track.rearrange.nearest=Varje till n\u00e4rmaste sp\u00e5rpunkt
+menu.range=Omr\u00e5de
+menu.range.all=V\u00e4lj alla
+menu.range.none=V\u00e4lj ingen
+menu.range.start=St\u00e4ll in b\u00f6rjan p\u00e5 omr\u00e5de
+menu.range.end=St\u00e4ll in slut p\u00e5 omr\u00e5de
+menu.range.average=Medelv\u00e4rdesval
+menu.range.reverse=Backa omr\u00e5de
+menu.range.mergetracksegments=Sl\u00e5 ihop sp\u00e5rsegment
+menu.range.cutandmove=Klipp och flytta urval
+menu.point=Punkt
+menu.point.editpoint=Redigera punkt
+menu.point.deletepoint=Radera punkt
+menu.photo=Foto
+menu.photo.saveexif=Spara och avsluta
+menu.audio=Ljud
+menu.view=Vy
+menu.view.showsidebars=Visa sidolister
+menu.view.browser=Karta i ett l\u00e4sarf\u00f6nster
+menu.view.browser.google=Google Maps
+menu.view.browser.openstreetmap=Openstreetmap
+menu.view.browser.mapquest=Mapquest
+menu.view.browser.yahoo=Yahoo maps
+menu.view.browser.bing=Bing maps
+menu.settings=Inst\u00e4llningar
+menu.settings.onlinemode=Ladda karta fr\u00e5n Internet
+menu.settings.autosave=Autospara inst\u00e4llningar vid avslut
+menu.help=Hj\u00e4lp
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=S
+altkey.menu.range=O
+altkey.menu.point=P
+altkey.menu.view=V
+altkey.menu.photo=T
+altkey.menu.audio=L
+altkey.menu.settings=I
+altkey.menu.help=H
+
+openweathermap.lang=se
menu.file=Dosya
menu.file.addphotos=Foto ekle
menu.file.save=Kaydet
-menu.file.exit=Ç\u0131k\u0131\u015f
+menu.file.exit=\u00c7\u0131k\u0131\u015f
menu.track=\u0130z
menu.track.undo=Geri al
menu.track.clearundo=Geri alma listesi s\u0131f\u0131rla
dialog.saveexif.force=Ufak hatalar\u0131 bo\u015fver
dialog.charts.xaxis=X axis
dialog.charts.yaxis=Y axis
-dialog.charts.output=Ç\u0131kt\u0131
+dialog.charts.output=\u00c7\u0131kt\u0131
dialog.charts.screen=Ekranda g\u00f6ster
dialog.charts.svg=SVG dosya olarak g\u00f6ster
dialog.charts.svgwidth=SVG geni\u015fli\u011fi
dialog.correlate.options.photolater=Foto noktadan sonra
dialog.correlate.options.pointlaterphoto=Nokta fotodan sonra
dialog.pastecoordinates.coords=Koordinatlar
-dialog.help.help=Ayr\u0131nt\u0131l\u0131 bilgi ve kullanma k\u0131lavuzu i\u00e7in l\u00fctfen\n http://activityworkshop.net/software/gpsprune/\n sitesinde bak.
+dialog.help.help=Ayr\u0131nt\u0131l\u0131 bilgi ve kullanma k\u0131lavuzu i\u00e7in l\u00fctfen\n http://gpsprune.activityworkshop.net/\n sitesinde bak.
dialog.about.version=S\u00fcr\u00fcm
dialog.about.build=Build
dialog.about.summarytext1=GpsPrune GPS ayg\u0131tlardan veri y\u00fckler, g\u00f6r\u00fcnt\u00fcler ver d\u00fczenler bir uygulamad\u0131r.
dialog.about.systeminfo.gnuplot=Gnuplot kuruldu
dialog.about.yes=Evet
dialog.about.no=Hay\u0131r
-dialog.about.credits.translators=Çevirmen
+dialog.about.credits.translators=\u00c7evirmen
dialog.about.credits.thanks=Te\u015fekk\u00fcrler
dialog.about.readme=Beni oku
dialog.checkversion.uptodate=GpsPrune'nin so s\u00fcr\u00fcm\u00fc kullan\u0131yorsun.
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Yeni s\u00fcr\u00fcm\u00fcn\u00fcn devir tarihi
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Yeni s\u00fcr\u00fcm indirmek i\u00e7in http://activityworkshop.net/software/gpsprune/download.html adresine git.
+dialog.checkversion.download=Yeni s\u00fcr\u00fcm indirmek i\u00e7in http://gpsprune.activityworkshop.net/download.html adresine git.
dialog.keys.intro=Fare yerinde a\u015fa\u011f\u0131daki k\u0131sayol tu\u015flar\u0131 kullanabilirsin:
dialog.keys.keylist=<table><tr><td>Ok tu\u015flar\u0131</td><td>Haritay\u0131 sola/sa\u011fa/a\u015fa\u011f\u0131/yukar\u0131 kayd\u0131r</td></tr><tr><td>Ctrl + sol, sa\u011f</td><td>\u00d6nceki/sonraki noktay\u0131 se\u00e7</td></tr><tr><td>Ctrl + yukar/a\u015fa\u011f\u0131</td><td>Yak\u0131nla\u015ft\u0131r/Uzakla\u015ft\u0131r</td></tr><tr><td>Del</td><td>Se\u00e7ili noltay\u0131 sil</td></tr></table>
dialog.saveconfig.desc=A\u011fa\u015f\u0131daki ayarlar\u0131 bir dasyada kaydedilir:
dialog.saveconfig.prune.mapserverindex=Harita sunucunun index
dialog.saveconfig.prune.mapserverurl=Harita sunucunun adresi
dialog.saveconfig.prune.kmzimagewidth=KMZ resim geni\u015fli\u011fi
+dialog.saveconfig.prune.kmzimageheight=KMZ resim y\u00fcksekli\u011fi
dialog.setpaths.intro=\u0130ste\u011fe ba\u011fl\u0131 a\u015fa\u011f\u0131daki uygulamalar\u0131n veriyolu kaydedebilirsin:
dialog.addaltitude.noaltitudes=Se\u00e7ili s\u0131rada y\u00fckseklik bilgisi bulunmad\u0131
dialog.addaltitude.desc=Eklenecek y\u00fckseklik ofseti
dialog.setcolours.background=Arkafonu
dialog.setcolours.borders=Kenarlar
-dialog.setcolours.lines=Çizgiler
+dialog.setcolours.lines=\u00c7izgiler
dialog.setcolours.primary=Birincil
dialog.setcolours.secondary=\u0130kincil
dialog.setcolours.point=Noktalar
button.moveup=Yukar\u0131
button.movedown=A\u015fa\u011f\u0131
button.edit=D\u00fczenle
-button.exit=Ç\u0131k\u0131\u015f
+button.exit=\u00c7\u0131k\u0131\u015f
button.close=Kapat
button.continue=Devam
button.yes=Evet
# External urls
url.googlemaps=maps.google.com
+wikipedia.lang=tr
+openweathermap.lang=tr
# Cardinals for 3d plots
cardinal.n=K
--- /dev/null
+# Text entries for the GpsPrune application
+# Ukrainian entries thanks to serhijdubyk
+
+# Menu entries
+menu.file=\u0424\u0430\u0439\u043b
+menu.file.addphotos=\u0414\u043e\u0434\u0430\u0442\u0438 \u0444\u043e\u0442\u043e
+menu.file.recentfiles=\u041f\u0440\u0438\u0439\u043d\u044f\u0442\u0456 \u0444\u0430\u0439\u043b\u0438
+menu.file.save=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u044f\u043a \u0442\u0435\u043a\u0441\u0442
+menu.file.exit=\u0412\u0438\u0445\u0456\u0434
+menu.track=\u0422\u0440\u0435\u043a
+menu.track.undo=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438
+menu.track.clearundo=\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043c\u0456\u043d
+menu.track.markrectangle=\u041f\u043e\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u0443 \u043f\u0440\u044f\u043c\u043e\u043a\u0443\u0442\u043d\u0438\u043a\u0443
+menu.track.deletemarked=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u043f\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u0456 \u0442\u043e\u0447\u043a\u0438
+menu.track.rearrange=\u041f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0438
+menu.track.rearrange.start=\u0423\u0441\u0435 \u043d\u0430 \u043f\u043e\u0447\u0430\u0442\u043e\u043a \u0444\u0430\u0439\u043b\u0443
+menu.track.rearrange.end=\u0423\u0441\u0435 \u043d\u0430 \u043a\u0456\u043d\u0435\u0446\u044c \u0444\u0430\u0439\u043b\u0443
+menu.track.rearrange.nearest=\u041f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u0434\u043e \u043d\u0430\u0439\u0431\u043b\u0438\u0436\u0447\u043e\u0457
+menu.range=\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b
+menu.range.all=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u0443\u0441\u0456
+menu.range.none=\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438 \u0432\u0438\u0431\u0456\u0440\u043a\u0443
+menu.range.start=\u041f\u043e\u0447\u0430\u0442\u043e\u043a \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0443
+menu.range.end=\u041a\u0456\u043d\u0435\u0446\u044c \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0443
+menu.range.average=\u0422\u043e\u0447\u043a\u0430 \u043f\u043e \u0441\u0435\u0440\u0435\u0434\u043d\u044c\u043e\u043c\u0443
+menu.range.reverse=\u041f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b
+menu.range.mergetracksegments=\u0417\u043b\u0438\u0442\u0438 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0438 \u0442\u0440\u0435\u043a\u0443
+menu.range.cutandmove=\u0412\u0438\u0440\u0456\u0437\u0430\u0442\u0438 \u0456 \u043f\u0435\u0440\u0435\u043c\u0456\u0441\u0442\u0438\u0442\u0438 \u0432\u0438\u0431\u0456\u0440\u043a\u0443
+menu.point=\u0422\u043e\u0447\u043a\u0430
+menu.point.editpoint=\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0442\u043e\u0447\u043a\u0443
+menu.point.deletepoint=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0443
+menu.photo=\u0421\u0432\u0456\u0442\u043b\u0438\u043d\u0438
+menu.photo.saveexif=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0432 Exif
+menu.audio=\u0417\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438
+menu.view=\u0412\u0438\u0433\u043b\u044f\u0434
+menu.view.showsidebars=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u043f\u0430\u043d\u0435\u043b\u044c
+menu.view.browser=\u0412\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u043c\u0430\u043f\u0443 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0456
+menu.view.browser.google=\u041c\u0430\u043f\u0438 Google
+menu.view.browser.openstreetmap=Openstreetmap
+menu.view.browser.mapquest=Mapquest
+menu.view.browser.yahoo=\u041c\u0430\u043f\u0438 Yahoo
+menu.view.browser.bing=\u041c\u0430\u043f\u0438 Bing
+menu.settings=\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f
+menu.settings.onlinemode=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u043c\u0430\u043f\u0438 \u0437 \u0406\u043d\u0442\u0435\u0440\u043d\u0435\u0442
+menu.settings.autosave=\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u0435 \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u043d\u044f \u043f\u0440\u0438 \u0432\u0438\u0445\u043e\u0434\u0456
+menu.help=\u0414\u043e\u043f\u043e\u043c\u043e\u0433\u0430
+# Popup menu for map
+menu.map.zoomin=\u0417\u0431\u0456\u043b\u044c\u0448\u0438\u0442\u0438
+menu.map.zoomout=\u0417\u043c\u0435\u043d\u0448\u0438\u0442\u0438
+menu.map.zoomfull=\u0417\u0431\u0456\u043b\u044c\u0448\u0438\u0442\u0438 \u0434\u043e \u043f\u043e\u0432\u043d\u043e\u0457 \u0448\u043a\u0430\u043b\u0438
+menu.map.newpoint=\u0421\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043e\u0434\u043d\u0443 \u0442\u043e\u0447\u043a\u0443
+menu.map.drawpoints=\u0421\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043a\u0456\u043b\u044c\u043a\u0430 \u0442\u043e\u0447\u043e\u043a
+menu.map.connect=\u0417\u2019\u0454\u0434\u043d\u0430\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u0442\u0440\u0435\u043a\u0443 \u0437 \u043b\u0456\u043d\u0456\u0454\u044e
+menu.map.autopan=\u0412\u0456\u0434\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u0438 \u0432\u0438\u0431\u0440\u0430\u043d\u0435
+menu.map.showmap=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u041e\u0421\u041c-\u043c\u0430\u043f\u0443
+menu.map.showscalebar=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u043b\u0456\u043d\u0456\u0439\u043a\u0443 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0443
+menu.map.editmode=\u0420\u0435\u0436\u0438\u043c \u0440\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u043d\u043d\u044f
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=T
+altkey.menu.range=R
+altkey.menu.point=P
+altkey.menu.view=V
+altkey.menu.photo=O
+altkey.menu.audio=A
+altkey.menu.settings=S
+altkey.menu.help=H
+
+# Ctrl shortcuts for menu items
+shortcut.menu.file.open=O
+shortcut.menu.file.load=L
+shortcut.menu.file.save=S
+shortcut.menu.track.undo=Z
+shortcut.menu.edit.compress=C
+shortcut.menu.range.all=A
+shortcut.menu.help.help=H
+
+# Functions
+function.open=\u0412\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u0444\u0430\u0439\u043b
+function.importwithgpsbabel=\u0406\u043c\u043f\u043e\u0440\u0442 \u0444\u0430\u0439\u043b\u0443 \u0437GPSBabel
+function.loadfromgps=\u0412\u0438\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0437 GPS
+function.sendtogps=\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0432 GPS
+function.exportkml=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 KML
+function.exportgpx=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 GPX
+function.exportpov=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 POV
+function.exportsvg=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 SVG
+function.exportimage=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f
+function.editwaypointname=\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0456\u043c\u2019\u044f \u0448\u043b\u044f\u0445\u043e\u0432\u043e\u0457 \u0442\u043e\u0447\u043a\u0438
+function.compress=\u0421\u0442\u0438\u0441\u043d\u0443\u0442\u0438 \u0442\u0440\u0435\u043a
+function.deleterange=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b
+function.croptrack=\u041e\u0431\u0440\u0456\u0437\u0430\u0442\u0438 \u0442\u0440\u0435\u043a
+function.interpolate=\u0406\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0456\u044f \u0442\u043e\u0447\u043e\u043a
+function.addtimeoffset=\u0414\u043e\u0434\u0430\u0442\u0438 \u043f\u043e\u0437\u043d\u0430\u0447\u043a\u0443 \u0447\u0430\u0441\u0443
+function.addaltitudeoffset=\u0414\u043e\u0434\u0430\u0442\u0438 \u043f\u043e\u0437\u043d\u0430\u0447\u043a\u0443 \u0432\u0438\u0441\u043e\u0442\u0438
+function.convertnamestotimes=\u041f\u0435\u0440\u0435\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u0456\u043c\u2019\u044f \u0448\u043b\u044f\u0445\u043e\u0432\u043e\u0457 \u0442\u043e\u0447\u043a\u0438 \u0443 \u0447\u0430\u0441
+function.deletefieldvalues=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u043f\u043e\u043b\u044f
+function.findwaypoint=\u0417\u043d\u0430\u0439\u0442\u0438 \u0448\u043b\u044f\u0445\u043e\u0432\u0443 \u0442\u043e\u0447\u043a\u0443
+function.pastecoordinates=\u0412\u0432\u0435\u0434\u0435\u043d\u043d\u044f \u043d\u043e\u0432\u0438\u0445 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442
+function.charts=\u0413\u0440\u0430\u0444\u0456\u043a\u0438
+function.show3d=3D-\u0432\u0438\u0433\u043b\u044f\u0434
+function.distances=\u0412\u0456\u0434\u0441\u0442\u0430\u043d\u0456
+function.fullrangedetails=\u0414\u0435\u0442\u0430\u043b\u0456\u0437\u0430\u0446\u0456\u044f \u043f\u043e \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0443
+function.estimatetime=\u041f\u0440\u0438\u0431\u043b\u0438\u0437\u043d\u0438\u0439 \u0447\u0430\u0441
+function.learnestimationparams=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 \u0432\u0433\u0430\u0434\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0440\u0438\u0431\u043b\u0438\u0437\u043d\u043e\u0433\u043e \u0447\u0430\u0441\u0443
+function.setmapbg=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u043c\u0430\u043f\u0443-\u043f\u0456\u0434\u043a\u043b\u0430\u0434\u043a\u0443
+function.setpaths=\u0412\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0438 \u0448\u043b\u044f\u0445\u0438 \u0434\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c
+function.getgpsies=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0442\u0440\u0435\u043a\u0438 \u0437 Gpsies
+function.uploadgpsies=\u0412\u0438\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0442\u0440\u0435\u043a\u0438 \u043d\u0430 Gpsies
+function.lookupsrtm=\u041e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 \u0432\u0438\u0441\u043e\u0442\u0438 \u0437 SRTM
+function.getwikipedia=\u041e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 \u043d\u0430\u0439\u0431\u043b\u0438\u0436\u0447\u0443 \u0441\u0442\u0430\u0442\u0442\u044e \u0437 \u0412\u0456\u043a\u0456\u043f\u0435\u0434\u0456\u0457
+function.searchwikipedianames=\u041f\u043e\u0448\u0443\u043a \u0441\u0442\u0430\u0442\u0435\u0439 \u0437 \u0412\u0456\u043a\u0456\u043f\u0435\u0434\u0456\u0457 \u0437\u0430 \u043d\u0430\u0437\u0432\u043e\u044e
+function.downloadosm=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 OSM-\u0434\u0430\u043d\u0456 \u043d\u0430 \u0442\u0435\u0440\u0438\u0442\u043e\u0440\u0456\u044e
+function.duplicatepoint=\u041a\u043e\u043f\u0456\u044e\u0432\u0430\u0442\u0438 \u0442\u043e\u0447\u043a\u0443 \u0432 \u043a\u0456\u043d\u0435\u0446\u044c \u0442\u0440\u0435\u043a\u0443
+function.setcolours=\u0412\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0438 \u043a\u043e\u043b\u044c\u043e\u0440\u0438
+function.setlinewidth=\u0417\u0430\u0434\u0430\u0442\u0438 \u0448\u0438\u0440\u0438\u043d\u0443 \u043b\u0456\u043d\u0456\u0457
+function.setlanguage=\u0412\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0438 \u043c\u043e\u0432\u0443
+function.connecttopoint=\u041f\u0440\u0438\u043a\u0440\u0456\u043f\u0438\u0442\u0438 \u0434\u043e \u0442\u043e\u0447\u043a\u0438
+function.disconnectfrompoint=\u0412\u0456\u0434\u043a\u0440\u0456\u043f\u0438\u0442\u0438 \u0432\u0456\u0434 \u0442\u043e\u0447\u043a\u0438
+function.removephoto=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443
+function.correlatephotos=\u0417\u0456\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0438 \u0437\u0430 \u0447\u0430\u0441\u043e\u043c
+function.rearrangephotos=\u0412\u043f\u043e\u0440\u044f\u0434\u043a\u0443\u0432\u0430\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0438 \u0437\u0430 \u0442\u0440\u0435\u043a\u043e\u043c
+function.rotatephotoleft=\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443 \u043d\u0430 90\u00b0 \u0432\u043b\u0456\u0432\u043e
+function.rotatephotoright=\u041f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443 \u043d\u0430 90\u00b0 \u0432\u043f\u0440\u0430\u0432\u043e
+function.photopopup=\u0412\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443 \u0432 \u043e\u043a\u0440\u0435\u043c\u043e\u043c\u0443 \u0432\u0456\u043a\u043d\u0456
+function.ignoreexifthumb=\u0417\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u043f\u043e\u0432\u043d\u0443 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443
+function.loadaudio=\u0414\u043e\u0434\u0430\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441
+function.removeaudio=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441
+function.correlateaudios=\u0417\u0456\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0438 \u0437\u0430 \u0447\u0430\u0441\u043e\u043c
+function.playaudio=\u041f\u0440\u043e\u0433\u0440\u0430\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441
+function.stopaudio=\u0417\u0443\u043f\u0438\u043d\u0438\u0442\u0438 \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441
+function.help=\u0414\u043e\u043f\u043e\u043c\u043e\u0433\u0430
+function.showkeys=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u0433\u0430\u0440\u044f\u0447\u0456 \u043a\u043b\u0430\u0432\u0456\u0448\u0456
+function.about=\u041f\u0440\u043e GpsPrune
+function.checkversion=\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u0438\u0442\u0438 \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f
+function.saveconfig=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f
+function.diskcache=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u043c\u0430\u043f\u0438 \u043d\u0430 \u0434\u0438\u0441\u043a
+function.managetilecache=\u0423\u043f\u0440\u0430\u0432\u043b\u0456\u043d\u043d\u044f \u043a\u0435\u0448\u0435\u043c
+
+# Dialogs
+dialog.exit.confirm.title=\u0412\u0438\u0445\u0456\u0434
+dialog.exit.confirm.text=\u0414\u0430\u043d\u0456 \u043d\u0435 \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u0456! \u041f\u0440\u043e\u0434\u043e\u0432\u0436\u0438\u0442\u0438?
+dialog.openappend.title=\u0414\u043e\u0434\u0430\u0442\u0438 \u0430\u0431\u043e \u0432\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u0437\u0430\u043c\u0456\u0441\u0442\u044c \u0432\u0456\u0434\u043a\u0440\u0438\u0442\u0438\u0445.
+dialog.openappend.text=\u0414\u043e\u0434\u0430\u0442\u0438 \u0434\u043e \u043f\u043e\u0442\u043e\u0447\u043d\u0438\u0445 \u0434\u0430\u043d\u0438\u0445?
+dialog.deletepoint.title=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0443
+dialog.deletepoint.deletephoto=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443 \u0437 \u0446\u0456\u0454\u0457 \u0442\u043e\u0447\u043a\u0438?
+dialog.deletephoto.title=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0443
+dialog.deletephoto.deletepoint=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0443 \u0437 \u0446\u0456\u0454\u0457 \u0447\u0432\u0456\u0442\u043b\u0438\u043d\u0438?
+dialog.deleteaudio.deletepoint=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0443 \u0437 \u0446\u044c\u043e\u0433\u043e \u0437\u0432\u0443\u043a\u043e\u0437\u0430\u043f\u0438\u0441\u0443?
+dialog.openoptions.title=\u0412\u0456\u0434\u043a\u0440\u0438\u0442\u0438 \u043e\u043f\u0446\u0456\u0457
+dialog.openoptions.filesnippet=\u0424\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u0444\u0430\u0439\u043b\u0443
+dialog.load.table.field=\u041f\u043e\u043b\u0435
+dialog.load.table.datatype=\u0422\u0438\u043f \u0434\u0430\u043d\u0438\u0445
+dialog.load.table.description=\u041e\u043f\u0438\u0441
+dialog.delimiter.label=\u0420\u043e\u0437\u0434\u0456\u043b\u044c\u043d\u0438\u043a \u043f\u043e\u043b\u0456\u0432
+dialog.delimiter.comma=\u041a\u043e\u043c\u0430 ,
+dialog.delimiter.tab=\u0422\u0430\u0431\u0443\u043b\u044f\u0446\u0456\u044f
+dialog.delimiter.space=\u041f\u0440\u043e\u0431\u0456\u043b
+dialog.delimiter.semicolon=\u041a\u0440\u0430\u043f\u043a\u0430 \u0437 \u043a\u043e\u043c\u043e\u044e ;
+dialog.delimiter.other=\u0406\u043d\u0448\u0435
+dialog.openoptions.deliminfo.records=\u0437\u0430\u043f\u0438\u0441, \u0437
+dialog.openoptions.deliminfo.fields=\u043f\u043e\u043b\u0435
+dialog.openoptions.deliminfo.norecords=\u041d\u0435\u043c\u0430\u0454 \u0437\u0430\u043f\u0438\u0441\u0456\u0432
+dialog.openoptions.altitudeunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u0432\u0438\u0441\u043e\u0442
+dialog.openoptions.speedunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u0448\u0432\u0438\u0434\u043a\u043e\u0441\u0442\u0456
+dialog.openoptions.vertspeedunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u0448\u0432\u0438\u0434\u043a\u043e\u0441\u0442\u0456 \u043f\u043e \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0456
+dialog.openoptions.vspeed.positiveup=\u041f\u043e\u0437\u0438\u0442\u0438\u0432\u043d\u0456 \u0448\u0432\u0438\u0434\u043a\u043e\u0441\u0442\u0456 \u0432\u0433\u043e\u0440\u0443
+dialog.openoptions.vspeed.positivedown=\u041f\u043e\u0437\u0438\u0442\u0438\u0432\u043d\u0456 \u0448\u0432\u0438\u0434\u043a\u043e\u0441\u0442\u0456 \u0432\u043d\u0438\u0437
+dialog.open.contentsdoubled=\u0426\u0435\u0439 \u0444\u0430\u0439\u043b \u043c\u0456\u0441\u0442\u0438\u0442\u044c \u0434\u0443\u0431\u043b\u044e\u0432\u0430\u043d\u043d\u044f \u0432 \u043a\u043e\u0436\u043d\u0456\u0439 \u0442\u043e\u0447\u0446\u0456, \n\u043e\u0434\u043d\u0430 \u044f\u043a \u0448\u043b\u044f\u0445\u043e\u0432\u0430 \u0442\u043e\u0447\u043a\u0430 \u0456 \u043e\u0434\u043d\u0430 \u044f\u043a \u0442\u0440\u0435\u043a\u043e\u0432\u0430 \u0442\u043e\u0447\u043a\u0430.
+dialog.selecttracks.intro=\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0442\u0440\u0435\u043a(-\u0438) \u0434\u043b\u044f \u0432\u0456\u0434\u043a\u0440\u0438\u0442\u0442\u044f
+dialog.selecttracks.noname=\u0411\u0435\u0437\u0456\u043c\u0435\u043d\u043d\u0438\u0439
+dialog.jpegload.subdirectories=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u043f\u0456\u0434\u0442\u0435\u043a\u0438
+dialog.jpegload.loadjpegswithoutcoords=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0438 \u0431\u0435\u0437 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442
+dialog.jpegload.loadjpegsoutsidearea=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u0441\u0432\u0456\u0442\u043b\u0438\u043d\u0438 \u0437\u0430 \u043c\u0435\u0436\u0430\u043c\u0438 \u043f\u043e\u0442\u043e\u0447\u043d\u043e\u0457 \u043e\u0431\u043b\u0430\u0441\u0442\u0456
+dialog.jpegload.progress.title=\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0435\u043d\u043d\u044f \u0441\u0432\u0456\u0442\u043b\u0438\u043d
+dialog.jpegload.progress=\u0411\u0443\u0434\u044c-\u043b\u0430\u0441\u043a\u0430, \u0437\u0430\u0447\u0435\u043a\u0430\u0439\u0442\u0435, \u0439\u0434\u0435 \u043f\u043e\u0448\u0443\u043a \u0441\u0432\u0456\u0442\u043b\u0438\u043d
+dialog.gpsload.nogpsbabel=\u201egpsbabel\u201c \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u043e. \u041f\u0440\u043e\u0434\u043e\u0432\u0436\u0438\u0442\u0438?
+dialog.gpsload.device=\u0406\u043c\u2019\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e
+dialog.gpsload.format=\u0424\u043e\u0440\u043c\u0430\u0442
+dialog.gpsload.getwaypoints=\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0448\u043b\u044f\u0445\u043e\u0432\u0456 \u0442\u043e\u0447\u043a\u0438
+dialog.gpsload.gettracks=\u0417\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0442\u0440\u0435\u043a\u0438
+dialog.gpsload.save=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0434\u043e \u0444\u0430\u0439\u043b\u0443
+dialog.gpssend.sendwaypoints=\u0412\u0438\u0441\u043b\u0430\u0442\u0438 \u0448\u043b\u044f\u0445\u043e\u0432\u0456 \u0442\u043e\u0447\u043a\u0438
+dialog.gpssend.sendtracks=\u0412\u0438\u0441\u043b\u0430\u0442\u0438 \u0442\u0440\u0435\u043a\u0438
+dialog.gpssend.trackname=\u041d\u0430\u0437\u0432\u0430 \u0442\u0440\u0435\u043a\u0443
+dialog.gpsbabel.filters=\u0424\u0456\u043b\u044c\u0442\u0440\u0438
+dialog.addfilter.title=\u0414\u043e\u0434\u0430\u0442\u0438 \u0444\u0456\u043b\u044c\u0442\u0440
+dialog.gpsbabel.filter.discard=\u0412\u0456\u0434\u043a\u0438\u043d\u0443\u0442\u0438
+dialog.gpsbabel.filter.simplify=\u0421\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u0438
+dialog.gpsbabel.filter.distance=\u0412\u0456\u0434\u0441\u0442\u0430\u043d\u044c
+dialog.gpsbabel.filter.interpolate=\u0406\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044e\u0432\u0430\u0442\u0438
+dialog.gpsbabel.filter.discard.intro=\u0412\u0456\u0434\u043a\u0438\u043d\u0443\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u044f\u043a\u0449\u043e
+dialog.gpsbabel.filter.discard.hdop=\u0413\u043e\u0440\u041f\u043e\u0433\u0456\u0440\u0448\u0422\u043e\u0447\u043d >
+dialog.gpsbabel.filter.discard.vdop=\u0412\u0435\u0440\u0442\u041f\u043e\u0433\u0456\u0440\u0448\u0422\u043e\u0447\u043d >
+dialog.gpsbabel.filter.discard.numsats=\u0427\u0438\u0441\u043b\u043e \u0441\u0443\u043f\u0443\u0442\u043d\u0438\u043a\u0456\u0432 <
+dialog.gpsbabel.filter.discard.nofix=\u0422\u043e\u0447\u043a\u0430 \u043d\u0435 \u043c\u0430\u0454 \u0432\u0438\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044f
+dialog.gpsbabel.filter.discard.unknownfix=\u0422\u043e\u0447\u043a\u0430 \u043c\u0430\u0454 \u043d\u0435\u0432\u0456\u0434\u043e\u043c\u0435 \u0432\u0438\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044f
+dialog.gpsbabel.filter.simplify.intro=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0438 \u0434\u043e
+dialog.gpsbabel.filter.simplify.maxpoints=\u041a\u0456\u043b\u044c\u043a\u0456\u0441\u0442\u044c \u0442\u043e\u0447\u043e\u043a <
+dialog.gpsbabel.filter.simplify.maxerror=\u0430\u0431\u043e \u043f\u043e\u043c\u0438\u043b\u043a\u0430 \u0432\u0456\u0434\u0441\u0442\u0430\u043d\u0456 <
+dialog.gpsbabel.filter.simplify.crosstrack=\u043f\u0435\u0440\u0435\u0445\u0440\u0435\u0449\u0435\u043d\u043d\u044f \u0442\u0440\u0435\u043a\u0456\u0432
+dialog.gpsbabel.filter.simplify.length=\u0440\u0456\u0437\u043d\u0438\u0446\u044f \u0432 \u0434\u043e\u0432\u0436\u0438\u043d\u0456
+dialog.gpsbabel.filter.simplify.relative=\u0432\u0456\u0434\u043d\u043e\u0441\u043d\u043e \u0413\u043e\u0440\u041f\u043e\u0433\u0456\u0440\u0448\u0422\u043e\u0447\u043d
+dialog.gpsbabel.filter.distance.intro=\u0412\u0438\u043b\u0443\u0447\u0438\u0442\u0438 \u0442\u043e\u0447\u043a\u0438, \u044f\u043a\u0449\u043e \u0431\u043b\u0438\u0437\u044c\u043a\u043e \u0434\u043e \u0431\u0443\u0434\u044c-\u044f\u043a\u043e\u0457 \u043f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u044c\u043e\u0457 \u0442\u043e\u0447\u043a\u0438
+dialog.gpsbabel.filter.distance.distance=\u042f\u043a\u0449\u043e \u0432\u0456\u0434\u0441\u0442\u0430\u043d\u044c <
+dialog.gpsbabel.filter.distance.time=\u0456 \u0440\u0456\u0437\u043d\u0438\u0446\u044f \u0432 \u0447\u0430\u0441\u0456 <
+dialog.gpsbabel.filter.interpolate.intro=\u0414\u043e\u0434\u0430\u0442\u0438 \u0434\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u0456 \u0442\u043e\u0447\u043a\u0438 \u043c\u0456\u0436 \u0442\u043e\u0447\u043e\u043a \u043d\u0430 \u0442\u0440\u0435\u043a\u0443
+dialog.gpsbabel.filter.interpolate.distance=\u042f\u043a\u0449\u043e \u0432\u0456\u0434\u0441\u0442\u0430\u043d\u044c >
+dialog.gpsbabel.filter.interpolate.time=\u0430\u0431\u043e \u0440\u0456\u0437\u043d\u0438\u0446\u044f \u0432 \u0447\u0430\u0441\u0456 >
+dialog.saveoptions.title=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0444\u0430\u0439\u043b
+dialog.save.fieldstosave=\u041f\u043e\u043b\u044f \u0434\u043b\u044f \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u043d\u044f
+dialog.save.table.field=\u041f\u043e\u043b\u0435
+dialog.save.table.hasdata=\u041c\u0430\u0454 \u0434\u0430\u0442\u0443
+dialog.save.table.save=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438
+dialog.save.headerrow=\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0440\u044f\u0434\u043a\u0430
+dialog.save.coordinateunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442
+dialog.save.altitudeunits=\u041e\u0434\u0438\u043d\u0438\u0446\u0456 \u0432\u0438\u0441\u043e\u0442\u0438
+dialog.save.timestampformat=\u0424\u043e\u0440\u043c\u0430\u0442 \u0447\u0430\u0441\u0443
+dialog.save.overwrite.title=\u0424\u0430\u0439\u043b \u0432\u0436\u0435 \u0456\u0441\u043d\u0443\u0454
+dialog.save.overwrite.text=\u0424\u0430\u0439\u043b \u0432\u0436\u0435 \u0456\u0441\u043d\u0443\u0454. \u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u0439\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u0442\u0438?
+dialog.save.notypesselected=\u041d\u0435 \u0432\u0438\u0431\u0440\u0430\u043d\u043e \u0442\u0438\u043f \u0442\u043e\u0447\u043e\u043a
+dialog.exportkml.text=\u043e\u043f\u0438\u0441 \u0434\u043e \u0434\u0430\u043d\u0438\u0445
+dialog.exportkml.altitude=\u0410\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u0456 \u0432\u0438\u0441\u043e\u0442\u0438 (\u0434\u043b\u044f \u0430\u0432\u0456\u0430\u0446\u0456\u0457)
+dialog.exportkml.kmz=\u0421\u0442\u0438\u0441\u043d\u0435\u043d\u043d\u044f \u0434\u043b\u044f kmz-\u0444\u0430\u0439\u043b\u0443
+dialog.exportkml.exportimages=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0435\u0441\u043a\u0456\u0437\u0443 \u0432 kmz
+dialog.exportkml.trackcolour=\u041a\u043e\u043b\u0456\u0440 \u0442\u0440\u0435\u043a\u0443
+
+# External urls
+url.googlemaps=maps.google.com.ua
+wikipedia.lang=uk
+openweathermap.lang=ua
+
+# Below here is still Russian
+#############################
+
menu.file.recentfiles=\u6700\u8fd1\u6253\u5f00\u8fc7\u6587\u4ef6
menu.file.save=\u4fdd\u5b58
menu.file.exit=\u9000\u51fa
+menu.online=\u8054\u7f51
menu.track=\u8f68\u8ff9
menu.track.undo=\u64a4\u9500
menu.track.clearundo=\u6e05\u9664\u64a4\u9500\u6e05\u5355
# Alt keys for menus
altkey.menu.file=F
+altkey.menu.online=N
altkey.menu.track=T
altkey.menu.range=R
altkey.menu.point=P
function.learnestimationparams=\u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4
function.setmapbg=\u80cc\u666f\u5730\u56fe
function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84
+function.splitsegments=\u5206\u5272\u8f68\u8ff9
+function.sewsegments=\u63a5\u5408\u8f68\u8ff9\u7247\u6bb5
function.getgpsies=\u83b7\u53d6Gpsies\u8f68\u8ff9
function.uploadgpsies=\u4e0a\u4f20\u8f68\u8ff9\u5230Gpsies
function.lookupsrtm=\u4eceSRTM\u83b7\u5f97\u9ad8\u5ea6\u4fe1\u606f
+function.downloadsrtm=\u4e0b\u8f7dSRTM\u6570\u636e
function.getwikipedia=\u7ef4\u57fa\u767e\u79d1\u6709\u5173\u672c\u5730\u6587\u7ae0
function.searchwikipedianames=\u6309\u540d\u5b57\u4ece\u7ef4\u57fa\u767e\u79d1\u67e5\u627e
function.downloadosm=\u4e0b\u8f7d\u6b64\u5730OSM\u6570\u636e
function.saveconfig=\u4fdd\u5b58\u8bbe\u7f6e
function.diskcache=\u4fdd\u5b58\u5730\u56fe
function.managetilecache=\u7ba1\u7406\u5730\u56fe\u533a\u57df\u6570\u636e\u7f13\u5b58
+function.getweatherforecast=\u83b7\u53d6\u5929\u6c14\u9884\u62a5
# Dialogs
dialog.exit.confirm.title=\u9000\u51fa
dialog.exportpov.modelstyle=\u6a21\u578b\u7c7b\u578b
dialog.exportpov.ballsandsticks=\u7403\u6746\u6a21\u578b
dialog.exportpov.tubesandwalls=\u7ba1\u5899\u6a21\u578b
-dialog.3d.warningtracksize=\u8f68\u8ff9\u542b\u6709\u592a\u591a\u822a\u70b9\uff0cJAVA3D\u53ef\u80fd\u65e0\u6cd5\u663e\u793a\u3002\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.3d.warningtracksize=\u8f68\u8ff9\u542b\u6709\u592a\u591a\u822a\u70b9\uff0cJAVA3D\u53ef\u80fd\u65e0\u6cd5\u663e\u793a\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.3d.useterrain=\u663e\u793a\u5730\u5f62
+dialog.3d.terraingridsize=\u7f51\u683c\u5927\u5c0f
dialog.exportpov.baseimage=\u57fa\u7840\u56fe
dialog.exportpov.cannotmakebaseimage=\u65e0\u6cd5\u4fdd\u5b58\u57fa\u7840\u56fe
dialog.baseimage.title=\u8bbe\u7f6e\u57fa\u7840\u56fe
dialog.exportsvg.gradients=\u4f7f\u7528\u6e10\u53d8\u8272
dialog.exportimage.noimagepossible=\u8f93\u51fa\u7684\u5730\u56fe\u56fe\u50cf\u9996\u5148\u9700\u8981\u7f13\u5b58\u5728\u672c\u5730\u78c1\u76d8\u4e0a
dialog.exportimage.drawtrack=\u7ed8\u51fa\u8f68\u8ff9
+dialog.exportimage.drawtrackpoints=\u7ed8\u51fa\u5404\u8f68\u8ff9\u70b9
dialog.exportimage.textscalepercent=\u6587\u5b57\u7f29\u653e\u6bd4\u4f8b (%)
dialog.pointtype.desc=\u4fdd\u5b58\u4e0b\u5217\u70b9\uff1a
dialog.pointtype.track=\u8f68\u8ff9\u70b9
dialog.pointtype.audio=\u5e26\u58f0\u97f3\u7684\u822a\u70b9
dialog.pointtype.selection=\u4ec5\u5df2\u9009\u62e9\u822a\u6bb5
dialog.confirmreversetrack.title=\u786e\u8ba4\u53cd\u8f6c
-dialog.confirmreversetrack.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u53cd\u8f6c\u540e\u53ef\u80fd\u4e22\u5931\u3002\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.confirmreversetrack.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u53cd\u8f6c\u540e\u53ef\u80fd\u4e22\u5931\n\u662f\u5426\u7ee7\u7eed\uff1f
dialog.confirmcutandmove.title=\u786e\u8ba4\u526a\u5207\u548c\u79fb\u52a8
-dialog.confirmcutandmove.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u79fb\u52a8\u540e\u53ef\u80fd\u4e22\u5931\u3002\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.confirmcutandmove.text=\u8f68\u8ff9\u5305\u542b\u65f6\u95f4\u4fe1\u606f\uff0c\u79fb\u52a8\u540e\u53ef\u80fd\u4e22\u5931\n\u662f\u5426\u7ee7\u7eed\uff1f
dialog.interpolate.parameter.text=\u6240\u9009\u4e24\u70b9\u4e2d\u63d2\u5165\u70b9\u7684\u4e2a\u6570
dialog.interpolate.betweenwaypoints=\u521b\u5efa\u4e2d\u7ee7\u822a\u70b9\uff1f
dialog.undo.title=\u64a4\u9500\u64cd\u4f5c
dialog.wikipedia.column.name=\u6587\u7ae0\u9898\u76ee
dialog.wikipedia.column.distance=\u8ddd\u79bb
dialog.correlate.notimestamps=\u6570\u636e\u70b9\u4e2d\u65e0\u65f6\u95f4\u4fe1\u606f\uff0c\u7167\u7247\u65e0\u6cd5\u94fe\u63a5
-dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u7167\u7247\u5df2\u94fe\u63a5\u3002\u7ee7\u7eed\uff1f
-dialog.correlate.nouncorrelatedaudios=\u6240\u6709\u97f3\u9891\u5df2\u94fe\u63a5\u3002\u7ee7\u7eed\uff1f
+dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u7167\u7247\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f
+dialog.correlate.nouncorrelatedaudios=\u6240\u6709\u97f3\u9891\u5df2\u94fe\u63a5\uff0c\u7ee7\u7eed\uff1f
dialog.correlate.photoselect.intro=\u9009\u62e9\u5df2\u94fe\u63a5\u7167\u7247\u4f5c\u4e3a\u65f6\u95f4\u504f\u79fb
dialog.correlate.select.photoname=\u7167\u7247\u540d
dialog.correlate.select.timediff=\u65f6\u95f4\u5dee
dialog.correlate.select.photolater=\u7167\u7247\u5ef6\u540e
-dialog.correlate.options.tip=\u63d0\u793a\uff1a\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u7167\u7247\uff0c\u53ef\u81ea\u52a8\u8ba1\u7b97\u65f6\u95f4\u504f\u79fb
dialog.correlate.options.intro=\u9009\u62e9\u81ea\u52a8\u94fe\u63a5\u8bbe\u7f6e
dialog.correlate.options.offsetpanel=\u65f6\u95f4\u504f\u79fb
dialog.correlate.options.offset=\u504f\u79fb
dialog.correlate.options.nodistancelimit=\u65e0\u8ddd\u79bb\u9650\u5236
dialog.correlate.options.distancelimit=\u8ddd\u79bb\u9650\u5236
dialog.correlate.options.correlate=\u5173\u8054
-dialog.correlate.alloutsiderange=\u65e0\u6cd5\u94fe\u63a5\uff0c\u6240\u6709\u7167\u7247\u8d85\u51fa\u8f68\u8ff9\u65f6\u95f4\u8303\u56f4\u3002\n\u8bf7\u6539\u53d8\u65f6\u95f4\u504f\u79fb\u6216\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u5728\u7167\u7247\u3002
+dialog.correlate.alloutsiderange=\u65e0\u6cd5\u94fe\u63a5\uff0c\u6240\u6709\u7167\u7247\u8d85\u51fa\u8f68\u8ff9\u65f6\u95f4\u8303\u56f4\n\u8bf7\u6539\u53d8\u65f6\u95f4\u504f\u79fb\u6216\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u5728\u7167\u7247
dialog.correlate.filetimes=\u6587\u4ef6\u65f6\u95f4\u8868\u793a\u58f0\u97f3\u7684\uff1a
dialog.correlate.filetimes2=\u90e8\u5206
dialog.correlate.correltimes=\u5982\u8981\u5173\u8054\uff0c\u8bf7\u4f7f\u7528\uff1a
dialog.compress.douglaspeucker.title=Douglas-Peucker \u538b\u7f29
dialog.compress.douglaspeucker.paramdesc=\u95f4\u8ddd\u7cfb\u6570
dialog.compress.summarylabel=\u8981\u5220\u9664\u7684\u70b9
-dialog.compress.confirm1=\u5df2\u6807\u8bb0
-dialog.compress.confirm2=\u70b9\u3002\n\u70b9\u51fb \u8f68\u8ff9->\u5220\u9664 \u5220\u9664\u8fd9\u4e9b\u70b9
+dialog.compress.confirm=\u5df2\u6807\u8bb0 %d \u70b9\n\u70b9\u51fb \u8f68\u8ff9->\u5220\u9664 \u5220\u9664\u8fd9\u4e9b\u70b9
dialog.compress.confirmnone=\u672a\u6807\u8bb0\u4efb\u4f55\u70b9
dialog.deletemarked.nonefound=\u65e0\u6cd5\u5220\u9664\u6570\u636e\u70b9
dialog.pastecoordinates.desc=\u5728\u6b64\u8f93\u5165\u6216\u7c98\u8d34\u5750\u6807\u70b9
dialog.diskcache.deleteold=\u5220\u9664\u65e7\u5730\u56fe\u5757
dialog.diskcache.maximumage=\u6700\u957f\u65f6\u95f4(\u5929)
dialog.diskcache.deleteall=\u5220\u9664\u6240\u6709\u5730\u56fe\u5757
-dialog.diskcache.deleted1=\u5df2\u5220\u9664
-dialog.diskcache.deleted2=\u7f13\u5b58\u5185\u6587\u4ef6
+dialog.diskcache.deleted=\u5df2\u5220\u9664 %d \u7f13\u5b58\u5185\u6587\u4ef6
dialog.deletefieldvalues.intro=\u9009\u62e9\u5f53\u524d\u8303\u56f4\u5185\u8981\u5220\u9664\u7684\u5b57\u6bb5
dialog.deletefieldvalues.nofields=\u9009\u5b9a\u8303\u56f4\u5185\u6ca1\u6709\u8981\u5220\u9664\u7684\u5b57\u6bb5
dialog.setlinewidth.text=\u8f93\u5165\u8f68\u8ff9\u7ebf\u5bbd\u50cf\u7d20\u503c(1-4)
dialog.downloadosm.desc=\u786e\u8ba4\u4eceOSM\u4e0b\u8f7d\u8be5\u5730\u533a\u539f\u59cb\u6570\u636e:
dialog.searchwikipedianames.search=\u67e5\u627e:
+dialog.weather.location=\u5730\u70b9
+dialog.weather.update=\u5929\u6c14\u9884\u62a5\u66f4\u65b0
+dialog.weather.sunrise=\u65e5\u51fa
+dialog.weather.sunset=\u65e5\u843d
+dialog.weather.temperatureunits=\u6e29\u5ea6
+dialog.weather.currentforecast=\u5929\u6c14\u73b0\u72b6
+dialog.weather.dailyforecast=\u9010\u65e5\u9884\u62a5
+dialog.weather.3hourlyforecast=\u4e09\u5c0f\u65f6\u9884\u62a5
+dialog.weather.day.now=\u5929\u6c14\u73b0\u72b6
+dialog.weather.day.today=\u4eca\u65e5
+dialog.weather.day.tomorrow=\u660e\u65e5
+dialog.weather.day.monday=\u5468\u4e00
+dialog.weather.day.tuesday=\u5468\u4e8c
+dialog.weather.day.wednesday=\u5468\u4e09
+dialog.weather.day.thursday=\u5468\u56db
+dialog.weather.day.friday=\u5468\u4e94
+dialog.weather.day.saturday=\u5468\u516d
+dialog.weather.day.sunday=\u5468\u65e5
+dialog.weather.creditnotice=\u5929\u6c14\u4fe1\u606f\u83b7\u53d6\u81eaopenweathermap.org\uff0c\u83b7\u53d6\u66f4\u591a\u5929\u6c14\u8be6\u60c5\u8bf7\u8bbf\u95ee\u7f51\u7ad9\u3002
# 3d window
dialog.3d.title=GpsPrune 3D \u663e\u793a
confirm.addaltitudeoffset=\u5df2\u52a0\u4e0a\u9ad8\u5ea6\u504f\u5dee
confirm.rearrangewaypoints=\u91cd\u65b0\u914d\u7f6e\u7684\u822a\u70b9
confirm.rearrangephotos=\u7167\u7247\u5df2\u91cd\u6392
+confirm.splitsegments=\u8f68\u8ff9\u5df2\u5206\u5272\u4e3a %d \u6bb5
+confirm.sewsegments=%d \u8f68\u8ff9\u6bb5\u5df2\u5408\u5e76
confirm.cutandmove=\u5df2\u79fb\u52a8\u7684\u8f68\u8ff9\u6bb5
confirm.interpolate=\u8f68\u8ff9\u70b9\u5df2\u6dfb\u52a0
confirm.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u5df2\u8f6c\u6362
-confirm.saveexif.ok1=\u5df2\u4fdd\u5b58
-confirm.saveexif.ok2=\u7167\u7247\u6587\u4ef6
+confirm.saveexif.ok=\u5df2\u4fdd\u5b58 %d \u7167\u7247\u6587\u4ef6
confirm.undo.single=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c
confirm.undo.multi=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c
confirm.jpegload.single=\u5df2\u52a0\u5165\u7167\u7247
confirm.createpoint=\u5df2\u521b\u5efa\u70b9
confirm.rotatephoto=\u7167\u7247\u5df2\u65cb\u8f6c
confirm.running=\u8bf7\u7a0d\u7b49...
-confirm.lookupsrtm1=\u627e\u5230
-confirm.lookupsrtm2=\u9ad8\u5ea6\u503c
+confirm.lookupsrtm=\u627e\u5230 %d \u9ad8\u5ea6\u503c
+confirm.downloadsrtm=\u4e0b\u8f7d %d \u9ad8\u5ea6\u6587\u4ef6\u5230\u7f13\u5b58\u4e2d
+confirm.downloadsrtm.none=\u65e0\u9700\u4e0b\u8f7d\uff0c\u6587\u4ef6\u5df2\u5b58\u50a8\u5728\u7f13\u5b58\u4e2d
confirm.deletefieldvalues=\u533a\u57df\u6570\u636e\u5df2\u5220\u9664
confirm.audioload=\u5df2\u6dfb\u52a0\u58f0\u97f3\u6587\u4ef6
confirm.correlateaudios.single=\u58f0\u97f3\u5df2\u5173\u8054
confirm.correlateaudios.multi=\u58f0\u97f3\u5df2\u5173\u8054
+# Tips, shown just once when appropriate
+tip.title=\u63d0\u793a
+tip.useamapcache=\u542f\u7528\u78c1\u76d8\u7f13\u5b58 (\u8bbe\u7f6e -> \u4fdd\u5b58\u5730\u56fe)\n\u53ef\u4ee5\u63d0\u9ad8\u663e\u793a\u901f\u5ea6\uff0c\u51cf\u5c11\u7f51\u7edc\u6d41\u91cf
+tip.learntimeparams=\u5bf9\u8bb0\u5f55\u7684\u8f68\u8ff9\u542f\u7528 \u8f68\u8ff9 -> \u4f7f\u7528\u5f53\u524d\u8f68\u8ff9\u53c2\u6570\u4f30\u8ba1\u65f6\u95f4\n\u5c06\u4f7f\u7ed3\u679c\u66f4\u52a0\u7cbe\u786e
+tip.downloadsrtm=\u53ef\u4ee5\u70b9\u51fb \u8054\u7f51 -> \u4e0b\u8f7dSRTM\u6570\u636e\n\u5c06\u6570\u636e\u4fdd\u5b58\u5230\u78c1\u76d8\u7f13\u5b58\u4ee5\u63d0\u9ad8\u901f\u5ea6
+tip.usesrtmfor3d=\u6b64\u8f68\u8ff9\u6ca1\u6709\u9ad8\u5ea6\u4fe1\u606f\n\u53ef\u4ee5\u901a\u8fc7SRTM\u83b7\u53d6\u5927\u81f4\u9ad8\u5ea6\u4ee5\u663e\u793a3D\u89c6\u56fe
+tip.manuallycorrelateone=\u63d0\u793a\uff1a\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u7167\u7247\uff0c\u53ef\u81ea\u52a8\u8ba1\u7b97\u65f6\u95f4\u504f\u79fb
+
# Buttons
button.ok=\u786e\u5b9a
button.back=\u8fd4\u56de
button.no=\u5426
button.yestoall=\u5168\u90e8\u662f
button.notoall=\u5168\u90e8\u5426
+button.always=\u603b\u662f
button.select=\u9009\u62e9
button.selectall=\u5168\u9009
button.selectnone=\u5168\u4e0d\u9009
fieldname.custom=\u7528\u6237
fieldname.prefix=\u6570\u636e\u6bb5
fieldname.distance=\u8ddd\u79bb
-fieldname.movingdistance=\u79fb\u52a8\u8ddd\u79bb
fieldname.duration=\u65f6\u957f
fieldname.speed=\u901f\u5ea6
fieldname.verticalspeed=\u5782\u76f4\u901f\u5ea6
units.degmin=\u5ea6-\u5206
units.deg=\u5ea6
units.iso8601=ISO 8601
+units.degreescelsius=\u6444\u6c0f\u5ea6
+units.degreescelsius.short=\u00b0C
+units.degreesfahrenheit=\u534e\u6c0f\u5ea6
+units.degreesfahrenheit.short=\u00b0F
# How to combine conditions, such as filters
logic.and=\u4e0e
# External urls
url.googlemaps=ditu.google.cn
wikipedia.lang=zh
+openweathermap.lang=zh_cn
# Cardinals for 3d plots
cardinal.n=N
undo.insert=\u63d2\u5165\u822a\u70b9
undo.reverse=\u53cd\u8f6c\u6bb5
undo.mergetracksegments=\u5408\u5e76\u6bb5
+undo.splitsegments=\u5206\u5272\u8f68\u8ff9
+undo.sewsegments=\u5408\u5e76\u8f68\u8ff9\u7247\u6bb5
undo.addtimeoffset=\u6dfb\u52a0\u65f6\u95f4\u504f\u79fb
undo.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb
undo.rearrangewaypoints=\u91cd\u65b0\u914d\u7f6e\u822a\u70b9
error.save.failed=\u5411\u6587\u4ef6\u4fdd\u5b58\u6570\u636e\u5931\u8d25
error.saveexif.filenotfound=\u627e\u4e0d\u5230\u7167\u7247\u6587\u4ef6
error.saveexif.cannotoverwrite1=\u7167\u7247
-error.saveexif.cannotoverwrite2=\u662f\u53ea\u8bfb\u6587\u4ef6\u3002\u4fdd\u5b58\u526f\u672c\uff1f
-error.saveexif.failed1=\u65e0\u6cd5\u4fdd\u5b58
-error.saveexif.failed2=\u5f20\u7167\u7247
-error.saveexif.forced1=
-error.saveexif.forced2=\u5f20\u7167\u7247\u9700\u8981\u5f3a\u5236\u6267\u884c
+error.saveexif.cannotoverwrite2=\u662f\u53ea\u8bfb\u6587\u4ef6\uff0c\u4fdd\u5b58\u526f\u672c\uff1f
+error.saveexif.failed=\u65e0\u6cd5\u4fdd\u5b58 %d \u5f20\u7167\u7247
+error.saveexif.forced=%d \u5f20\u7167\u7247\u9700\u8981\u5f3a\u5236\u6267\u884c
error.load.dialogtitle=\u5bfc\u5165\u6570\u636e\u9519\u8bef
error.load.noread=\u65e0\u6cd5\u8bfb\u6587\u4ef6
error.load.nopoints=\u6587\u4ef6\u4e2d\u65e0\u5750\u6807\u4fe1\u606f
error.jpegload.nofilesfound=\u627e\u4e0d\u5230\u6587\u4ef6
error.jpegload.nojpegsfound=\u627e\u4e0d\u5230Jpeg\u6587\u4ef6
error.jpegload.nogpsfound=\u627e\u4e0d\u5230GPS\u4fe1\u606f
-error.jpegload.exifreadfailed=Exif\u8bfb\u53d6\u9519\u8bef\u3002\u9700\u8981\u5185\u90e8\u6216\u8005\u5916\u90e8\u5e93\u624d\u80fd\u8bfb\u53d6
+error.jpegload.exifreadfailed=Exif\u8bfb\u53d6\u9519\u8bef\n\u9700\u8981\u5185\u90e8\u6216\u8005\u5916\u90e8\u5e93\u624d\u80fd\u8bfb\u53d6
error.audioload.nofilesfound=\u672a\u627e\u5230\u58f0\u97f3\u6587\u4ef6
error.gpsload.unknown=\u672a\u77e5\u9519\u8bef
error.undofailed.title=\u64a4\u9500\u5931\u8d25
error.cache.empty=\u533a\u57df\u6570\u636e\u6587\u4ef6\u5939\u7a7a
error.cache.cannotdelete=\u65e0\u53ef\u5220\u9664\u533a\u57df\u6570\u636e
error.interpolate.invalidparameter=\u8f93\u5165\u70b9\u6570\u91cf\u5fc5\u987b\u57281\u52301000\u4e4b\u95f4
-error.learnestimationparams.failed=\u65e0\u6cd5\u4ece\u6b64\u8f68\u8ff9\u5f97\u5230\u53c2\u6570\u3002 \n \u5c1d\u8bd5\u8f7d\u5165\u66f4\u591a\u8f68\u8ff9\u3002
+error.learnestimationparams.failed=\u65e0\u6cd5\u4ece\u6b64\u8f68\u8ff9\u5f97\u5230\u53c2\u6570\n \u5c1d\u8bd5\u8f7d\u5165\u66f4\u591a\u8f68\u8ff9
+error.tracksplit.nosplit=\u6b64\u8f68\u8ff9\u65e0\u6cd5\u5206\u5272
+error.downloadsrtm.nocache=\u6587\u4ef6\u65e0\u6cd5\u4fdd\u5b58\n\u8bf7\u68c0\u67e5\u78c1\u76d8\u7f13\u5b58
+error.sewsegments.nothingdone=\u8f68\u8ff9\u7247\u6bb5\u65e0\u6cd5\u5408\u5e76\n\u8be5\u8f68\u8ff9\u73b0\u5305\u542b %d \u4e2a\u7247\u6bb5
/**
* @return an object array for the format descriptions
*/
- public static Object[] getDescriptions() {
+ public static String[] getDescriptions() {
return getColumn(0);
}
// Label for filename
private JLabel _inputFileLabel = null;
// Dropdown for format of file
- private JComboBox _formatDropdown = null;
+ private JComboBox<String> _formatDropdown = null;
// Last used file suffix
private String _lastSuffix = null;
grid.add(_inputFileLabel);
JLabel formatLabel = new JLabel(I18nManager.getText("dialog.gpsload.format"));
grid.add(formatLabel);
- _formatDropdown = new JComboBox(BabelFileFormats.getDescriptions());
+ _formatDropdown = new JComboBox<String>(BabelFileFormats.getDescriptions());
grid.add(_formatDropdown);
gridPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 20));
*/
protected void saveConfigValues()
{
- // Save the filter string (but don't remove it if it's now blank)
+ // Save the filter string, clear it if it's now blank
final String filter = _filterPanel.getFilterString();
- if (filter != null && !filter.equals("")) {
- Config.setConfigString(Config.KEY_GPSBABEL_FILTER, filter);
- }
+ Config.setConfigString(Config.KEY_GPSBABEL_FILTER, filter);
}
}
{
if (currLine.indexOf('\0') >= 0)
{
- try {reader.close();} catch (IOException ioe2) {}
+ reader.close();
return; // it's a binary file, shouldn't use this cacher
}
if (currLine.trim().length() > 0)
package tim.prune.load;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JDialog;
import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JProgressBar;
-import tim.prune.I18nManager;
import tim.prune.function.Cancellable;
+import tim.prune.gui.GenericProgressDialog;
/**
* Class to show a progress dialog for loading media.
- * Used for regular photo / audio loads plus the async
- * loading function.
+ * Used for regular photo / audio loads plus the async loading function.
+ * Maybe this class isn't really needed...
*/
-public class MediaLoadProgressDialog
+public class MediaLoadProgressDialog extends GenericProgressDialog
{
- private JDialog _progressDialog = null;
- private JProgressBar _progressBar = null;
- private JFrame _parentFrame = null;
- private Cancellable _function = null;
-
/**
* Constructor
* @param inParentFrame parent frame for creating dialog
*/
public MediaLoadProgressDialog(JFrame inParentFrame, Cancellable inFunction)
{
- _parentFrame = inParentFrame;
- _function = inFunction;
- }
-
- /**
- * Create the dialog to show the progress
- */
- private void createProgressDialog()
- {
- _progressDialog = new JDialog(_parentFrame, I18nManager.getText("dialog.jpegload.progress.title"));
- _progressDialog.setLocationRelativeTo(_parentFrame);
- _progressBar = new JProgressBar(0, 100);
- _progressBar.setValue(0);
- _progressBar.setStringPainted(true);
- _progressBar.setString("");
- JPanel panel = new JPanel();
- panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
- panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
- panel.add(new JLabel(I18nManager.getText("dialog.jpegload.progress")));
- panel.add(_progressBar);
- JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
- cancelButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _function.cancel();
- }
- });
- panel.add(cancelButton);
- _progressDialog.getContentPane().add(panel);
- _progressDialog.pack();
- _progressDialog.setVisible(true);
- }
-
- /**
- * Show the dialog in indeterminate mode, before limits are calculated
- */
- public void show()
- {
- if (_progressDialog == null)
- {
- createProgressDialog();
- _progressBar.setIndeterminate(true);
- }
- }
-
- /**
- * Update the progress bar
- * @param inCurrent current value
- * @param inMax maximum value
- */
- public void showProgress(int inCurrent, int inMax)
- {
- if (_progressDialog == null)
- createProgressDialog();
- if (_progressBar.isIndeterminate())
- _progressBar.setIndeterminate(false);
- if (inMax > 0)
- _progressBar.setMaximum(inMax);
- _progressBar.setValue(inCurrent);
- _progressBar.setString("" + inCurrent + " / " + _progressBar.getMaximum());
- }
-
- /**
- * Close the dialog
- */
- public void close()
- {
- if (_progressDialog != null)
- _progressDialog.dispose();
+ super("dialog.jpegload.progress.title", "dialog.jpegload.progress", inParentFrame, inFunction);
}
}
private JLabel _statusLabel = null;
private DelimiterInfo[] _delimiterInfos = null;
private FileCacher _fileCacher = null;
- private JList _snippetBox = null;
+ private JList<String> _snippetBox = null;
private FileExtractTableModel _fileExtractTableModel = null;
private JTable _fieldTable;
private FieldSelectionTableModel _fieldTableModel = null;
- private JComboBox _altitudeUnitsDropdown = null;
- private JComboBox _hSpeedUnitsDropdown = null;
- private JComboBox _vSpeedUnitsDropdown = null;
+ private JComboBox<String> _altitudeUnitsDropdown = null;
+ private JComboBox<String> _hSpeedUnitsDropdown = null;
+ private JComboBox<String> _vSpeedUnitsDropdown = null;
private JRadioButton _vSpeedUpwardsRadio = null;
private ComponentHider _componentHider = null;
private int _selectedField = -1;
delimsPanel.add(_statusLabel);
firstCard.add(delimsPanel, BorderLayout.SOUTH);
// load snippet to show first few lines
- _snippetBox = new JList(_fileCacher.getSnippet(SNIPPET_SIZE, MAX_SNIPPET_WIDTH));
+ _snippetBox = new JList<String>(_fileCacher.getSnippet(SNIPPET_SIZE, MAX_SNIPPET_WIDTH));
_snippetBox.setEnabled(false);
firstCard.add(makeLabelledPanel("dialog.openoptions.filesnippet", _snippetBox), BorderLayout.CENTER);
JLabel altLabel = new JLabel(I18nManager.getText("dialog.openoptions.altitudeunits") + ": ");
altGrid.add(altLabel);
String[] altUnits = {I18nManager.getText("units.metres"), I18nManager.getText("units.feet")};
- _altitudeUnitsDropdown = new JComboBox(altUnits);
+ _altitudeUnitsDropdown = new JComboBox<String>(altUnits);
altGrid.add(_altitudeUnitsDropdown);
holderPanel.add(altUnitsPanel);
// Horizontal speed
speedPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("fieldname.speed")));
JLabel speedLabel = new JLabel(I18nManager.getText("dialog.openoptions.speedunits") + ": ");
speedGrid.add(speedLabel);
- _hSpeedUnitsDropdown = new JComboBox();
+ _hSpeedUnitsDropdown = new JComboBox<String>();
for (Unit spUnit : UnitSetLibrary.ALL_SPEED_UNITS) {
_hSpeedUnitsDropdown.addItem(I18nManager.getText(spUnit.getNameKey()));
}
vSpeedPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("fieldname.verticalspeed")));
JLabel vSpeedLabel = new JLabel(I18nManager.getText("dialog.openoptions.vertspeedunits") + ": ");
vSpeedGrid.add(vSpeedLabel);
- _vSpeedUnitsDropdown = new JComboBox();
+ _vSpeedUnitsDropdown = new JComboBox<String>();
for (Unit spUnit : UnitSetLibrary.ALL_SPEED_UNITS) {
_vSpeedUnitsDropdown.addItem(I18nManager.getText(spUnit.getNameKey()));
}
_fieldTableModel.updateData(startFieldArray);
_fieldTable.setModel(_fieldTableModel);
// add dropdowns to second column
- JComboBox fieldTypesBox = new JComboBox();
+ JComboBox<String> fieldTypesBox = new JComboBox<String>();
String[] fieldNames = Field.getFieldNames();
for (int i=0; i<fieldNames.length; i++)
{
private WholeNumberField _hdopField = null;
private WholeNumberField _vdopField = null;
- private JComboBox _combineDopsCombo = null;
+ private JComboBox<String> _combineDopsCombo = null;
private WholeNumberField _numSatsField = null;
private JCheckBox _noFixCheckbox = null;
private JCheckBox _unknownFixCheckbox = null;
_hdopField = new WholeNumberField(2);
_hdopField.addKeyListener(_paramChangeListener);
dopPanel.add(_hdopField);
- _combineDopsCombo = new JComboBox(new String[] {I18nManager.getText("logic.and"), I18nManager.getText("logic.or")});
+ _combineDopsCombo = new JComboBox<String>(new String[] {I18nManager.getText("logic.and"), I18nManager.getText("logic.or")});
dopPanel.add(_combineDopsCombo);
dopPanel.add(new JLabel(I18nManager.getText("dialog.gpsbabel.filter.discard.vdop"), SwingConstants.RIGHT));
_vdopField = new WholeNumberField(2);
}
private DecimalNumberField _distField = null;
- private JComboBox _distUnitsCombo = null;
+ private JComboBox<String> _distUnitsCombo = null;
private WholeNumberField _secondsField = null;
_distField = new DecimalNumberField();
_distField.addKeyListener(_paramChangeListener);
gridPanel.add(_distField);
- _distUnitsCombo = new JComboBox(new String[] {I18nManager.getText("units.metres"), I18nManager.getText("units.feet")});
+ _distUnitsCombo = new JComboBox<String>(new String[] {I18nManager.getText("units.metres"), I18nManager.getText("units.feet")});
gridPanel.add(_distUnitsCombo);
gridPanel.add(new JLabel(I18nManager.getText("dialog.gpsbabel.filter.distance.time")));
_secondsField = new WholeNumberField(4);
}
private DecimalNumberField _distField = null;
- private JComboBox _distUnitsCombo = null;
+ private JComboBox<String> _distUnitsCombo = null;
private WholeNumberField _secondsField = null;
_distField = new DecimalNumberField();
_distField.addKeyListener(_paramChangeListener);
gridPanel.add(_distField);
- _distUnitsCombo = new JComboBox(new String[] {I18nManager.getText("units.kilometres"), I18nManager.getText("units.miles")});
+ _distUnitsCombo = new JComboBox<String>(new String[] {I18nManager.getText("units.kilometres"), I18nManager.getText("units.miles")});
gridPanel.add(_distUnitsCombo);
gridPanel.add(new JLabel(I18nManager.getText("dialog.gpsbabel.filter.interpolate.time")));
_secondsField = new WholeNumberField(4);
private WholeNumberField _maxPointsField = null;
private DecimalNumberField _distField = null;
- private JComboBox _distUnitsCombo = null;
+ private JComboBox<String> _distUnitsCombo = null;
private JRadioButton _crossTrackRadio = null;
private JRadioButton _lengthRadio = null;
private JRadioButton _relativeRadio = null;
_distField = new DecimalNumberField();
_distField.addKeyListener(_paramChangeListener);
gridPanel.add(_distField);
- _distUnitsCombo = new JComboBox(new String[] {
+ _distUnitsCombo = new JComboBox<String>(new String[] {
I18nManager.getText(UnitSetLibrary.UNITS_KILOMETRES.getNameKey()),
I18nManager.getText(UnitSetLibrary.UNITS_MILES.getNameKey())
});
import java.io.File;
import java.io.FileInputStream;
import java.util.zip.GZIPInputStream;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
import tim.prune.App;
import tim.prune.I18nManager;
import tim.prune.data.SourceInfo;
{
istream = new GZIPInputStream(new FileInputStream(inFile));
_xmlLoader.reset();
- SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
- saxParser.parse(istream, _xmlLoader);
+ // Parse the stream using either Xerces or java classes
+ _xmlLoader.parseXmlStream(istream);
XmlHandler handler = _xmlLoader.getHandler();
if (handler == null) {
_app.showErrorMessage("error.load.dialogtitle", "error.load.noread");
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
import tim.prune.App;
import tim.prune.I18nManager;
public void run()
{
FileInputStream inStream = null;
+ boolean success = false;
try
{
- // Construct a SAXParser and use this as a default handler
- SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
inStream = new FileInputStream(_file);
- saxParser.parse(inStream, this);
+ success = parseXmlStream(inStream);
+ }
+ catch (FileNotFoundException fnfe) {}
+
+ // Clean up the stream, don't need it any more
+ try {inStream.close();} catch (IOException e2) {}
+ if (success)
+ {
// Check whether handler was properly instantiated
if (_handler == null)
{
new MediaLinkInfo(_handler.getLinkArray()));
}
}
- catch (Exception e)
+ }
+
+
+ /**
+ * Try both Xerces and the built-in java classes to parse the given xml stream
+ * @param inStream input stream from file / zip / gzip
+ * @return true on success, false if both xerces and built-in parser failed
+ */
+ public boolean parseXmlStream(InputStream inStream)
+ {
+ boolean success = false;
+ // Firstly, try to use xerces to parse the xml (will throw an exception if not available)
+ try
{
- // Show error dialog
- _app.showErrorMessageNoLookup("error.load.dialogtitle",
- I18nManager.getText("error.load.othererror") + " " + e.getMessage());
+ XMLReader xmlReader = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
+ xmlReader.setContentHandler(this);
+ xmlReader.parse(new InputSource(inStream));
+ success = true; // worked
}
- finally {
- try {inStream.close();} catch (IOException e2) {}
+ catch (Exception e) {} // don't care too much if it didn't work, there's a backup
+
+ // If that didn't work, try the built-in classes (which work for xml1.0 but handling for 1.1 contains bugs)
+ if (!success)
+ {
+ try
+ {
+ // Construct a SAXParser and use this as a default handler
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ saxParser.parse(inStream, this);
+ success = true;
+ }
+ catch (Exception e)
+ {
+ // Show error dialog
+ _app.showErrorMessageNoLookup("error.load.dialogtitle",
+ I18nManager.getText("error.load.othererror") + " " + e.getMessage());
+ }
}
+ return success;
}
-
/**
* Receive a tag
* @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
if (suffix.equals(".kml") || suffix.equals(".gpx") || suffix.equals(".xml"))
{
_xmlLoader.reset();
- SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
- saxParser.parse(file.getInputStream(entry), _xmlLoader);
+ // Parse the stream using either Xerces or java classes
+ _xmlLoader.parseXmlStream(file.getInputStream(entry));
XmlHandler handler = _xmlLoader.getHandler();
if (handler == null) {
_app.showErrorMessage("error.load.dialogtitle", "error.load.othererror");
-GpsPrune version 15.2
-=====================
+GpsPrune version 16
+===================
GpsPrune is an application for viewing, editing and managing coordinate data from GPS systems,
including format conversion, charting and photo correlation.
Full details can be found at http://activityworkshop.net/software/gpsprune/
-GpsPrune is copyright 2006-2013 activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
+GpsPrune is copyright 2006-2014 activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
You may freely use the software, and may help others to freely use it too. For further information
on your rights and how they are protected, see the included license.txt file.
=======
To run GpsPrune from the jar file, simply call it from a command prompt or shell:
- java -jar gpsprune_15.2.jar
+ java -jar gpsprune_16.jar
If the jar file is saved in a different directory, you will need to include the path.
Depending on your system settings, you may be able to click or double-click on the jar file
or other link can of course be made should you wish.
To specify a language other than the default, use an additional parameter, eg:
- java -jar gpsprune_15.2.jar --lang=DE
+ java -jar gpsprune_16.jar --lang=DE
-New with version 15.2
+New with version 16
=====================
The following features were added since version 15:
- - Improved translations
- - Fixed bug with speed charts using gnuplot
- - Fixed bug with dragging a mid-point within a selection
- - Fixed bug with duplicate entries in profile popup menu
- - Fixed bug with loading zoom level of custom map sources
+ - Extend povray output using terrain and/or map image
+ - Extend java3d output using terrain and/or map image
+ - Weather forecasts
+ - Splitting a track into segments based on distance or time
+ - Sewing track segments together
+ - Function to download and save SRTM tiles
New with version 15
-===================
+=====================
The following features were added since version 14:
- Extend povray output using map image on base plane
- Export an image of the map and track at a selected zoom level
- Allow loading of speeds and vertical speeds from text files
New with version 14
-===================
+=====================
The following features were added since version 13:
- Dragging of existing points
- Creation of new points by dragging the halfway point between two points
package tim.prune.save;
import java.awt.BorderLayout;
-import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
+import javax.swing.JProgressBar;
-import tim.prune.DataSubscriber;
import tim.prune.I18nManager;
import tim.prune.config.Config;
import tim.prune.data.Track;
import tim.prune.gui.map.MapSource;
import tim.prune.gui.map.MapSourceLibrary;
+import tim.prune.threedee.ImageDefinition;
/**
* Dialog to let you choose the parameters for a base image
- * (source and zoom)
+ * (source and zoom) including preview
*/
public class BaseImageConfigDialog implements Runnable
{
/** Parent to notify */
- private DataSubscriber _parent = null;
+ private BaseImageConsumer _parent = null;
/** Parent dialog for position */
private JDialog _parentDialog = null;
/** Track to use for preview image */
/** Panel to hold the other controls */
private JPanel _mainPanel = null;
/** Dropdown for map source */
- private JComboBox _mapSourceDropdown = null;
+ private JComboBox<String> _mapSourceDropdown = null;
/** Dropdown for zoom levels */
- private JComboBox _zoomDropdown = null;
- /** Warning label that image is incomplete */
- private JLabel _imageIncompleteLabel = null;
+ private JComboBox<String> _zoomDropdown = null;
+ /** Button to trigger a download of the missing map tiles */
+ private JButton _downloadTilesButton = null;
+ /** Progress bar for downloading additional tiles */
+ private JProgressBar _progressBar = null;
/** Label for number of tiles found */
private JLabel _tilesFoundLabel = null;
/** Label for image size in pixels */
private JLabel _imageSizeLabel = null;
/** Image preview panel */
private ImagePreviewPanel _previewPanel = null;
+ /** Grouter, used to avoid regenerating images */
+ private MapGrouter _grouter = new MapGrouter();
/** OK button, needs to be enabled/disabled */
private JButton _okButton = null;
/** Flag for rebuilding dialog, don't bother refreshing and recalculating */
private boolean _rebuilding = false;
/** Cached values to allow cancellation of dialog */
- private boolean _useImage = false;
- private int _sourceIndex = 0;
- private int _zoomLevel = 0;
+ private ImageDefinition _imageDef = new ImageDefinition();
/**
* @param inParentDialog parent dialog
* @param inTrack track object
*/
- public BaseImageConfigDialog(DataSubscriber inParent, JDialog inParentDialog, Track inTrack)
+ public BaseImageConfigDialog(BaseImageConsumer inParent, JDialog inParentDialog, Track inTrack)
{
_parent = inParent;
_parentDialog = inParentDialog;
_track = inTrack;
}
+ /**
+ * @param inDefinition image definition object from previous dialog
+ */
+ public void setImageDefinition(ImageDefinition inDefinition)
+ {
+ _imageDef = inDefinition;
+ if (_imageDef == null) {
+ _imageDef = new ImageDefinition();
+ }
+ }
+
/**
* Begin the function
*/
private void initDialog()
{
_rebuilding = true;
- _useImageCheckbox.setSelected(_useImage);
+ _useImageCheckbox.setSelected(_imageDef.getUseImage());
// Populate the dropdown of map sources from the library in case it has changed
_mapSourceDropdown.removeAllItems();
for (int i=0; i<MapSourceLibrary.getNumSources(); i++)
{
_mapSourceDropdown.addItem(MapSourceLibrary.getSource(i).getName());
}
- if (_sourceIndex < 0 || _sourceIndex >= _mapSourceDropdown.getItemCount()) {
- _sourceIndex = 0;
+ int sourceIndex = _imageDef.getSourceIndex();
+ if (sourceIndex < 0 || sourceIndex >= _mapSourceDropdown.getItemCount()) {
+ sourceIndex = 0;
}
- _mapSourceDropdown.setSelectedIndex(_sourceIndex);
+ _mapSourceDropdown.setSelectedIndex(sourceIndex);
// Zoom level
- if (_useImage)
+ int zoomLevel = _imageDef.getZoom();
+ if (_imageDef.getUseImage())
{
for (int i=0; i<_zoomDropdown.getItemCount(); i++)
{
String item = _zoomDropdown.getItemAt(i).toString();
try {
- if (Integer.parseInt(item) == _zoomLevel) {
+ if (Integer.parseInt(item) == zoomLevel)
+ {
_zoomDropdown.setSelectedIndex(i);
break;
}
currentZoom = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
}
catch (Exception nfe) {}
+ // First time in, the dropdown might be empty but we still might have a zoom in the definition
+ if (_zoomDropdown.getItemCount() == 0) {
+ currentZoom = _imageDef.getZoom();
+ }
// Get the extent of the track so we can work out how big the images are going to be for each zoom level
- // System.out.println("Ranges are: x=" + _track.getXRange().getRange() + ", y=" + _track.getYRange().getRange());
final double xyExtent = Math.max(_track.getXRange().getRange(), _track.getYRange().getRange());
int zoomToSelect = -1;
/**
- * @return true if image has been selected
+ * @return image definition object
*/
- public boolean useImage() {
- return _useImage;
- }
-
- /**
- * @return index of selected image source
- */
- public int getSourceIndex() {
- return _sourceIndex;
- }
-
- /**
- * @return selected zoom level
- */
- public int getZoomLevel() {
- return _zoomLevel;
+ public ImageDefinition getImageDefinition() {
+ return _imageDef;
}
/**
JLabel sourceLabel = new JLabel(I18nManager.getText("dialog.baseimage.mapsource") + ": ");
sourceLabel.setHorizontalAlignment(JLabel.RIGHT);
controlsPanel.add(sourceLabel);
- _mapSourceDropdown = new JComboBox();
+ _mapSourceDropdown = new JComboBox<String>();
_mapSourceDropdown.addItem("name of map source");
// Add listener to dropdown to change zoom levels
_mapSourceDropdown.addActionListener(new ActionListener() {
JLabel zoomLabel = new JLabel(I18nManager.getText("dialog.baseimage.zoom") + ": ");
zoomLabel.setHorizontalAlignment(JLabel.RIGHT);
controlsPanel.add(zoomLabel);
- _zoomDropdown = new JComboBox();
+ _zoomDropdown = new JComboBox<String>();
// Add action listener to enable ok button when zoom changed
_zoomDropdown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// Label panel on right
JPanel labelPanel = new JPanel();
labelPanel.setLayout(new BorderLayout());
- _imageIncompleteLabel = new JLabel(I18nManager.getText("dialog.baseimage.incomplete"));
- _imageIncompleteLabel.setForeground(Color.RED);
- _imageIncompleteLabel.setVisible(false);
- labelPanel.add(_imageIncompleteLabel, BorderLayout.NORTH);
+ JPanel downloadPanel = new JPanel();
+ downloadPanel.setLayout(new BorderLayout(4, 4));
+ _downloadTilesButton = new JButton(I18nManager.getText("button.load"));
+ _downloadTilesButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ downloadRemainingTiles();
+ }
+ });
+ _downloadTilesButton.setVisible(false);
+ downloadPanel.add(_downloadTilesButton, BorderLayout.NORTH);
+ _progressBar = new JProgressBar();
+ _progressBar.setIndeterminate(true);
+ _progressBar.setVisible(false);
+ downloadPanel.add(_progressBar, BorderLayout.SOUTH);
+ labelPanel.add(downloadPanel, BorderLayout.NORTH);
JPanel labelGridPanel = new JPanel();
labelGridPanel.setLayout(new GridLayout(0, 2, 10, 4));
labelGridPanel.add(new JLabel(I18nManager.getText("dialog.baseimage.tiles") + ": "));
private void updateImagePreview()
{
// Clear labels
- _imageIncompleteLabel.setVisible(false);
+ _downloadTilesButton.setVisible(false);
_tilesFoundLabel.setText("");
_imageSizeLabel.setText("");
if (_useImageCheckbox.isSelected() && _mapSourceDropdown.getSelectedIndex() >= 0
private void storeValues()
{
// Store values of controls in variables
- _useImage = _useImageCheckbox.isSelected();
- _sourceIndex = _mapSourceDropdown.getSelectedIndex();
- try {
- String zoomStr = _zoomDropdown.getSelectedItem().toString();
- _zoomLevel = Integer.parseInt(zoomStr);
- }
- catch (Exception nfe) {
- _zoomLevel = 0;
- }
- // Call parent to retrieve values
- _parent.dataUpdated(DataSubscriber.ALL);
+ _imageDef.setUseImage(_useImageCheckbox.isSelected(),
+ _mapSourceDropdown.getSelectedIndex(),
+ getSelectedZoomLevel());
+ // Inform parent that details have changed
+ _parent.baseImageChanged();
}
/**
final int zoomIndex = _zoomDropdown.getSelectedIndex();
if (!_useImageCheckbox.isSelected() || mapIndex < 0 || zoomIndex < 0) {return;}
- // Get the map source and zoom level
+ // Get the map source from the index
MapSource mapSource = MapSourceLibrary.getSource(mapIndex);
- int zoomLevel = 0;
- try {
- zoomLevel = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
- }
- catch (Exception e) {}
// Use the Grouter to create an image (slow, blocks thread)
- GroutedImage groutedImage = MapGrouter.createMapImage(_track, mapSource, zoomLevel);
+ GroutedImage groutedImage = _grouter.createMapImage(_track, mapSource, getSelectedZoomLevel());
// If the dialog hasn't changed, pass the generated image to the preview panel
if (_useImageCheckbox.isSelected()
&& groutedImage != null)
{
_previewPanel.setImage(groutedImage);
+ final int numTilesRemaining = groutedImage.getNumTilesTotal() - groutedImage.getNumTilesUsed();
+ final boolean offerDownload = numTilesRemaining > 0 && numTilesRemaining < 50;
// Set values of labels
- _imageIncompleteLabel.setVisible(!groutedImage.isComplete());
+ _downloadTilesButton.setVisible(offerDownload);
+ _downloadTilesButton.setEnabled(offerDownload);
_tilesFoundLabel.setText(groutedImage.getNumTilesUsed() + " / " + groutedImage.getNumTilesTotal());
if (groutedImage.getImageSize() > 0) {
_imageSizeLabel.setText("" + groutedImage.getImageSize());
{
_previewPanel.setImage(null);
// Clear labels
- _imageIncompleteLabel.setVisible(false);
+ _downloadTilesButton.setVisible(false);
_tilesFoundLabel.setText("");
_imageSizeLabel.setText("");
}
}
+ /**
+ * @return zoom level selected in the dropdown
+ */
+ private int getSelectedZoomLevel()
+ {
+ int zoomLevel = 0;
+ try {
+ zoomLevel = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
+ }
+ catch (Exception e) {
+ System.err.println("Exception: " + e.getClass().getName() + " : " + e.getMessage());
+ }
+ return zoomLevel;
+ }
+
/**
* @return true if any map data has been found for the image
*/
public boolean getFoundData()
{
- return _useImage && _zoomLevel > 0 && _previewPanel != null && _previewPanel.getTilesFound();
+ return _imageDef.getUseImage() && _imageDef.getZoom() > 0
+ && _previewPanel != null && _previewPanel.getTilesFound();
+ }
+
+ /**
+ * @return true if selected zoom is valid for the current track (based only on pixel size)
+ */
+ public boolean isSelectedZoomValid()
+ {
+ final double xyExtent = Math.max(_track.getXRange().getRange(), _track.getYRange().getRange());
+ // How many pixels does this give?
+ final int zoomFactor = 1 << _imageDef.getZoom();
+ final int pixCount = (int) (xyExtent * zoomFactor * 256);
+ return (pixCount > 100 // less than this isn't worth it
+ && pixCount < 4000); // don't want to run out of memory
+ }
+
+ /**
+ * @return the map grouter for retrieval of generated image
+ */
+ public MapGrouter getGrouter()
+ {
+ return _grouter;
+ }
+
+ /**
+ * @return method triggered by "download" button, to asynchronously download the missing tiles
+ */
+ private void downloadRemainingTiles()
+ {
+ _downloadTilesButton.setEnabled(false);
+ new Thread(new Runnable() {
+ public void run()
+ {
+ _progressBar.setVisible(true);
+ // Use a grouter to get all tiles from the TileManager, including downloading
+ MapGrouter grouter = new MapGrouter();
+ final int mapIndex = _mapSourceDropdown.getSelectedIndex();
+ if (!_useImageCheckbox.isSelected() || mapIndex < 0) {return;}
+ MapSource mapSource = MapSourceLibrary.getSource(mapIndex);
+ grouter.createMapImage(_track, mapSource, getSelectedZoomLevel(), true);
+ _progressBar.setVisible(false);
+ // And then refresh the dialog
+ _grouter.clearMapImage();
+ updateImagePreview();
+ }
+ }).start();
}
}
--- /dev/null
+package tim.prune.save;
+
+/**
+ * Interface used to inform consumers that the base image has been changed
+ */
+public interface BaseImageConsumer
+{
+ /** Notify consumer that base image has changed */
+ public void baseImageChanged();
+}
}
_progressBar.setVisible(false);
// Show confirmation
- UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.saveexif.ok1") + " "
- + numSaved + " " + I18nManager.getText("confirm.saveexif.ok2"));
+ UpdateMessageBroker.informSubscribers(I18nManager.getTextWithNumber("confirm.saveexif.ok", numSaved));
if (numFailed > 0)
{
JOptionPane.showMessageDialog(_parentFrame,
- I18nManager.getText("error.saveexif.failed1") + " " + numFailed + " "
- + I18nManager.getText("error.saveexif.failed2"),
+ I18nManager.getTextWithNumber("error.saveexif.failed", numFailed),
I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE);
}
if (numForced > 0)
{
JOptionPane.showMessageDialog(_parentFrame,
- I18nManager.getText("error.saveexif.forced1") + " " + numForced + " "
- + I18nManager.getText("error.saveexif.forced2"),
+ I18nManager.getTextWithNumber("error.saveexif.forced", numForced),
I18nManager.getText("dialog.saveexif.title"), JOptionPane.WARNING_MESSAGE);
}
// close dialog, all finished
if (inCachers != null) {gpxHeader = inCachers.getFirstHeader();}
if (gpxHeader == null || gpxHeader.length() < 5)
{
+ // TODO: Consider changing this to default to GPX 1.1
// Create default (1.0) header
gpxHeader = "<gpx version=\"1.0\" creator=\"" + GPX_CREATOR
+ "\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
return _image != null && _numTilesFound > 0;
}
- /**
- * @return true if all the required tiles were found
- */
- public boolean isComplete() {
- return _numTilesMissing == 0;
- }
-
/**
* @return the pixel dimensions of the result image
*/
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
-import javax.swing.border.EtchedBorder;
import tim.prune.App;
-import tim.prune.DataSubscriber;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.config.ColourScheme;
import tim.prune.data.DataPoint;
import tim.prune.data.DoubleRange;
import tim.prune.data.Track;
+import tim.prune.gui.BaseImageDefinitionPanel;
import tim.prune.gui.GuiGridLayout;
import tim.prune.gui.WholeNumberField;
import tim.prune.gui.map.MapSource;
import tim.prune.gui.map.MapSourceLibrary;
import tim.prune.gui.map.MapUtils;
import tim.prune.load.GenericFileFilter;
+import tim.prune.threedee.ImageDefinition;
/**
* Class to handle the exporting of map images, optionally with track data drawn on top.
* This allows images larger than the screen to be generated.
*/
-public class ImageExporter extends GenericFunction implements DataSubscriber
+public class ImageExporter extends GenericFunction implements BaseImageConsumer
{
private JDialog _dialog = null;
private JCheckBox _drawDataCheckbox = null;
+ private JCheckBox _drawTrackPointsCheckbox = null;
private WholeNumberField _textScaleField = null;
- private JLabel _baseImageLabel = null;
- private BaseImageConfigDialog _baseImageConfig = null;
+ private BaseImageDefinitionPanel _baseImagePanel = null;
private JFileChooser _fileChooser = null;
private JButton _okButton = null;
_dialog.pack();
_textScaleField.setValue(100);
}
- // Make base image dialog too
- if (_baseImageConfig == null) {
- _baseImageConfig = new BaseImageConfigDialog(this, _dialog, _app.getTrackInfo().getTrack());
- }
// Check if there is a cache to use
if (!BaseImageConfigDialog.isImagePossible())
return;
}
- updateBaseImageDetails();
+ _baseImagePanel.updateBaseImageDetails();
+ baseImageChanged();
// Show dialog
_dialog.setVisible(true);
}
// Checkbox for drawing track or not
_drawDataCheckbox = new JCheckBox(I18nManager.getText("dialog.exportimage.drawtrack"));
_drawDataCheckbox.setSelected(true); // draw by default
+ // Also whether to draw track points or not
+ _drawTrackPointsCheckbox = new JCheckBox(I18nManager.getText("dialog.exportimage.drawtrackpoints"));
+ _drawTrackPointsCheckbox.setSelected(true);
+ // Add listener to en/disable trackpoints checkbox
+ _drawDataCheckbox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ _drawTrackPointsCheckbox.setEnabled(_drawDataCheckbox.isSelected());
+ }
+ });
// TODO: Maybe have other controls such as line width, symbol scale factor
JPanel controlsPanel = new JPanel();
public void actionPerformed(ActionEvent e)
{
doExport();
- MapGrouter.clearMapImage();
+ _baseImagePanel.getGrouter().clearMapImage();
_dialog.dispose();
}
});
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- MapGrouter.clearMapImage();
+ _baseImagePanel.getGrouter().clearMapImage();
_dialog.dispose();
}
});
{
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
_dialog.dispose();
- MapGrouter.clearMapImage();
+ _baseImagePanel.getGrouter().clearMapImage();
}
}
};
_drawDataCheckbox.addKeyListener(closer);
// Panel for the base image
- JPanel imagePanel = new JPanel();
- imagePanel.setLayout(new BorderLayout(10, 4));
- imagePanel.add(new JLabel(I18nManager.getText("dialog.exportpov.baseimage") + ": "), BorderLayout.WEST);
- _baseImageLabel = new JLabel("Typical sourcename");
- imagePanel.add(_baseImageLabel, BorderLayout.CENTER);
- JButton baseImageButton = new JButton(I18nManager.getText("button.edit"));
- baseImageButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent event) {
- changeBaseImage();
- }
- });
- baseImageButton.addKeyListener(closer);
- imagePanel.add(baseImageButton, BorderLayout.EAST);
- imagePanel.setBorder(BorderFactory.createCompoundBorder(
- BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
- );
+ _baseImagePanel = new BaseImageDefinitionPanel(this, _dialog, _app.getTrackInfo().getTrack());
+
+ // Panel for the checkboxes at the top
+ JPanel checkPanel = new JPanel();
+ checkPanel.setLayout(new BoxLayout(checkPanel, BoxLayout.Y_AXIS));
+ checkPanel.add(_drawDataCheckbox);
+ checkPanel.add(_drawTrackPointsCheckbox);
// add these panels to the holder panel
JPanel holderPanel = new JPanel();
holderPanel.setLayout(new BorderLayout(5, 5));
- holderPanel.add(_drawDataCheckbox, BorderLayout.NORTH);
+ holderPanel.add(checkPanel, BorderLayout.NORTH);
holderPanel.add(controlsPanel, BorderLayout.CENTER);
- holderPanel.add(imagePanel, BorderLayout.SOUTH);
+ holderPanel.add(_baseImagePanel, BorderLayout.SOUTH);
holderPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
panel.add(holderPanel, BorderLayout.NORTH);
return panel;
}
- /**
- * Change the base image by calling the BaseImageConfigDialog
- */
- private void changeBaseImage()
- {
- // Check if there is a cache to use
- if (BaseImageConfigDialog.isImagePossible())
- {
- // Show new dialog to choose image details
- _baseImageConfig.beginWithImageYes();
- }
- }
-
- /**
- * Callback from base image config dialog
- */
- public void dataUpdated(byte inUpdateType)
- {
- updateBaseImageDetails();
- }
-
- /** Not required */
- public void actionCompleted(String inMessage) {
- }
-
- /**
- * Update the description label according to the selected base image details
- */
- private void updateBaseImageDetails()
- {
- String desc = null;
- if (_baseImageConfig.useImage())
- {
- MapSource source = MapSourceLibrary.getSource(_baseImageConfig.getSourceIndex());
- if (source != null) {
- desc = source.getName() + " ("
- + _baseImageConfig.getZoomLevel() + ")";
- }
- }
- if (desc == null) {
- desc = I18nManager.getText("dialog.about.no");
- }
- _baseImageLabel.setText(desc);
- _okButton.setEnabled(_baseImageConfig.useImage() && _baseImageConfig.getFoundData()
- && MapGrouter.isZoomLevelOk(_app.getTrackInfo().getTrack(), _baseImageConfig.getZoomLevel()));
- }
/**
* Select the file and export data to it
*/
private void doExport()
{
- // OK pressed, so choose output file
_okButton.setEnabled(false);
+ // OK pressed, so choose output file
if (_fileChooser == null)
{
_fileChooser = new JFileChooser();
private boolean exportFile(File inPngFile)
{
// Get the image file from the grouter
- MapSource source = MapSourceLibrary.getSource(_baseImageConfig.getSourceIndex());
- GroutedImage baseImage = MapGrouter.getMapImage(_app.getTrackInfo().getTrack(), source,
- _baseImageConfig.getZoomLevel());
+ ImageDefinition imageDef = _baseImagePanel.getImageDefinition();
+ MapSource source = MapSourceLibrary.getSource(imageDef.getSourceIndex());
+ MapGrouter grouter = _baseImagePanel.getGrouter();
+ GroutedImage baseImage = grouter.getMapImage(_app.getTrackInfo().getTrack(), source,
+ imageDef.getZoom());
if (baseImage == null || !baseImage.isValid())
{
_app.showErrorMessage(getNameKey(), "dialog.exportpov.cannotmakebaseimage");
// Work out x, y limits for drawing
DoubleRange xRange = inImage.getXRange();
DoubleRange yRange = inImage.getYRange();
- int zoomFactor = 1 << _baseImageConfig.getZoomLevel();
+ final int zoomFactor = 1 << _baseImagePanel.getImageDefinition().getZoom();
Graphics g = inImage.getImage().getGraphics();
- // TODO: Set colour, line width
+ // TODO: Set line width, style etc
g.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT));
// Loop over points
// draw from previous point to this one
g.drawLine(prevX, prevY, px, py);
}
- // draw this point
- g.drawRect(px-2, py-2, 3, 3);
+ // Only draw points if requested
+ if (_drawTrackPointsCheckbox.isSelected())
+ {
+ g.drawRect(px-2, py-2, 3, 3);
+ }
// save coordinates
prevX = px; prevY = py;
}
// Note: Differences from main map: No mapPosition (modifying position and visible points),
// no selection, no opacities, maybe different scale/text factors
}
+
+ /**
+ * Base image has changed, need to enable/disable ok button
+ */
+ public void baseImageChanged()
+ {
+ final boolean useImage = _baseImagePanel.getImageDefinition().getUseImage();
+ final int zoomLevel = _baseImagePanel.getImageDefinition().getZoom();
+ final boolean okEnabled = useImage && _baseImagePanel.getFoundData()
+ && MapGrouter.isZoomLevelOk(_app.getTrackInfo().getTrack(), zoomLevel);
+ _okButton.setEnabled(okEnabled);
+ }
}
package tim.prune.save;
-import tim.prune.config.Config;
import tim.prune.data.DoubleRange;
import tim.prune.data.Track;
import tim.prune.data.TrackExtents;
-import tim.prune.gui.map.DiskTileCacher;
import tim.prune.gui.map.MapSource;
+import tim.prune.gui.map.MapTileManager;
+import tim.prune.gui.map.TileConsumer;
import java.awt.Color;
import java.awt.Graphics;
* Class to handle the sticking together (grouting) of map tiles
* to create a single map image for the current track
*/
-public abstract class MapGrouter
+public class MapGrouter implements TileConsumer
{
/** The most recently produced image */
- private static GroutedImage _lastGroutedImage = null;
+ private GroutedImage _lastGroutedImage = null;
/**
* Clear the last image, it's not needed any more
*/
- public static void clearMapImage() {
+ public void clearMapImage() {
_lastGroutedImage = null;
}
* @param inZoom selected zoom level
* @return grouted image, or null if no image could be created
*/
- public static GroutedImage createMapImage(Track inTrack, MapSource inMapSource, int inZoom)
+ public GroutedImage createMapImage(Track inTrack, MapSource inMapSource, int inZoom)
+ {
+ return createMapImage(inTrack, inMapSource, inZoom, false);
+ }
+
+ /**
+ * Grout the required map tiles together according to the track's extent
+ * @param inTrack track object
+ * @param inMapSource map source to use (may have one or two layers)
+ * @param inZoom selected zoom level
+ * @param inDownload true to download tiles, false (by default) to just pull from disk
+ * @return grouted image, or null if no image could be created
+ */
+ public GroutedImage createMapImage(Track inTrack, MapSource inMapSource, int inZoom, boolean inDownload)
{
// Get the extents of the track including a standard (10%) border around the data
TrackExtents extents = new TrackExtents(inTrack);
DoubleRange xRange = extents.getXRange();
DoubleRange yRange = extents.getYRange();
- // Get path to disk cache
- final String cachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
// Work out which tiles are required
final int zoomFactor = 1 << inZoom;
final int minTileX = (int) (xRange.getMinimum() * zoomFactor);
// Work out how big the final image will be, create a BufferedImage
final int pixCount = (int) (extents.getXRange().getRange() * zoomFactor * 256);
- if (pixCount < 2) {return null;}
+ if (pixCount < 2 || inZoom == 0) {return null;}
BufferedImage resultImage = new BufferedImage(pixCount, pixCount, BufferedImage.TYPE_INT_RGB);
Graphics g = resultImage.getGraphics();
g.setColor(Color.WHITE);
// Work out where to start drawing the tiles on the image
int xOffset = (int) ((minTileX - xRange.getMinimum() * zoomFactor) * 256);
+ // Make a map tile manager to load (or download) the tiles
+ MapTileManager tileManager = new MapTileManager(this);
+ tileManager.setMapSource(inMapSource);
+ tileManager.enableTileDownloading(inDownload);
+ tileManager.setReturnIncompleteImages();
+ tileManager.setZoom(inZoom);
+
int numTilesUsed = 0;
int numTilesMissing = 0;
+
// Loop over the tiles
for (int x = minTileX; x <= maxTileX; x++)
{
{
for (int layer=0; layer < inMapSource.getNumLayers(); layer++)
{
- Image tile = DiskTileCacher.getTile(cachePath, inMapSource.makeFilePath(layer, inZoom, x, y), false);
+ Image tile = tileManager.getTile(layer, x, y, true);
+ // If we're downloading tiles, wait until the tile isn't null
+ int waitCount = 0;
+ while (tile == null && inDownload && waitCount < 3)
+ {
+ // System.out.println("wait " + waitCount + " for tile to be not null");
+ try {Thread.sleep(250);} catch (InterruptedException e) {}
+ tile = tileManager.getTile(layer, x, y, false); // don't request another download
+ waitCount++;
+ }
+ // See if there's a tile or not
if (tile != null)
{
// Wait until tile is available (loaded asynchronously)
- while (tile.getWidth(null) < 0) {
+ while (tile.getWidth(null) < 0 && !inDownload)
+ {
+ // System.out.println("Wait for tile width");
try {
- Thread.sleep(100);
+ Thread.sleep(inDownload ? 500 : 100);
}
catch (InterruptedException ie) {}
}
numTilesUsed++;
g.drawImage(tile, xOffset, yOffset, null);
}
- else numTilesMissing++;
+ else
+ {
+ // null tile, that means it's either not available or really slow to start downloading
+ numTilesMissing++;
+ }
}
yOffset += 256;
}
* @param inZoom selected zoom level
* @return grouted image, or null if no image could be created
*/
- public static GroutedImage getMapImage(Track inTrack, MapSource inMapSource, int inZoom)
+ public synchronized GroutedImage getMapImage(Track inTrack, MapSource inMapSource, int inZoom)
{
if (_lastGroutedImage == null) {
_lastGroutedImage = createMapImage(inTrack, inMapSource, inZoom);
final int pixCount = (int) (extents.getXRange().getRange() * zoomFactor * 256);
return pixCount > 2 && pixCount < 4000;
}
+
+ /** React to tiles being updated by the tile manager */
+ public void tilesUpdated(boolean inIsOk)
+ {
+ // Doesn't need any action
+ }
}
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
-import javax.swing.border.EtchedBorder;
import tim.prune.App;
-import tim.prune.DataSubscriber;
+import tim.prune.FunctionLibrary;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
import tim.prune.data.NumberUtils;
import tim.prune.data.Track;
import tim.prune.function.Export3dFunction;
+import tim.prune.function.srtm.LookupSrtmFunction;
+import tim.prune.gui.BaseImageDefinitionPanel;
import tim.prune.gui.DialogCloser;
+import tim.prune.gui.TerrainDefinitionPanel;
import tim.prune.gui.map.MapSource;
import tim.prune.gui.map.MapSourceLibrary;
import tim.prune.load.GenericFileFilter;
+import tim.prune.threedee.ImageDefinition;
+import tim.prune.threedee.TerrainHelper;
import tim.prune.threedee.ThreeDModel;
/**
* Class to export a 3d scene of the track to a specified Pov file
- * Note: Subscriber interface only used for callback from image config dialog
*/
-public class PovExporter extends Export3dFunction implements DataSubscriber
+public class PovExporter extends Export3dFunction
{
private Track _track = null;
private JDialog _dialog = null;
private JTextField _cameraXField = null, _cameraYField = null, _cameraZField = null;
private JTextField _fontName = null, _altitudeFactorField = null;
private JRadioButton _ballsAndSticksButton = null;
- private JLabel _baseImageLabel = null;
- private JButton _baseImageButton = null;
- private BaseImageConfigDialog _baseImageConfig = null;
+ /** Panel for defining the base image */
+ private BaseImageDefinitionPanel _baseImagePanel = null;
+ /** Component for defining the terrain */
+ private TerrainDefinitionPanel _terrainPanel = null;
// defaults
private static final double DEFAULT_CAMERA_DISTANCE = 30.0;
*/
public void begin()
{
- // Make dialog window to select angles, colours etc
+ // Make dialog window to select inputs
if (_dialog == null)
{
_dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
_dialog.setLocationRelativeTo(_parentFrame);
_dialog.getContentPane().add(makeDialogComponents());
}
- // Make base image dialog
- if (_baseImageConfig == null)
- {
- _baseImageConfig = new BaseImageConfigDialog(this, _dialog, _track);
+ // Get exaggeration factor from config
+ final int exaggFactor = Config.getConfigInt(Config.KEY_HEIGHT_EXAGGERATION);
+ if (exaggFactor > 0) {
+ _altFactor = exaggFactor / 100.0;
}
// Set angles
_cameraYField.setText(_cameraY);
_cameraZField.setText(_cameraZ);
_altitudeFactorField.setText("" + _altFactor);
- updateBaseImageDetails();
+ // Pass terrain and image def parameters (if any) to the panels
+ if (_terrainDef != null) {
+ _terrainPanel.initTerrainParameters(_terrainDef);
+ }
+ if (_imageDef != null) {
+ _baseImagePanel.initImageParameters(_imageDef);
+ }
+ _baseImagePanel.updateBaseImageDetails();
// Show dialog
_dialog.pack();
_dialog.setVisible(true);
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- doExport();
- MapGrouter.clearMapImage();
+ // Need to launch export in new thread
+ new Thread(new Runnable() {
+ public void run()
+ {
+ doExport();
+ _baseImagePanel.getGrouter().clearMapImage();
+ }
+ }).start();
_dialog.dispose();
}
});
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- MapGrouter.clearMapImage();
+ _baseImagePanel.getGrouter().clearMapImage();
_dialog.dispose();
}
});
group.add(_ballsAndSticksButton); group.add(tubesButton);
stylePanel.add(radioPanel);
- // Panel for the base image
- JPanel imagePanel = new JPanel();
- imagePanel.setLayout(new BorderLayout(10, 4));
- imagePanel.add(new JLabel(I18nManager.getText("dialog.exportpov.baseimage") + ": "), BorderLayout.WEST);
- _baseImageLabel = new JLabel("Typical sourcename");
- imagePanel.add(_baseImageLabel, BorderLayout.CENTER);
- _baseImageButton = new JButton(I18nManager.getText("button.edit"));
- _baseImageButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent event) {
- changeBaseImage();
- }
- });
- imagePanel.add(_baseImageButton, BorderLayout.EAST);
- // Put these image controls inside a holder panel with an outline
- JPanel imageHolderPanel = new JPanel();
- imageHolderPanel.setBorder(BorderFactory.createCompoundBorder(
- BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
- );
- imageHolderPanel.setLayout(new BorderLayout());
- imageHolderPanel.add(imagePanel, BorderLayout.NORTH);
+ // Panel for the base image (parent is null because we don't need callback)
+ _baseImagePanel = new BaseImageDefinitionPanel(null, _dialog, _track);
+ // Panel for the terrain definition
+ _terrainPanel = new TerrainDefinitionPanel();
// add these panels to the holder panel
JPanel holderPanel = new JPanel();
boxPanel.add(Box.createVerticalStrut(4));
boxPanel.add(stylePanel);
boxPanel.add(Box.createVerticalStrut(4));
- boxPanel.add(imageHolderPanel);
+ boxPanel.add(_terrainPanel);
+ boxPanel.add(Box.createVerticalStrut(4));
+ boxPanel.add(_baseImagePanel);
holderPanel.add(boxPanel, BorderLayout.CENTER);
panel.add(holderPanel, BorderLayout.CENTER);
return panel;
}
- /**
- * Change the base image by calling the BaseImageConfigDialog
- */
- private void changeBaseImage()
- {
- // Check if there is a cache to use
- if (BaseImageConfigDialog.isImagePossible())
- {
- // Show new dialog to choose image details
- _baseImageConfig.begin();
- }
- else {
- _app.showErrorMessage(getNameKey(), "dialog.exportimage.noimagepossible");
- }
- }
-
- /**
- * Update the description label according to the selected base image details
- */
- private void updateBaseImageDetails()
- {
- String desc = null;
- if (_baseImageConfig.useImage())
- {
- MapSource source = MapSourceLibrary.getSource(_baseImageConfig.getSourceIndex());
- if (source != null) {
- desc = source.getName() + " ("
- + _baseImageConfig.getZoomLevel() + ")";
- }
- }
- if (desc == null) {
- desc = I18nManager.getText("dialog.about.no");
- }
- _baseImageLabel.setText(desc);
- }
/**
* Select the file and export data to it
}
final int nameLen = povFile.getName().length() - 4;
final File imageFile = new File(povFile.getParentFile(), povFile.getName().substring(0, nameLen) + "_base.png");
- final boolean imageExists = _baseImageConfig.useImage() && imageFile.exists();
+ final File terrainFile = new File(povFile.getParentFile(), povFile.getName().substring(0, nameLen) + "_terrain.png");
+ final boolean imageExists = _baseImagePanel.getImageDefinition().getUseImage() && imageFile.exists();
+ final boolean terrainFileExists = _terrainPanel.getUseTerrain() && terrainFile.exists();
+
// Check if files exist and if necessary prompt for overwrite
Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
- if ((!povFile.exists() && !imageExists) || JOptionPane.showOptionDialog(_parentFrame,
+ if ((!povFile.exists() && !imageExists && !terrainFileExists)
+ || JOptionPane.showOptionDialog(_parentFrame,
I18nManager.getText("dialog.save.overwrite.text"),
I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
== JOptionPane.YES_OPTION)
{
- // Export the file
- if (exportFile(povFile, imageFile))
+ // Export the file(s)
+ if (exportFiles(povFile, imageFile, terrainFile))
{
// file saved - store directory in config for later
Config.setConfigString(Config.KEY_TRACK_DIR, povFile.getParentFile().getAbsolutePath());
+ // also store exaggeration
+ Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_altFactor * 100));
}
else
{
/**
- * Export the track data to the specified file
+ * Export the data to the specified file(s)
* @param inPovFile File object to save pov file to
* @param inImageFile file object to save image to
+ * @param inTerrainFile file object to save terrain to
* @return true if successful
*/
- private boolean exportFile(File inPovFile, File inImageFile)
+ private boolean exportFiles(File inPovFile, File inImageFile, File inTerrainFile)
{
FileWriter writer = null;
// find out the line separator for this system
try
{
// try to use given altitude cap
- double altFactor = Double.parseDouble(_altitudeFactorField.getText());
- model.setAltitudeFactor(altFactor);
+ double givenFactor = Double.parseDouble(_altitudeFactorField.getText());
+ if (givenFactor > 0.0) _altFactor = givenFactor;
}
catch (NumberFormatException nfe) { // parse failed, reset
- _altitudeFactorField.setText("1.0");
+ _altitudeFactorField.setText("" + _altFactor);
}
- model.scale();
+ model.setAltitudeFactor(_altFactor);
- boolean useImage = _baseImageConfig.useImage();
+ // Write base image if necessary
+ ImageDefinition imageDef = _baseImagePanel.getImageDefinition();
+ boolean useImage = imageDef.getUseImage();
if (useImage)
{
// Get base image from grouter
- MapSource mapSource = MapSourceLibrary.getSource(_baseImageConfig.getSourceIndex());
- GroutedImage baseImage = MapGrouter.getMapImage(_track, mapSource, _baseImageConfig.getZoomLevel());
+ MapSource mapSource = MapSourceLibrary.getSource(imageDef.getSourceIndex());
+ MapGrouter grouter = _baseImagePanel.getGrouter();
+ GroutedImage baseImage = grouter.getMapImage(_track, mapSource, imageDef.getZoom());
try
{
useImage = ImageIO.write(baseImage.getImage(), "png", inImageFile);
}
}
+ boolean useTerrain = _terrainPanel.getUseTerrain();
+ if (useTerrain)
+ {
+ TerrainHelper terrainHelper = new TerrainHelper(_terrainPanel.getGridSize());
+ Track terrainTrack = terrainHelper.createGridTrack(_track);
+ // Get the altitudes from SRTM for all the points in the track
+ LookupSrtmFunction srtmLookup = (LookupSrtmFunction) FunctionLibrary.FUNCTION_LOOKUP_SRTM;
+ srtmLookup.begin(terrainTrack);
+ while (srtmLookup.isRunning())
+ {
+ try {
+ Thread.sleep(750); // just polling in a wait loop isn't ideal but simple
+ }
+ catch (InterruptedException e) {}
+ }
+ // Fix the voids
+ terrainHelper.fixVoids(terrainTrack);
+
+ model.setTerrain(terrainTrack);
+ model.scale();
+
+ // Call TerrainHelper to write out the data from the model
+ terrainHelper.writeHeightMap(model, inTerrainFile);
+ }
+ else
+ {
+ // No terrain required, so just scale the model as it is
+ model.scale();
+ }
+
// Create file and write basics
writer = new FileWriter(inPovFile);
- writeStartOfFile(writer, lineSeparator, useImage ? inImageFile : null);
+ writeStartOfFile(writer, lineSeparator, useImage ? inImageFile : null, useTerrain ? inTerrainFile : null);
// write out points
if (_ballsAndSticksButton.isSelected()) {
* @param inWriter Writer to use for writing file
* @param inLineSeparator line separator to use
* @param inImageFile image file to reference (or null if none)
+ * @param inTerrainFile terrain file to reference (or null if none)
* @throws IOException on file writing error
*/
- private void writeStartOfFile(FileWriter inWriter, String inLineSeparator, File inImageFile)
+ private void writeStartOfFile(FileWriter inWriter, String inLineSeparator, File inImageFile, File inTerrainFile)
throws IOException
{
- inWriter.write("// Pov file produced by GpsPrune - see http://activityworkshop.net/");
+ inWriter.write("// Pov file produced by GpsPrune - see http://gpsprune.activityworkshop.net/");
+ inWriter.write(inLineSeparator);
+ inWriter.write("#version 3.6;");
inWriter.write(inLineSeparator);
inWriter.write(inLineSeparator);
// Select font based on user input
// Make the definition of the base plane depending on whether there's an image or not
final boolean useImage = (inImageFile != null);
- final String boxDefinition = (inImageFile == null ?
- " <-10.0, -0.15, -10.0>," + inLineSeparator
- + " <10.0, 0.15, 10.0>" + inLineSeparator
- + " pigment { color rgb <0.5 0.75 0.8> }"
- :
+ final boolean useImageOnBox = useImage && (inTerrainFile == null);
+ final String boxDefinition = (useImageOnBox ?
" <0, 0, 0>, <1, 1, 0.001>" + inLineSeparator
+ " pigment {image_map { png \"" + inImageFile.getName() + "\" map_type 0 interpolate 2 once } }" + inLineSeparator
+ " scale 20.0 rotate <90, 0, 0>" + inLineSeparator
- + " translate <-10.0, 0, -10.0>");
+ + " translate <-10.0, 0, -10.0>"
+ : " <-10.0, -0.15, -10.0>," + inLineSeparator
+ + " <10.0, 0.0, 10.0>" + inLineSeparator
+ + " pigment { color rgb <0.5 0.75 0.8> }");
// TODO: Maybe could use the same geometry for the imageless case, would simplify code a bit
+ // Definition of terrain shape if any
+ final String terrainDefinition = makeTerrainString(inTerrainFile, inImageFile, inLineSeparator);
+
// Set up output
String[] outputLines = {
"global_settings { ambient_light rgb <4, 4, 4> }", "",
"box {",
boxDefinition,
"}", "",
+ // terrain
+ terrainDefinition,
// write cardinals
"// Cardinal letters N,S,E,W",
"text {",
}
}
+ /**
+ * Make a description of the height_field object for the terrain, depending on terrain and image
+ * @param inTerrainFile terrain file, or null if none
+ * @param inImageFile image file, or null if none
+ * @param inLineSeparator line separator
+ * @return String for inserting into pov file
+ */
+ private static String makeTerrainString(File inTerrainFile, File inImageFile, String inLineSeparator)
+ {
+ if (inTerrainFile == null) {return "";}
+ StringBuilder sb = new StringBuilder();
+ sb.append("//Terrain").append(inLineSeparator)
+ .append("height_field {").append(inLineSeparator)
+ .append("\tpng \"").append(inTerrainFile.getName()).append("\" smooth").append(inLineSeparator)
+ .append("\tfinish {diffuse 0.7 phong 0.2}").append(inLineSeparator);
+ if (inImageFile != null) {
+ sb.append("\tpigment {image_map { png \"").append(inImageFile.getName()).append("\" } rotate x*90}").append(inLineSeparator);
+ }
+ else {
+ sb.append("\tpigment {color rgb <0.55 0.7 0.55> }").append(inLineSeparator);
+ }
+ sb.append("\tscale 20.0").append(inLineSeparator)
+ .append("\ttranslate <-10.0, 0, -10.0>").append(inLineSeparator).append("}");
+ return sb.toString();
+ }
/**
* Write out all the data points to the file in the balls-and-sticks style
}
return segmentList;
}
-
- /**
- * Callback from base image config dialog
- */
- public void dataUpdated(byte inUpdateType)
- {
- updateBaseImageDetails();
- }
-
- /** Not required */
- public void actionCompleted(String inMessage) {
- }
}
*/
public void begin()
{
- // Make dialog window to select angles, colours etc
+ // Make dialog window to select input parameters
if (_dialog == null)
{
_dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
_dialog.setLocationRelativeTo(_parentFrame);
_dialog.getContentPane().add(makeDialogComponents());
}
+ // Get exaggeration factor from config
+ final int exaggFactor = Config.getConfigInt(Config.KEY_HEIGHT_EXAGGERATION);
+ if (exaggFactor > 0) {
+ _altFactor = exaggFactor / 100.0;
+ }
// Set angles
NumberFormat threeDP = NumberFormat.getNumberInstance();
{
// file saved - store directory in config for later
Config.setConfigString(Config.KEY_TRACK_DIR, file.getParentFile().getAbsolutePath());
+ // also store exaggeration
+ Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_altFactor * 100));
}
else {
// export failed so need to choose again
--- /dev/null
+package tim.prune.threedee;
+
+/**
+ * Holds the definition of the image to use
+ * (whether or not to use an image, and the source index and zoom)
+ */
+public class ImageDefinition
+{
+ private boolean _useImage = false;
+ private int _sourceIndex = 0;
+ private int _zoom = 0;
+
+
+ /**
+ * Empty constructor specifying no image
+ */
+ public ImageDefinition()
+ {
+ this(false, 0, 0);
+ }
+
+ /**
+ * Constructor
+ * @param inUse true to use an image
+ * @param inSourceIndex index of map source
+ * @param inZoom zoom level
+ */
+ public ImageDefinition(boolean inUse, int inSourceIndex, int inZoom)
+ {
+ setUseImage(inUse, inSourceIndex, inZoom);
+ }
+
+ /**
+ * Set the parameters
+ * @param inUse true to use an image
+ * @param inSourceIndex index of map source
+ * @param inZoom zoom level
+ */
+ public void setUseImage(boolean inUse, int inSourceIndex, int inZoom)
+ {
+ _useImage = inUse;
+ _sourceIndex = inSourceIndex;
+ _zoom = inZoom;
+ }
+
+ /**
+ * @return true if image should be used, false otherwise
+ */
+ public boolean getUseImage() {
+ return _useImage && _sourceIndex >= 0 && _zoom > 2;
+ }
+
+ /**
+ * @return source index
+ */
+ public int getSourceIndex() {
+ return _sourceIndex;
+ }
+
+ /**
+ * @return zoom level
+ */
+ public int getZoom() {
+ return _zoom;
+ }
+}
package tim.prune.threedee;
-import java.awt.FlowLayout;
import java.awt.BorderLayout;
+import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.Font3D;
import javax.media.j3d.FontExtrusion;
+import javax.media.j3d.GeometryArray;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.Group;
import javax.media.j3d.Material;
import javax.media.j3d.PointLight;
+import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Text3D;
+import javax.media.j3d.Texture;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
+import javax.media.j3d.TriangleStripArray;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.vecmath.Matrix3d;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
+import javax.vecmath.TexCoord2f;
import javax.vecmath.Vector3d;
+import tim.prune.FunctionLibrary;
+import tim.prune.I18nManager;
+import tim.prune.data.Track;
+import tim.prune.function.Export3dFunction;
+import tim.prune.function.srtm.LookupSrtmFunction;
+import tim.prune.gui.map.MapSourceLibrary;
+import tim.prune.save.GroutedImage;
+import tim.prune.save.MapGrouter;
+
import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.geometry.Cylinder;
import com.sun.j3d.utils.geometry.Sphere;
+import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.universe.SimpleUniverse;
-import tim.prune.FunctionLibrary;
-import tim.prune.I18nManager;
-import tim.prune.data.Track;
-import tim.prune.function.Export3dFunction;
-
/**
* Class to hold main window for java3d view of data
private JFrame _frame = null;
private ThreeDModel _model = null;
private OrbitBehavior _orbit = null;
- private double _altFactor = 5.0;
+ private double _altFactor = -1.0;
+ private ImageDefinition _imageDefinition = null;
+ private GroutedImage _baseImage = null;
+ private TerrainDefinition _terrainDefinition = null;
/** only prompt about big track size once */
private static boolean TRACK_SIZE_WARNING_GIVEN = false;
_track = inTrack;
}
+ /**
+ * @param inFactor altitude factor to use
+ */
+ public void setAltitudeFactor(double inFactor)
+ {
+ _altFactor = inFactor;
+ }
+
+ /**
+ * Set the parameters for the base image and do the grouting already
+ * (setTrack should already be called by now)
+ */
+ public void setBaseImageParameters(ImageDefinition inDefinition)
+ {
+ _imageDefinition = inDefinition;
+ if (inDefinition != null && inDefinition.getUseImage())
+ {
+ _baseImage = new MapGrouter().createMapImage(_track, MapSourceLibrary.getSource(inDefinition.getSourceIndex()),
+ inDefinition.getZoom());
+ }
+ else _baseImage = null;
+ }
+
+ /**
+ * Set the terrain parameters
+ */
+ public void setTerrainParameters(TerrainDefinition inDefinition)
+ {
+ _terrainDefinition = inDefinition;
+ }
/**
* Show the window
*/
public void show() throws ThreeDException
{
- // Get the altitude exaggeration to use
- Object factorString = JOptionPane.showInputDialog(_parentFrame,
- I18nManager.getText("dialog.3d.altitudefactor"),
- I18nManager.getText("dialog.3d.title"),
- JOptionPane.QUESTION_MESSAGE, null, null, _altFactor);
- if (factorString == null) return;
- try {
- _altFactor = Double.parseDouble(factorString.toString());
- }
- catch (Exception e) {} // Ignore parse errors
- if (_altFactor < 1.0) {_altFactor = 1.0;}
+ // Make sure altitude exaggeration is positive
+ if (_altFactor < 0.0) {_altFactor = 1.0;}
// Set up the graphics config
GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
plane = new Box(10f, 0.04f, 10f, planeAppearance);
objTrans.addChild(plane);
+ // Image on top of base plane, if specified
+ final boolean showTerrain = _terrainDefinition != null && _terrainDefinition.getUseTerrain();
+ if (_baseImage != null && !showTerrain)
+ {
+ QuadArray baseSquare = new QuadArray (4, QuadArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2);
+ baseSquare.setCoordinate(0, new Point3f(-10f, 0.05f, -10f));
+ baseSquare.setCoordinate(1, new Point3f(-10f, 0.05f, 10f));
+ baseSquare.setCoordinate(2, new Point3f( 10f, 0.05f, 10f));
+ baseSquare.setCoordinate(3, new Point3f( 10f, 0.05f, -10f));
+ // and set anchor points for the texture
+ baseSquare.setTextureCoordinate(0, 0, new TexCoord2f(0.0f, 1.0f));
+ baseSquare.setTextureCoordinate(0, 1, new TexCoord2f(0.0f, 0.0f));
+ baseSquare.setTextureCoordinate(0, 2, new TexCoord2f(1.0f, 0.0f));
+ baseSquare.setTextureCoordinate(0, 3, new TexCoord2f(1.0f, 1.0f));
+ // Set appearance including image
+ Appearance baseAppearance = new Appearance();
+ Texture mapImage = new TextureLoader(_baseImage.getImage(), _frame).getTexture();
+ baseAppearance.setTexture(mapImage);
+ objTrans.addChild(new Shape3D(baseSquare, baseAppearance));
+ }
+
+ // Create model containing track information
+ _model = new ThreeDModel(_track);
+ _model.setAltitudeFactor(_altFactor);
+
+ if (showTerrain)
+ {
+ // TODO: Is it maybe possible to cache the last terrainTrack?
+ // (if the dataTrack and the resolution haven't changed)
+ // Construct the terrain track according to these extents and the grid size
+ TerrainHelper terrainHelper = new TerrainHelper(_terrainDefinition.getGridSize());
+ Track terrainTrack = terrainHelper.createGridTrack(_track);
+ // Get the altitudes from SRTM for all the points in the track
+ LookupSrtmFunction srtmLookup = (LookupSrtmFunction) FunctionLibrary.FUNCTION_LOOKUP_SRTM;
+ srtmLookup.begin(terrainTrack);
+ while (srtmLookup.isRunning())
+ {
+ try {
+ Thread.sleep(750); // just polling in a wait loop isn't ideal but simple
+ }
+ catch (InterruptedException e) {}
+ }
+
+ // Fix the voids
+ terrainHelper.fixVoids(terrainTrack);
+
+ // Give the terrain definition to the _model as well
+ _model.setTerrain(terrainTrack);
+ _model.scale();
+
+ objTrans.addChild(createTerrain(_model, terrainHelper, _baseImage));
+ }
+ else
+ {
+ // No terrain, so just scale the model as it is
+ _model.scale();
+ }
+
// N, S, E, W
GeneralPath bevelPath = new GeneralPath();
bevelPath.moveTo(0.0f, 0.0f);
objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.w"), new Point3f(-11f, 0f, 0f), compassFont));
objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.e"), new Point3f(10f, 0f, 0f), compassFont));
- // create and scale model
- _model = new ThreeDModel(_track);
- _model.setAltitudeFactor(_altFactor);
- _model.scale();
-
// Add points to model
objTrans.addChild(createDataPoints(_model));
}
- /** @return track point object */
+ /**
+ * @return track point object
+ */
private static Group createTrackpoint(Point3d inPointPos, byte inHeightCode)
{
Material mat = getTrackpointMaterial(inHeightCode);
}
- /** @return Material object for track points with the appropriate colour for the height */
+ /**
+ * @return Material object for track points with the appropriate colour for the height
+ */
private static Material getTrackpointMaterial(byte inHeightCode)
{
// create default material
return group;
}
+ /**
+ * Create a java3d Shape for the terrain
+ * @param inModel threedModel
+ * @param inHelper terrain helper
+ * @param inBaseImage base image for shape, or null for no image
+ * @return Shape3D object
+ */
+ private static Shape3D createTerrain(ThreeDModel inModel, TerrainHelper inHelper, GroutedImage inBaseImage)
+ {
+ final int numNodes = inHelper.getGridSize();
+ final int RESULT_SIZE = numNodes * (numNodes * 2 - 2);
+ final int GEOMETRY_COLOURING_TYPE = (inBaseImage == null ? GeometryArray.COLOR_3 : GeometryArray.TEXTURE_COORDINATE_2);
+
+ int[] stripData = inHelper.getStripLengths();
+ TriangleStripArray tsa = new TriangleStripArray(RESULT_SIZE, GeometryArray.COORDINATES | GEOMETRY_COLOURING_TYPE,
+ stripData);
+ // Get the scaled terrainTrack coordinates (or just heights) from the model
+ final int nSquared = numNodes * numNodes;
+ Point3d[] rawPoints = new Point3d[nSquared];
+ for (int i=0; i<nSquared; i++)
+ {
+ double height = inModel.getScaledTerrainValue(i) * MODEL_SCALE_FACTOR;
+ rawPoints[i] = new Point3d(inModel.getScaledTerrainHorizValue(i) * MODEL_SCALE_FACTOR,
+ Math.max(height, 0.05), // make sure it's above the box
+ -inModel.getScaledTerrainVertValue(i) * MODEL_SCALE_FACTOR);
+ }
+ tsa.setCoordinates(0, inHelper.getTerrainCoordinates(rawPoints));
+
+ Appearance tAppearance = new Appearance();
+ if (inBaseImage != null)
+ {
+ tsa.setTextureCoordinates(0, 0, inHelper.getTextureCoordinates());
+ Texture mapImage = new TextureLoader(inBaseImage.getImage()).getTexture();
+ tAppearance.setTexture(mapImage);
+ }
+ else
+ {
+ Color3f[] colours = new Color3f[RESULT_SIZE];
+ Color3f terrainColour = new Color3f(0.1f, 0.2f, 0.2f);
+ for (int i=0; i<RESULT_SIZE; i++) {colours[i] = terrainColour;}
+ tsa.setColors(0, colours);
+ }
+ return new Shape3D(tsa, tAppearance);
+ }
/**
* Calculate the angles and call them back to the app
Point3d result = new Point3d();
secondTran.transform(point, result);
firstTran.transform(result);
- // Callback settings to pov export function
+
+ // Give the settings to the rendering function
inFunction.setCameraCoordinates(result.x, result.y, result.z);
inFunction.setAltitudeExaggeration(_altFactor);
+ inFunction.setTerrainDefinition(_terrainDefinition); // ignored by svg, used by pov
+ inFunction.setImageDefinition(_imageDefinition); // ignored by svg, used by pov
+
inFunction.begin();
}
}
--- /dev/null
+package tim.prune.threedee;
+
+/**
+ * Holds the definition of the terrain to use
+ * (whether or not to use a terrain, and the resolution)
+ */
+public class TerrainDefinition
+{
+ private boolean _useTerrain = false;
+ private int _gridSize = 0;
+
+ /**
+ * Empty constructor specifying no terrain
+ */
+ public TerrainDefinition()
+ {
+ this(false, 0);
+ }
+
+ /**
+ * Constructor
+ * @param inUse true to use a terrain
+ * @param inGridSize size of grid
+ */
+ public TerrainDefinition(boolean inUse, int inGridSize)
+ {
+ setUseTerrain(inUse, inGridSize);
+ }
+
+ /**
+ * Set the parameters
+ * @param inUse true to use a terrain
+ * @param inGridSize size of grid
+ */
+ public void setUseTerrain(boolean inUse, int inGridSize)
+ {
+ _useTerrain = inUse;
+ _gridSize = inGridSize;
+ }
+
+ /**
+ * @return true if terrain should be used, false otherwise
+ */
+ public boolean getUseTerrain() {
+ return _useTerrain && _gridSize > 2;
+ }
+
+ /**
+ * @return grid size
+ */
+ public int getGridSize() {
+ return _gridSize;
+ }
+}
--- /dev/null
+package tim.prune.threedee;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.vecmath.Point3d;
+import javax.vecmath.TexCoord2f;
+
+import tim.prune.data.Altitude;
+import tim.prune.data.Coordinate;
+import tim.prune.data.DataPoint;
+import tim.prune.data.DoubleRange;
+import tim.prune.data.Field;
+import tim.prune.data.FieldList;
+import tim.prune.data.Latitude;
+import tim.prune.data.Longitude;
+import tim.prune.data.Track;
+import tim.prune.data.TrackExtents;
+import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.map.MapUtils;
+
+/**
+ * Helper for generating the arrays needed for the 3d terrain
+ */
+public class TerrainHelper
+{
+ /** Number of nodes on each side of the square grid */
+ private int _gridSize = 0;
+
+ /**
+ * Constructor
+ * @param inGridSize grid size
+ */
+ public TerrainHelper(int inGridSize) {
+ _gridSize = inGridSize;
+ }
+
+ /**
+ * @return grid size
+ */
+ public int getGridSize() {
+ return _gridSize;
+ }
+
+
+ /**
+ * Convert the terrain coordinates from raw form to TriangleStripArray form
+ * (with repeated nodes)
+ * @param inRawPoints array of raw points as formed from the track
+ * @return point coordinates as array
+ */
+ public Point3d[] getTerrainCoordinates(Point3d[] inRawPoints)
+ {
+ final int numNodes = _gridSize * _gridSize;
+ if (_gridSize <= 1 || inRawPoints == null || inRawPoints.length != numNodes) {return null;}
+ // Put these nodes into a new result array (repeating nodes as necessary)
+ final int resultSize = _gridSize * (_gridSize * 2 - 2);
+ Point3d[] result = new Point3d[resultSize];
+ final int numStrips = _gridSize - 1;
+ int resultIndex = 0;
+ for (int strip=0; strip<numStrips; strip++)
+ {
+ for (int col=0; col<_gridSize; col++)
+ {
+ int bottomNodeIndex = strip * _gridSize + col;
+ int topNodeIndex = bottomNodeIndex + _gridSize;
+ result[resultIndex++] = inRawPoints[bottomNodeIndex];
+ result[resultIndex++] = inRawPoints[topNodeIndex];
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Get the texture coordinates as an array
+ * @return texture coordinates as array
+ */
+ public TexCoord2f[] getTextureCoordinates()
+ {
+ if (_gridSize <= 1) {return null;}
+ final int numNodes = _gridSize * _gridSize;
+ final float gridStep = 1.0f / (_gridSize - 1);
+ // Build all the required nodes
+ TexCoord2f[] nodes = new TexCoord2f[numNodes];
+ for (int i=0; i<_gridSize; i++)
+ {
+ for (int j=0; j<_gridSize; j++)
+ {
+ nodes[j * _gridSize + i] = new TexCoord2f(gridStep * i, 1.0f - gridStep * j);
+ }
+ }
+ // Now put these nodes into a new result array (repeating nodes as necessary)
+ final int resultSize = _gridSize * (_gridSize * 2 - 2);
+ TexCoord2f[] result = new TexCoord2f[resultSize];
+ final int numStrips = _gridSize - 1;
+ int resultIndex = 0;
+ for (int strip=0; strip<numStrips; strip++)
+ {
+ for (int col=0; col<_gridSize; col++)
+ {
+ int bottomNodeIndex = strip * _gridSize + col;
+ int topNodeIndex = bottomNodeIndex + _gridSize;
+ result[resultIndex++] = nodes[bottomNodeIndex];
+ result[resultIndex++] = nodes[topNodeIndex];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return strip lengths as array
+ */
+ public int[] getStripLengths()
+ {
+ final int numStrips = _gridSize - 1;
+ final int nodesPerStrip = _gridSize * 2;
+ int[] result = new int[numStrips];
+ for (int i=0; i<numStrips; i++) {
+ result[i] = nodesPerStrip;
+ }
+ return result;
+ }
+
+ /**
+ * Create a grid of points in a new Track
+ * @param inDataTrack track from which the extents should be obtained
+ * @return Track containing all the points in the grid
+ */
+ public Track createGridTrack(Track inDataTrack)
+ {
+ // Work out the size of the current track
+ TrackExtents extents = new TrackExtents(inDataTrack);
+ extents.applySquareBorder();
+ DoubleRange xRange = extents.getXRange();
+ DoubleRange yRange = extents.getYRange();
+ // Create the array of points
+ final int numPoints = _gridSize * _gridSize;
+ final double xStep = xRange.getRange() / (_gridSize - 1);
+ final double yStep = yRange.getRange() / (_gridSize - 1);
+ DataPoint[] points = new DataPoint[numPoints];
+ for (int i=0; i<_gridSize; i++)
+ {
+ double pY = yRange.getMinimum() + i * yStep;
+ for (int j=0; j<_gridSize; j++)
+ {
+ // Create a new point with the appropriate lat and long, with no altitude
+ double pX = xRange.getMinimum() + j * xStep;
+ DataPoint point = new DataPoint(
+ new Latitude(MapUtils.getLatitudeFromY(pY), Coordinate.FORMAT_NONE),
+ new Longitude(MapUtils.getLongitudeFromX(pX), Coordinate.FORMAT_NONE),
+ null);
+ //System.out.println("Created point at " + point.getLatitude().output(Coordinate.FORMAT_DEG_MIN_SEC)
+ // + ", " + point.getLongitude().output(Coordinate.FORMAT_DEG_MIN_SEC));
+ points[i * _gridSize + j] = point;
+ }
+ }
+ // Put these into a new track
+ Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE};
+ Track grid = new Track(new FieldList(fields), points);
+ return grid;
+ }
+
+ /**
+ * Write the given terrain track out to an indexed png file
+ * @param inModel three-d data model with terrain
+ * @param inPngFile file to write to
+ */
+ public void writeHeightMap(ThreeDModel inModel, File inPngFile)
+ {
+ BufferedImage image = new BufferedImage(_gridSize, _gridSize, BufferedImage.TYPE_BYTE_INDEXED);
+ for (int y=0; y<_gridSize; y++)
+ {
+ for (int x=0; x<_gridSize; x++)
+ {
+ double heightValue = inModel.getScaledTerrainValue(y * _gridSize + x) * 256;
+ // Need to ask colour model what rgb to use for this index (a little round-the-houses)
+ image.setRGB(x, y, image.getColorModel().getRGB((int) heightValue));
+ }
+ }
+ try
+ {
+ ImageIO.write(image, "PNG", inPngFile);
+ }
+ catch (IOException ioe) {System.err.println(ioe.getClass().getName() + " - " + ioe.getMessage());}
+ }
+
+
+ /**
+ * Try to fix the voids in the given terrain track by averaging neighbour values where possible
+ * @param inTerrainTrack terrain track to fix
+ */
+ public void fixVoids(Track inTerrainTrack)
+ {
+ int numVoids = countVoids(inTerrainTrack);
+ if (numVoids == 0) {return;}
+ // System.out.println("Starting to fix, num voids = " + numVoids);
+ // Fix the holes which are surrounded on all four sides by non-holes
+ fixSingleHoles(inTerrainTrack);
+ // System.out.println("Fixed single holes, now num voids = " + countVoids(inTerrainTrack));
+ // Maybe there is something to do in the corners?
+ fixCorners(inTerrainTrack);
+ // Now fix the bigger holes, which should fix everything left
+ fixBiggerHoles(inTerrainTrack);
+ final int numHolesLeft = countVoids(inTerrainTrack);
+ if (numHolesLeft > 0) {
+ System.out.println("Fixed bigger holes, now num voids = " + countVoids(inTerrainTrack));
+ }
+ }
+
+ /**
+ * @param inTerrainTrack terrain track
+ * @return number of voids (points without altitudes)
+ */
+ private static int countVoids(Track inTerrainTrack)
+ {
+ int numVoids = 0;
+ if (inTerrainTrack != null)
+ {
+ for (int i=0; i<inTerrainTrack.getNumPoints(); i++) {
+ if (!inTerrainTrack.getPoint(i).hasAltitude()) {
+ numVoids++;
+ }
+ }
+ }
+ return numVoids;
+ }
+
+ /**
+ * Just deal with single holes surrounded by at least four direct neighbours
+ * @param inTerrainTrack terrain track to fix
+ */
+ private void fixSingleHoles(Track inTerrainTrack)
+ {
+ // Holes with neighbours in all directions
+ final int startIndex = 1, endIndex = _gridSize - 2;
+ for (int x = startIndex; x <= endIndex; x++)
+ {
+ for (int y = startIndex; y <= endIndex; y++)
+ {
+ int pIndex = x * _gridSize + y;
+ // Get the point and its neighbours
+ final DataPoint p = inTerrainTrack.getPoint(pIndex);
+ if (!p.hasAltitude())
+ {
+ final DataPoint pl = inTerrainTrack.getPoint(pIndex - 1);
+ final DataPoint pr = inTerrainTrack.getPoint(pIndex + 1);
+ final DataPoint pu = inTerrainTrack.getPoint(pIndex + _gridSize);
+ final DataPoint pd = inTerrainTrack.getPoint(pIndex - _gridSize);
+ // Check if the points are null??
+ if (pl == null || pr == null || pu == null || pd == null)
+ {
+ System.err.println("Woah. Got a null point in fixSingleHoles. x=" + x + ", y=" + y + ", grid=" + _gridSize);
+ System.err.println("index=" + pIndex);
+ if (pl == null) System.err.println("pl is null");
+ if (pr == null) System.err.println("pr is null");
+ if (pu == null) System.err.println("pu is null");
+ if (pd == null) System.err.println("pd is null");
+ continue;
+ }
+ // Check that all the neighbours have altitudes
+ if (pl.hasAltitude() && pr.hasAltitude() && pu.hasAltitude() && pd.hasAltitude())
+ {
+ // Now check the double-neighbours
+ final DataPoint pll = inTerrainTrack.getPoint(pIndex - 2);
+ final DataPoint prr = inTerrainTrack.getPoint(pIndex + 2);
+ final DataPoint puu = inTerrainTrack.getPoint(pIndex + 2 * _gridSize);
+ final DataPoint pdd = inTerrainTrack.getPoint(pIndex - 2 * _gridSize);
+
+ double altitude = 0.0;
+ if (pll != null && pll.hasAltitude() && prr != null && prr.hasAltitude()
+ && puu != null && puu.hasAltitude() && pdd != null & pdd.hasAltitude())
+ {
+ // Use the double-neighbours too to take into account the gradients
+ altitude = (
+ pl.getAltitude().getMetricValue() * 1.5
+ - pll.getAltitude().getMetricValue() * 0.5
+ + pr.getAltitude().getMetricValue() * 1.5
+ - prr.getAltitude().getMetricValue() * 0.5
+ + pd.getAltitude().getMetricValue() * 1.5
+ - pdd.getAltitude().getMetricValue() * 0.5
+ + pu.getAltitude().getMetricValue() * 1.5
+ - puu.getAltitude().getMetricValue() * 0.5) / 4.0;
+ }
+ else
+ {
+ // no double-neighbours, just use neighbours
+ altitude = (
+ pl.getAltitude().getMetricValue()
+ + pr.getAltitude().getMetricValue()
+ + pd.getAltitude().getMetricValue()
+ + pu.getAltitude().getMetricValue()) / 4.0;
+ }
+ // Set this altitude in the point
+ p.setFieldValue(Field.ALTITUDE, "" + altitude, false);
+ // force value to metres
+ p.getAltitude().reset(new Altitude((int) altitude, UnitSetLibrary.UNITS_METRES));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Try to fix the corners, if they're blank
+ * @param inTerrainTrack terrain track
+ */
+ private void fixCorners(Track inTerrainTrack)
+ {
+ fixCorner(inTerrainTrack, 0, 1, 1);
+ fixCorner(inTerrainTrack, _gridSize-1, -1, 1);
+ fixCorner(inTerrainTrack, (_gridSize-1)*_gridSize, 1, -1);
+ fixCorner(inTerrainTrack, _gridSize*_gridSize-1, -1, -1);
+ }
+
+ /**
+ * Fix a single corner by searching along adjacent edges and averaging the nearest neighbours
+ * @param inTerrainTrack terrain track
+ * @param inCornerIndex index of corner to fill
+ * @param inXinc increment in x direction (+1 or -1)
+ * @param inYinc increment in y direction (+1 or -1)
+ */
+ private void fixCorner(Track inTerrainTrack, int inCornerIndex, int inXinc, int inYinc)
+ {
+ DataPoint corner = inTerrainTrack.getPoint(inCornerIndex);
+ if (corner == null || corner.hasAltitude()) {return;}
+ // Corner hasn't got an altitude, we'll have to look for it
+ int sIndex1 = inCornerIndex, sIndex2 = inCornerIndex;
+ Altitude alt1 = null, alt2 = null;
+
+ for (int i=1; i<_gridSize && !corner.hasAltitude(); i++)
+ {
+ sIndex1 += inXinc;
+ sIndex2 += (inYinc * _gridSize);
+ // System.out.println("To fill corner " + inCornerIndex + ", looking at indexes " + sIndex1 + " and " + sIndex2);
+ if (alt1 == null)
+ {
+ DataPoint source1 = inTerrainTrack.getPoint(sIndex1);
+ if (source1 != null && source1.hasAltitude()) {alt1 = source1.getAltitude();}
+ }
+ if (alt2 == null)
+ {
+ DataPoint source2 = inTerrainTrack.getPoint(sIndex2);
+ if (source2 != null && source2.hasAltitude()) {alt2 = source2.getAltitude();}
+ }
+ // Can we average these?
+ if (alt1 != null && alt2 != null)
+ {
+ // System.out.println("Averaging values " + alt1.getMetricValue() + " and " + alt2.getMetricValue());
+ int newAltitude = (int) ((alt1.getMetricValue() + alt2.getMetricValue()) / 2.0);
+ corner.setFieldValue(Field.ALTITUDE, "" + newAltitude, false);
+ }
+ }
+ }
+
+ /**
+ * Try to fix bigger holes by interpolating between neighbours
+ * @param inTerrainTrack terrain track
+ */
+ private void fixBiggerHoles(Track inTerrainTrack)
+ {
+ double[] altitudes = new double[inTerrainTrack.getNumPoints()];
+ for (int i=0; i<_gridSize; i++)
+ {
+ int prevHoriz = -1, prevVert = -1;
+ for (int j=0; j<_gridSize; j++)
+ {
+ if (inTerrainTrack.getPoint(i * _gridSize + j).hasAltitude())
+ {
+ if (prevHoriz > -1 && prevHoriz != (j-1))
+ {
+// System.out.println("Found a gap for y=" + i +" between x=" + prevHoriz + " and " + j + " (" + (j-prevHoriz-1) + ")");
+ double startVal = inTerrainTrack.getPoint(i * _gridSize + prevHoriz).getAltitude().getMetricValue();
+ double endVal = inTerrainTrack.getPoint(i * _gridSize + j).getAltitude().getMetricValue();
+ for (int k=prevHoriz + 1; k< j; k++)
+ {
+ double val = startVal + (k-prevHoriz) * (endVal-startVal) / (j-prevHoriz);
+ if (altitudes[i * _gridSize + k] > 0.0) {
+ altitudes[i * _gridSize + k] = (altitudes[i * _gridSize + k] + val) / 2.0;
+ }
+ else {
+ altitudes[i * _gridSize + k] = val;
+ }
+ }
+ }
+ prevHoriz = j;
+ }
+ if (inTerrainTrack.getPoint(j * _gridSize + i).hasAltitude())
+ {
+ if (prevVert > -1 && prevVert != (j-1))
+ {
+// System.out.println("Found a gap for x=" + i +" between y=" + prevVert + " and " + j + " (" + (j-prevVert-1) + ")");
+ double startVal = inTerrainTrack.getPoint(prevVert * _gridSize + i).getAltitude().getMetricValue();
+ double endVal = inTerrainTrack.getPoint(j * _gridSize + i).getAltitude().getMetricValue();
+ for (int k=prevVert + 1; k< j; k++)
+ {
+ double val = startVal + (k-prevVert) * (endVal-startVal) / (j-prevVert);
+ if (altitudes[k * _gridSize + i] > 0.0) {
+ altitudes[k * _gridSize + i] = (altitudes[k * _gridSize + i] + val) / 2.0;
+ }
+ else {
+ altitudes[k * _gridSize + i] = val;
+ }
+ }
+ }
+ prevVert = j;
+ }
+ }
+ }
+ // Now the doubles have been set and/or averaged, we can set the values in the points
+ for (int i=0; i<inTerrainTrack.getNumPoints(); i++)
+ {
+ DataPoint p = inTerrainTrack.getPoint(i);
+ if (!p.hasAltitude() && altitudes[i] > 0.0)
+ {
+ p.setFieldValue(Field.ALTITUDE, "" + altitudes[i], false);
+ p.getAltitude().reset(new Altitude((int) altitudes[i], UnitSetLibrary.UNITS_METRES));
+ }
+ }
+ }
+}
public class ThreeDModel
{
private Track _track = null;
+ private Track _terrainTrack = null;
private PointScaler _scaler = null;
private double _scaleFactor = 1.0;
private double _altFactor = 1.0;
}
+ /**
+ * @param inTrack terrain track to set
+ */
+ public void setTerrain(Track inTrack)
+ {
+ _terrainTrack = inTrack;
+ }
+
/**
* @return the number of points in the model
*/
*/
public void setAltitudeFactor(double inFactor)
{
- if (inFactor >= 1.0) {
- _altFactor = inFactor;
- }
+ _altFactor = inFactor;
}
/**
{
// Use PointScaler to sort out x and y values
_scaler = new PointScaler(_track);
+ _scaler.addTerrain(_terrainTrack);
_scaler.scale(); // Add 10% border
// cap altitude scale factor if it's too big
if (maxAlt > 0.5)
{
// capped
- //System.out.println("Capped alt factor from " + _altFactor + " to " + (_altFactor * 0.5 / maxAlt));
+ // System.out.println("Capped alt factor from " + _altFactor + " to " + (_altFactor * 0.5 / maxAlt));
_altFactor = _altFactor * 0.5 / maxAlt;
}
{
// if no altitude, just return 0
double altVal = _scaler.getAltValue(inIndex);
- if (altVal < 0) return 0;
+ if (altVal <= 0.0) return 0.0;
// scale according to exaggeration factor
return altVal * _altFactor * _externalScaleFactor;
}
+ /**
+ * Get the scaled horizontal value for the specified terrain point
+ * @param inIndex index of point
+ * @return scaled horizontal value
+ */
+ public double getScaledTerrainHorizValue(int inIndex)
+ {
+ return _scaler.getTerrainHorizValue(inIndex) * _scaleFactor * _externalScaleFactor;
+ }
+
+ /**
+ * Get the scaled vertical value for the specified terrain point
+ * @param inIndex index of point
+ * @return scaled vertical value
+ */
+ public double getScaledTerrainVertValue(int inIndex)
+ {
+ return _scaler.getTerrainVertValue(inIndex) * _scaleFactor * _externalScaleFactor;
+ }
+
+ /**
+ * Get the scaled altitude value for the specified terrain point
+ * @param inIndex index of point
+ * @return scaled altitude value
+ */
+ public double getScaledTerrainValue(int inIndex)
+ {
+ // if no altitude, just return 0
+ double altVal = _scaler.getTerrainAltValue(inIndex);
+ if (altVal <= 0.0) return 0.0;
+ // don't scale by scale factor, needs to be unscaled
+ return altVal * _altFactor;
+ }
+
+
/**
* @param inIndex index of point, starting at 0
* @return point type, either POINT_TYPE_WAYPOINT or POINT_TYPE_NORMAL_POINT
*/
public void setTrack(Track inTrack);
+ /**
+ * @param inFactor altitude factor to use
+ */
+ public void setAltitudeFactor(double inFactor);
+
+ /**
+ * @param inDefinition image definition (image or not, source, zoom)
+ */
+ public void setBaseImageParameters(ImageDefinition inDefinition);
+
+ /**
+ * @param inDefinition terrain definition (terrain or not, resolution)
+ */
+ public void setTerrainParameters(TerrainDefinition inDefinition);
/**
* Show the window
{
// no java3d classes available
}
+ catch (NoClassDefFoundError nfe)
+ {
+ // no java3d classes available
+ }
catch (UnsatisfiedLinkError ule)
{
- // java3d available but somehow incompatible?
+ // java3d classes found but no native components
}
return has3d;
}
--- /dev/null
+package tim.prune.tips;
+
+/**
+ * Definition of a tip, including key and whether the tip
+ * has already been shown or not.
+ * This class is only visible within this package
+ */
+class TipDefinition
+{
+ /** Key of message to show when fired */
+ private String _messageKey = null;
+ /** Threshold of calls before tip is shown */
+ private int _threshold = 0;
+ /** Number of times this tip has been hit */
+ private int _hitCount = 0;
+ /** Flag whether tip is active or has already been shown */
+ private boolean _active = true;
+
+ /**
+ * Constructor
+ * @param inKey key for message to show
+ */
+ TipDefinition(String inKey)
+ {
+ this(inKey, 0);
+ }
+
+ /**
+ * Constructor
+ * @param inKey message key
+ * @param inThreshold threshold
+ */
+ TipDefinition(String inKey, int inThreshold)
+ {
+ _messageKey = inKey;
+ _threshold = inThreshold;
+ }
+
+ /**
+ * Hit this definition and check the threshold
+ * @return true if the message should be shown
+ */
+ boolean shouldShowMessage()
+ {
+ if (_active)
+ {
+ boolean overThreshold = (_hitCount >= _threshold);
+ if (!overThreshold) {
+ _hitCount++;
+ }
+ else {
+ _active = false; // only fire once
+ }
+ return overThreshold;
+ }
+ // not active
+ return false;
+ }
+
+ /**
+ * @return message key
+ */
+ String getMessageKey() {
+ return _messageKey;
+ }
+}
--- /dev/null
+package tim.prune.tips;
+
+/**
+ * Class to manage the showing of tips according
+ * to the fixed TipDefinitions
+ */
+public abstract class TipManager
+{
+ public static final int Tip_UseAMapCache = 0;
+ public static final int Tip_LearnTimeParams = 1;
+ public static final int Tip_DownloadSrtm = 2;
+ public static final int Tip_UseSrtmFor3d = 3;
+ public static final int Tip_ManuallyCorrelateOne = 4;
+ private static final int Number_Tips = Tip_ManuallyCorrelateOne + 1;
+
+ /** Array of tip definitions */
+ private static TipDefinition[] TIPDEFS = new TipDefinition[Number_Tips];
+
+ /** Static block to initialise tip definitions */
+ static
+ {
+ TIPDEFS[Tip_UseAMapCache] = new TipDefinition("tip.useamapcache", 150);
+ TIPDEFS[Tip_LearnTimeParams] = new TipDefinition("tip.learntimeparams");
+ TIPDEFS[Tip_DownloadSrtm] = new TipDefinition("tip.downloadsrtm", 5);
+ TIPDEFS[Tip_UseSrtmFor3d] = new TipDefinition("tip.usesrtmfor3d");
+ TIPDEFS[Tip_ManuallyCorrelateOne] = new TipDefinition("tip.manuallycorrelateone");
+ }
+
+ /**
+ * Fire a trigger for the specified tip and get the message key if tip should be shown
+ * @param inTipNumber number of tip from constants
+ * @return message key if a message should be shown, or null otherwise
+ */
+ public static String fireTipTrigger(int inTipNumber)
+ {
+ try {
+ TipDefinition tip = TIPDEFS[inTipNumber];
+ if (tip.shouldShowMessage()) {
+ return tip.getMessageKey();
+ }
+ }
+ catch (ArrayIndexOutOfBoundsException obe) {} // unrecognised tip given
+ return null;
+ }
+}
for (int i=0; i<numPoints; i++)
{
DataPoint point = inTrackInfo.getTrack().getPoint(i+_startIndex);
- point.getAltitude().reset(_altitudes[i]);
- point.setModified(true);
+ point.resetAltitude(_altitudes[i]);
}
_altitudes = null;
inTrackInfo.getSelection().markInvalid();
if (_moveTrackPoint != null) {
_moveTrackPoint.setSegmentStart(_moveToSegmentFlag);
}
+ inTrackInfo.getSelection().clearAll();
UpdateMessageBroker.informSubscribers();
}
}
/**\r
* Operation to undo a delete of a single audio item, either with or without point\r
*/\r
-public class UndoDeleteAudio implements UndoOperation\r
+public class UndoDeleteAudio extends UndoDeleteOperation\r
{\r
private int _audioIndex = -1;\r
private AudioClip _audio = null;\r
if (!inTrackInfo.getTrack().insertPoint(_point, _pointIndex)) {\r
throw new UndoException(getDescription());\r
}\r
+ // Change the current point/range selection if required\r
+ modifySelection(inTrackInfo, _pointIndex, _pointIndex);\r
}\r
else\r
{\r
--- /dev/null
+package tim.prune.undo;
+
+import tim.prune.data.TrackInfo;
+
+/**
+ * Abstract class to hold the selection handling required by all
+ * Undo operations which have undeleted something
+ */
+public abstract class UndoDeleteOperation implements UndoOperation
+{
+ /**
+ * Modify the current point/range selection after the delete operation is undone
+ * @param inTrackInfo track info object
+ * @param inStartIndex start index of reinserted range
+ * @param inEndIndex end index of reinserted range
+ */
+ protected static void modifySelection(TrackInfo inTrackInfo, int inStartIndex, int inEndIndex)
+ {
+ final int numPointsInserted = inEndIndex - inStartIndex + 1;
+ // See if there is a currently selected point, if so does it need to be modified
+ final int currentPoint = inTrackInfo.getSelection().getCurrentPointIndex();
+ if (currentPoint >= inStartIndex)
+ {
+ inTrackInfo.selectPoint(currentPoint + numPointsInserted);
+ }
+ // Same for currently selected range
+ int rangeStart = inTrackInfo.getSelection().getStart();
+ int rangeEnd = inTrackInfo.getSelection().getEnd();
+ if (rangeEnd >= inStartIndex && rangeEnd > rangeStart)
+ {
+ rangeEnd += numPointsInserted;
+ if (rangeStart >= inStartIndex) {
+ rangeStart += numPointsInserted;
+ }
+ inTrackInfo.getSelection().selectRange(rangeStart, rangeEnd);
+ }
+ }
+}
/**\r
* Operation to undo a delete of a single photo, either with or without point\r
*/\r
-public class UndoDeletePhoto implements UndoOperation\r
+public class UndoDeletePhoto extends UndoDeleteOperation\r
{\r
private int _photoIndex = -1;\r
private Photo _photo = null;\r
{\r
throw new UndoException(getDescription());\r
}\r
+ // Change the current point/range selection if required\r
+ modifySelection(inTrackInfo, _pointIndex, _pointIndex);\r
}\r
else\r
{\r
/**\r
* Operation to undo a delete of a single point\r
*/\r
-public class UndoDeletePoint implements UndoOperation\r
+public class UndoDeletePoint extends UndoDeleteOperation\r
{\r
private int _pointIndex = -1;\r
private DataPoint _point = null;\r
nextTrackPoint.setSegmentStart(false);\r
}\r
}\r
+ // If there's a current point or range selected, maybe need to adjust start and/or end\r
+ modifySelection(inTrackInfo, _pointIndex, _pointIndex);\r
}\r
}\r
/**\r
* Operation to undo a delete of a range of points\r
*/\r
-public class UndoDeleteRange implements UndoOperation\r
+public class UndoDeleteRange extends UndoDeleteOperation\r
{\r
/**\r
* Inner class to hold a single range information set\r
{\r
return _startIndex >= 0 && _points != null && _points.length > 0;\r
}\r
+\r
+ /**\r
+ * @return end index of range\r
+ */\r
+ public int getEndIndex()\r
+ {\r
+ return _startIndex + _points.length - 1;\r
+ }\r
}\r
\r
\r
// Undo both the ranges\r
performUndo(inTrackInfo, _rangeInfo1);\r
performUndo(inTrackInfo, _rangeInfo2);\r
+ // If there's a current point/range selected, maybe need to adjust start and/or end\r
+ if (_rangeInfo1 != null && _rangeInfo1.isValid()) {\r
+ modifySelection(inTrackInfo, _rangeInfo1._startIndex, _rangeInfo1.getEndIndex());\r
+ }\r
+ if (_rangeInfo2 != null && _rangeInfo2.isValid()) {\r
+ modifySelection(inTrackInfo, _rangeInfo2._startIndex, _rangeInfo2.getEndIndex());\r
+ }\r
}\r
\r
/**\r
{\r
// restore track to previous values\r
inTrackInfo.getTrack().replaceContents(_contents);\r
+ inTrackInfo.getSelection().clearAll();\r
}\r
-}
\ No newline at end of file
+}\r
--- /dev/null
+package tim.prune.undo;\r
+\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.Track;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo the sewing together of track segments\r
+ */\r
+public class UndoSewSegments extends UndoReorder\r
+{\r
+ /** All segment start flags need to be remembered as well */\r
+ private boolean[] _segmentStartFlags = null;\r
+\r
+ /**\r
+ * Constructor\r
+ * @param inTrack track contents to copy\r
+ */\r
+ public UndoSewSegments(Track inTrack)\r
+ {\r
+ super(inTrack, "undo.sewsegments");\r
+ // Also remember segment start flags, as they may have been changed by reversals\r
+ final int numPoints = inTrack.getNumPoints();\r
+ _segmentStartFlags = new boolean[numPoints];\r
+ for (int i=0; i<numPoints; i++) {\r
+ _segmentStartFlags[i] = inTrack.getPoint(i).getSegmentStart();\r
+ }\r
+ }\r
+\r
+ /** Perform the undo */\r
+ public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+ {\r
+ // Put all the points back in the right order\r
+ super.performUndo(inTrackInfo);\r
+ // And then restore the segment flags\r
+ for (int i=0; i<_segmentStartFlags.length; i++)\r
+ {\r
+ DataPoint point = inTrackInfo.getTrack().getPoint(i);\r
+ if (point != null && !point.isWaypoint()) {\r
+ point.setSegmentStart(_segmentStartFlags[i]);\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+package tim.prune.undo;
+
+import tim.prune.I18nManager;
+import tim.prune.data.Track;
+
+/**
+ * Undo splitting of track segments
+ */
+public class UndoSplitSegments extends UndoMergeTrackSegments
+{
+ /** Constructor */
+ public UndoSplitSegments(Track inTrack) {
+ super(inTrack, 0, inTrack.getNumPoints()-1);
+ }
+
+ /**
+ * @return description of operation
+ */
+ public String getDescription()
+ {
+ return I18nManager.getText("undo.splitsegments");
+ }
+}