Ignore:
Timestamp:
2023-02-23T21:45:50+01:00 (22 months ago)
Author:
taylor.smock
Message:

Fix #22317: IOOBE in DrawnPolyLine.getLastPoint (patch by TrickyFoxy, modified)

The modifications are as follows:

  • Added som unit tests
  • Fixed some SonarLint issues (not from patch)
  • Ensure that the last node can always be deleted
Location:
applications/editors/josm/plugins/FastDraw
Files:
8 added
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/DrawnPolyLine.java

    r35978 r36056  
    1313import org.openstreetmap.josm.data.coor.LatLon;
    1414import org.openstreetmap.josm.gui.MapView;
    15 
     15import org.openstreetmap.josm.tools.Logging;
     16
     17/**
     18 * A holder for how the polyline has been drawn
     19 */
    1620public class DrawnPolyLine {
    1721    MapView mv;
    18     private LinkedList<LatLon> points = new LinkedList<>();
     22    private final LinkedList<LatLon> points = new LinkedList<>();
    1923    private LinkedList<LatLon> simplePoints = new LinkedList<>();
    2024    private Set<LatLon> used;
    21     private Set<LatLon> fixed = new HashSet<>();
     25    private final Set<LatLon> fixed = new HashSet<>();
    2226
    2327    private int lastIdx;
    2428    private boolean closedFlag;
    2529
     30    /**
     31     * Create a new object
     32     */
    2633    public DrawnPolyLine() {
    2734        clear();
     
    5764
    5865    boolean wasSimplified() {
    59         return (simplePoints != null && simplePoints.size() > 0);
     66        return (simplePoints != null && !simplePoints.isEmpty());
    6067    }
    6168
     
    8592
    8693    void undo() {
    87         //if (points.size() > 0) points.removeLast();
    8894        if (lastIdx > 0 && lastIdx < points.size()) {
    8995            points.remove(lastIdx);
     
    114120            }
    115121        } else {
    116             // insert point into midlle of the line
     122            // insert point into middle of the line
    117123            if (points.isEmpty() || !coor.equals(points.get(lastIdx))) {
    118                 points.add(lastIdx+1, coor);
     124                points.add(lastIdx + 1, coor);
    119125                lastIdx++;
    120126            }
     
    137143    /**
    138144     * Increase epsilon to fit points count in maxPKM point per 1 km
     145     * @param initEpsilon min point-to line distance in pixels (tolerance, initial)
     146     * @param ekf The multiplier against the epsilon for the next round, until the max points per km is reached
     147     * @param k see {@link #getNodesPerKm(int)}
     148     * @param maxPKM The maximum nodes per km
     149     * @return The final epsilon value used
    139150     */
    140151    double autoSimplify(double initEpsilon, double ekf, int k, double maxPKM) {
    141152        double e = initEpsilon;
    142153        if (e < 1e-3) e = 1e-3;
    143         if (ekf < 1+1e-2) ekf = 1.01;
     154        if (ekf < 1 + 1e-2) ekf = 1.01;
    144155        simplify(e);
    145156        while (getNodesPerKm(k) > maxPKM && e < 1e3) {
    146             e = e*ekf;
     157            e = e * ekf;
    147158            simplify(e);
    148             //System.out.printf("eps=%f n=%d\n", e,simplePoints.size());
     159            Logging.trace("DrawnPolyLine: eps={0} n={1}", e, simplePoints.size());
    149160        }
    150161        return e;
     
    153164    /**
    154165     * Simplified drawn line, not touching the nodes includes in "fixed" set.
     166     * @param epsilon min point-to line distance in pixels (tolerance)
    155167     */
    156168    void simplify(double epsilon) {
    157         //System.out.println("Simplify polyline...");
     169        Logging.trace("DrawnPolyLine: Simplify polyline...");
    158170        int n = points.size();
    159171        if (n < 3) return;
     
    173185        simplePoints.addAll(points);
    174186        simplePoints.retainAll(used);
    175         //Main.map.mapView.repaint();
    176187        used = null;
    177188    }
     
    179190    /**
    180191     * Simplification of the line specified by "points" field.
    181      * Remainin points are included to "used" set.
     192     * Remaining points are included to "used" set.
    182193     * @param start - starting index
    183194     * @param end - ending index
     
    190201        LatLon first = points.get(start);
    191202        LatLon last = points.get(end);
    192         Point firstp = getPoint(first);
    193         Point lastp = getPoint(last);
     203        Point firstP = getPoint(first);
     204        Point lastP = getPoint(last);
    194205        used.add(first);
    195206        used.add(last);
     
    197208        if (end - start < 2) return;
    198209
    199         int farthest_node = -1;
    200         double farthest_dist = 0;
    201 
    202         double d = 0;
     210        int farthestNode = -1;
     211        double farthestDist = 0;
    203212
    204213        for (int i = start + 1; i < end; i++) {
    205             d = pointLineDistance(getPoint(points.get(i)), firstp, lastp);
    206             if (d > farthest_dist) {
    207                 farthest_dist = d;
    208                 farthest_node = i;
    209             }
    210         }
    211 
    212         if (farthest_dist > epsilon) {
    213             douglasPeucker(start, farthest_node, epsilon, depth + 1);
    214             douglasPeucker(farthest_node, end, epsilon, depth + 1);
    215         }
    216     }
    217 
    218     /** Modfified funclion from LakeWalker
     214            double d = pointLineDistance(getPoint(points.get(i)), firstP, lastP);
     215            if (d > farthestDist) {
     216                farthestDist = d;
     217                farthestNode = i;
     218            }
     219        }
     220
     221        if (farthestDist > epsilon) {
     222            douglasPeucker(start, farthestNode, epsilon, depth + 1);
     223            douglasPeucker(farthestNode, end, epsilon, depth + 1);
     224        }
     225    }
     226
     227    /**
     228     * Modified function from LakeWalker
    219229     * Gets distance from point p1 to line p2-p3
     230     * @param p1 The originating point
     231     * @param p2 An end of the line
     232     * @param p3 The other end of the line
     233     * @return The distance from point p1 to the line p2-p3
    220234     */
    221235    public double pointLineDistance(Point p1, Point p2, Point p3) {
     
    240254
    241255    void deleteNode(int idx) {
    242         if (idx <= lastIdx) lastIdx--;
     256        final boolean isClosed = isClosed();
     257        final LatLon point = points.get(idx);
     258        if (isClosed) {
     259            if (idx == 0) idx = getPointCount() - 1;
     260            if (idx == getPointCount() - 1) closedFlag = false;
     261        }
     262        if (idx <= lastIdx && lastIdx != 0) lastIdx--;
    243263        fixed.remove(points.get(idx));
    244264        points.remove(idx);
     265        if (isClosed && points.size() == 1 && points.get(0) == point) {
     266            this.deleteNode(0);
     267        }
    245268    }
    246269
     
    280303    }
    281304
    282     /** find starting point of the polyline line fragment close to p
     305    /**
     306     * find starting point of the polyline line fragment close to p
    283307     *  line fragment = segments between two fixed (green) nodes
     308     * @param p The point to find the segment for
     309     * @return the LatLon of the start of the segment
    284310     */
    285311    LatLon findBigSegment(Point p) {
     
    288314        Iterator<LatLon> it2 = points.listIterator(1);
    289315        Point p1, p2;
    290         LatLon pp1, pp2, start = null;
     316        LatLon pp1, pp2, start;
    291317        start = points.getFirst();
    292318        do {
     
    306332    }
    307333
    308     private double pointSegmentDistance(Point p, Point p1, Point p2) {
    309         double a, b, x, y, l, kt, kn, dist;
    310         x = p.x-p1.x;
    311         y = p.y-p1.y;
    312         a = p2.x-p1.x;
    313         b = p2.y-p1.y;
    314         l = Math.hypot(a, b);
    315         if (l == 0) return Math.hypot(x, y); // p1 = p2
    316         kt = (x*a+y*b)/l;
    317         kn = Math.abs((-x*b+y*a)/l);
     334    private static double pointSegmentDistance(Point p, Point p1, Point p2) {
     335        final int x = p.x - p1.x;
     336        final int y = p.y - p1.y;
     337        final int a = p2.x - p1.x;
     338        final int b = p2.y - p1.y;
     339        final double l = Math.hypot(a, b);
     340        if (l == 0) {
     341            return Math.hypot(x, y); // p1 = p2
     342        }
     343        final double kt = (x * a + y * b)/l;
     344        final double kn = Math.abs((-x * b + y * a)/l);
     345        double dist;
    318346        if (kt >= 0 && kt < l) dist = kn; else {
    319             dist = Math.min(Math.hypot(x, y), Math.hypot(x-a, y-b));
     347            dist = Math.min(Math.hypot(x, y), Math.hypot((double) x - a, (double) y - b));
    320348        }
    321349        return dist;
     
    360388     * max((k-1) / (L(i,i+1)+L(i+1,i+2)+...+L(i+k-1,i+k))) [ i=1..n-k ]
    361389     * @param k - window size (number of points to average points per km
     390     * @return The maximum number of <i>simplified</i> line points divided by the segment length
    362391     */
    363392    public double getNodesPerKm(int k) {
     
    369398        if (k > n) k = n;
    370399
    371         ILatLon pp1, pp2 = null;
     400        ILatLon pp1, pp2;
    372401        Iterator<LatLon> it1, it2;
    373402        it1 = pts.listIterator(0);
     
    376405        for (int i = 0; i < n-1; i++) {
    377406            pp1 = it1.next();
    378             //p1 = getPoint(pp1);
    379407            pp2 = it2.next();
    380             //p2 =sa getPoint(pp2);
    381408            lens[i] = pp1.greatCircleDistance(pp2);
    382409        }
    383410        double pkm = 0, maxpkm = 0;
    384411        double len = 0;
    385         int seg = 0; // averaged segments counts
     412        int seg; // averaged segments counts
    386413        for (int i = 1; i < n; i++) {
    387414            len += lens[i-1]; // add next next point
     
    395422                // len is length of points[i-windowSize] .. points[i]
    396423                if (len > 0) pkm = seg / len * 1000;
    397                 //System.out.println("i="+i+" pkm="+len+" pkm="+pkm);
     424                Logging.trace("DrawnPolyLine: i={0} len={1} pkm={2}", i, len, pkm);
    398425                if (pkm > maxpkm) maxpkm = pkm;
    399426            }
Note: See TracChangeset for help on using the changeset viewer.