1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
|
package tim.prune.function;
import java.util.ArrayList;
import tim.prune.App;
import tim.prune.I18nManager;
import tim.prune.cmd.AppendRangeCmd;
import tim.prune.config.Config;
import tim.prune.data.DataPoint;
import tim.prune.data.RangeStats;
import tim.prune.data.UnitSetLibrary;
/**
* Function to create waypoints marking regular distance intervals,
* regular time intervals, or halfway points
*/
public class CreateMarkerWaypointsFunction extends DistanceTimeLimitFunction
{
/** ArrayList of points to append to the track */
private final ArrayList<DataPoint> _pointsToAdd = new ArrayList<>();
/** Counter of previously used multiple */
private int _previousMultiple = 0;
/*
* Type of halfway point
*/
private enum HalfwayType
{
HALF_DISTANCE,
HALF_CLIMB,
HALF_DESCENT
}
/**
* Constructor
*/
public CreateMarkerWaypointsFunction(App inApp) {
super(inApp, true);
}
/**
* @return name key
*/
public String getNameKey() {
return "function.createmarkerwaypoints";
}
/**
* Init the state to start collecting a new set of points
*/
private void initMemory()
{
_pointsToAdd.clear();
_previousMultiple = 0;
}
/**
* The dialog has been completed and OK pressed, so do the point creation
*/
protected void performFunction()
{
// Determine which kind of markers to create
final int timeLimitSeconds = getTimeLimitInSeconds();
final boolean createByTime = (timeLimitSeconds > 0);
final double distLimitKm = getDistanceLimitKilometres();
final boolean createByDistance = (distLimitKm > 0.0);
final boolean createHalves = isHalvesSelected();
// set up the memory from scratch to collect the created points
initMemory();
if (createByTime || createByDistance) {
createWaypointsAtIntervals(timeLimitSeconds, distLimitKm);
}
else if (createHalves) {
createHalfwayWaypoints();
}
else {
return;
}
if (!_pointsToAdd.isEmpty())
{
AppendRangeCmd command = new AppendRangeCmd(_pointsToAdd);
command.setDescription(getName());
final String confirmMessage = I18nManager.getTextWithNumber("confirm.pointsadded", _pointsToAdd.size());
command.setConfirmText(confirmMessage);
_app.execute(command);
}
_dialog.dispose();
}
/**
* Create waypoints according to the given intervals
* @param inTimeLimitSeconds time limit in seconds
* @param inDistLimitKm distance limit in kilometres
*/
private void createWaypointsAtIntervals(int inTimeLimitSeconds, double inDistLimitKm)
{
final boolean createByTime = (inTimeLimitSeconds > 0);
final boolean createByDistance = (inDistLimitKm > 0.0);
// Make new waypoints, looping through the points in the track
DataPoint prevPoint = null;
double currValue = 0.0, prevValue = 0.0;
final int altitudeTolerance = getConfig().getConfigInt(Config.KEY_ALTITUDE_TOLERANCE) / 100;
RangeStats rangeStats = new RangeStats(altitudeTolerance);
final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
for (int i=0; i<numPoints; i++)
{
DataPoint currPoint = _app.getTrackInfo().getTrack().getPoint(i);
rangeStats.addPoint(currPoint);
if (!currPoint.isWaypoint())
{
// Calculate current value
if (createByTime)
{
currValue = rangeStats.getMovingDurationInSeconds();
processValue(prevPoint, prevValue, inTimeLimitSeconds, currPoint, currValue);
}
else if (createByDistance)
{
currValue = rangeStats.getMovingDistanceKilometres();
processValue(prevPoint, prevValue, inDistLimitKm, currPoint, currValue);
}
prevPoint = currPoint;
prevValue = currValue;
}
}
}
/**
* Consider a pair of points in the track to see if a new marker should be inserted between them
* @param inPrevPoint previous point
* @param inPrevValue value of function at this previous point
* @param inLimit user-specified limit for marker values
* @param inCurrPoint current point
* @param inCurrValue value of function at this current point
*/
private void processValue(DataPoint inPrevPoint, double inPrevValue, double inLimit,
DataPoint inCurrPoint, double inCurrValue)
{
// Check the current multiple and compare with previously used one
final int currMultiple = (int) Math.floor(inCurrValue / inLimit);
for (int m=_previousMultiple+1; m<=currMultiple; m++)
{
// Calculate position of limit between the two points
final double valueBeforeBreak = (m * inLimit) - inPrevValue;
final double valueAfterBreak = inCurrValue - (m * inLimit);
final double fractionFromPrev = valueBeforeBreak / (valueBeforeBreak + valueAfterBreak);
DataPoint marker = PointUtils.interpolate(inPrevPoint, inCurrPoint, fractionFromPrev);
marker.setWaypointName(createLimitDescription(m));
_pointsToAdd.add(marker);
}
_previousMultiple = currMultiple;
}
/**
* Create waypoints for the halfway markers
*/
private void createHalfwayWaypoints()
{
// Calculate the details of the whole track so we can see what to halve
final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
final int altitudeTolerance = getConfig().getConfigInt(Config.KEY_ALTITUDE_TOLERANCE) / 100;
RangeStats totalStats = new RangeStats(altitudeTolerance);
for (int i=0; i<numPoints; i++)
{
DataPoint currPoint = _app.getTrackInfo().getTrack().getPoint(i);
totalStats.addPoint(currPoint);
}
// Calculate total moving distance of track
final double totalDist = totalStats.getMovingDistanceKilometres();
// If the track has altitudes, also calculate total climb and total descent
final double totalClimb = totalStats.getMovingAltitudeRange().getClimb(UnitSetLibrary.UNITS_METRES);
final double totalDescent = totalStats.getMovingAltitudeRange().getDescent(UnitSetLibrary.UNITS_METRES);
final double halfDistance = totalDist / 2.0;
final double halfClimb = totalClimb / 2.0;
final double halfDescent = totalDescent / 2.0;
// Now loop through points again, looking for the halfway points
RangeStats partialStats = new RangeStats(altitudeTolerance);
DataPoint prevPoint = null;
boolean createdDistance = false, createdClimb = false, createdDescent = false;
double prevDistance = 0.0, prevClimb = 0.0, prevDescent = 0.0;
for (int i=0; i<numPoints; i++)
{
DataPoint currPoint = _app.getTrackInfo().getTrack().getPoint(i);
partialStats.addPoint(currPoint);
if (!currPoint.isWaypoint())
{
// distance
if (!createdDistance && totalDist > 0.0)
{
final double currDist = partialStats.getMovingDistanceKilometres();
createdDistance = processHalfValue(prevPoint, prevDistance, halfDistance,
currPoint, currDist, HalfwayType.HALF_DISTANCE);
prevDistance = currDist;
}
// climb
if (!createdClimb && totalClimb > 0.0)
{
final double currClimb = partialStats.getMovingAltitudeRange().getClimb(UnitSetLibrary.UNITS_METRES);
createdClimb = processHalfValue(prevPoint, prevClimb, halfClimb,
currPoint, currClimb, HalfwayType.HALF_CLIMB);
prevClimb = currClimb;
}
// descent
if (!createdDescent && totalDescent > 0.0)
{
final double currDescent = partialStats.getMovingAltitudeRange().getDescent(UnitSetLibrary.UNITS_METRES);
createdDescent = processHalfValue(prevPoint, prevDescent, halfDescent,
currPoint, currDescent, HalfwayType.HALF_DESCENT);
prevDescent = currDescent;
}
prevPoint = currPoint;
}
}
}
/**
* Consider a pair of points in the track to see if a new halfway marker should be inserted between them
* @param inPrevPoint previous point
* @param inPrevValue value of function at this previous point
* @param inTargetValue target halfway value
* @param inCurrPoint current point
* @param inCurrValue value of function at this current point
* @param inType type of halfway point
*/
private boolean processHalfValue(DataPoint inPrevPoint, double inPrevValue, double inTargetValue,
DataPoint inCurrPoint, double inCurrValue, HalfwayType inType)
{
if (inPrevValue <= inTargetValue && inCurrValue >= inTargetValue)
{
// Calculate position of limit between the two points
final double valueBeforeBreak = inTargetValue - inPrevValue;
final double valueAfterBreak = inCurrValue - inTargetValue;
final double fractionFromPrev = valueBeforeBreak / (valueBeforeBreak + valueAfterBreak);
DataPoint marker = PointUtils.interpolate(inPrevPoint, inCurrPoint, fractionFromPrev);
marker.setWaypointName(createHalfwayName(inType));
_pointsToAdd.add(marker);
return true;
}
return false;
}
/**
* Create the name of the halfway point according to type
* @param inType type of point
*/
private String createHalfwayName(HalfwayType inType)
{
final String typeString;
switch (inType)
{
case HALF_DISTANCE:
typeString = "distance";
break;
case HALF_CLIMB:
typeString = "climb";
break;
case HALF_DESCENT:
typeString = "descent";
break;
default:
return "half";
}
return I18nManager.getText("dialog.markers.half." + typeString);
}
}
|