package tim.prune.function.compress; import java.awt.Component; import java.awt.event.ActionListener; import tim.prune.data.DataPoint; import tim.prune.data.Track; /** * Douglas-Peucker algorithm for compresssion */ public class DouglasPeuckerAlgorithm extends SingleParameterAlgorithm { /** * Constructor * @param inTrack track object * @param inDetails track details object * @param inListener listener to attach to activation control */ public DouglasPeuckerAlgorithm(Track inTrack, TrackDetails inDetails, ActionListener inListener) { super(inTrack, inDetails, inListener); } /** * Perform the compression and work out which points should be deleted * @param inFlags deletion flags from previous algorithms * @return number of points deleted */ protected int compress(boolean[] inFlags) { // Parse parameter double param = getParameter(); // Use 1/x if x greater than 1 if (param > 1.0) param = 1.0 / param; if (param <= 0.0 || param >= 1.0) { // Parameter isn't valid, don't delete any return 0; } double threshold = _trackDetails.getTrackSpan() * param; int numPoints = _track.getNumPoints(); int origNumDeleted = countFlags(inFlags); // Convert inFlags into keepFlags int[] keepFlags = new int[numPoints]; int segStart = -1, segEnd = -1; // Loop over all points in track for (int i=0; i -1 && segEnd > segStart) { keepFlags[segEnd] = 1; // keep compressSegment(keepFlags, segStart, segEnd, threshold); segStart = segEnd = -1; } } if (inFlags[i]) keepFlags[i] = -1; // already deleted else if (currPoint.isWaypoint() || currPoint.hasMedia() || currPoint.getSegmentStart()) { keepFlags[i] = 1; // keep } // Don't consider points which are already marked as deleted, ignore waypoints if (!inFlags[i] && !currPoint.isWaypoint()) { // remember starts and ends if (segStart < 0) {segStart = i;} else {segEnd = i;} } } // Last segment, if any if (segStart >= 0 && segEnd > segStart) { keepFlags[segEnd] = 1; // keep compressSegment(keepFlags, segStart, segEnd, threshold); } // Convert keepFlags back into inFlags for (int i=1; i inSegStart) { compressSegment(inFlags, inSegStart, furthestIndex, inThreshold); compressSegment(inFlags, furthestIndex, inSegEnd, inThreshold); } return; } double maxDist = -1.0, dist = -1.0; int furthestIndex = -1; for (int i=inSegStart+1; i 1.0) { dist = endxy.vectorTo(currPoint).len(); // outside on the B side } else { // P lies between A and B so use dot product dist = Math.abs(perpendicular.dot(ac)); } if (dist > maxDist) { maxDist = dist; furthestIndex = i; } } } // Check furthest point and see if it's further than the threshold if (maxDist > inThreshold) { inFlags[furthestIndex] = 1; // Make recursive calls for bit before and bit after kept point compressSegment(inFlags, inSegStart, furthestIndex, inThreshold); compressSegment(inFlags, furthestIndex, inSegEnd, inThreshold); } } /** * @return specific gui components for dialog */ protected Component getSpecificGuiComponents() { return getSpecificGuiComponents("dialog.compress.douglaspeucker.paramdesc", "2000"); } /** * @return title key for box */ protected String getTitleTextKey() { return "dialog.compress.douglaspeucker.title"; } /** * Find the index of the point furthest away from the start and end points * @param inStartIndex start index of segment to check * @param inEndIndex end index of segment to check * @return index of furthest point, or -1 if none found */ private int getFurthestPointIndex(int inStartIndex, int inEndIndex) { int furthestIndex = -1; if (inStartIndex >= 0 && inEndIndex > inStartIndex) { final DataPoint startPoint = _track.getPoint(inStartIndex); double maxDist = 0.0; // Loop over points between start and end for (int i=inStartIndex+1; i maxDist) { furthestIndex = i; maxDist = distFromStart; } } } } return furthestIndex; } }