Changeset 27588 in osm for applications


Ignore:
Timestamp:
2012-01-22T23:22:56+01:00 (13 years ago)
Author:
tilusnet
Message:

Enhancement for angle preserving alignment: alignment still allowed if the alignee's endpoint is joint to two other segments which form a straight(ened) line.

Location:
applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/AlignWaysCmdKeepAngles.java

    r27353 r27588  
    3434
    3535        // Now the calculatedNodes reflect the coordinates that we'd have
    36         // without preserving the angles.
     36        // without preserving the angles, i.e. preserving the length.
    3737
    3838        Map<Node,EastNorth> calcNodesKeepLength = calculatedNodes;
    3939
     40        // Now we'll proceed with the hypothetical recalculation of the endpoint coordinates
     41        // following the rule of preserving angles instead. The new nodes will be stored in nodeArr[].
     42       
    4043        Node[] nodeArr = algnSeg.getSegmentEndPoints().toArray(new Node[2]);
    4144
     
    4447        AlignWaysGeomLine lineKeepLength = new AlignWaysGeomLine(enCalc1.getX(), enCalc1.getY(), enCalc2.getX(), enCalc2.getY());
    4548
    46         recalculateNodes(lineKeepLength, nodeArr[0]);
    47         recalculateNodes(lineKeepLength, nodeArr[1]);
    48 
    49     }
    50 
    51 
    52     void recalculateNodes(AlignWaysGeomLine alignedLineKeepLength, Node endpoint) {
    53 
     49        recalculateNodesAndValidate(lineKeepLength, nodeArr[0]);
     50        recalculateNodesAndValidate(lineKeepLength, nodeArr[1]);
     51
     52    }
     53
     54
     55    void recalculateNodesAndValidate(AlignWaysGeomLine alignedLineKeepLength, Node endpoint) {
     56
     57        if (endpoint.getEastNorth().equals(pivot)) {
     58                // endpoint is pivot: the coordinates won't change
     59                return;
     60        }
     61       
    5462        ArrayList<WaySegment> alws = algnSeg.getAdjacentWaySegments(endpoint);
    55         if (alws.size() == 1) {
     63        if (alws.size() <= 2) {
    5664            // We need the intersection point of
    57             //  - the freely rotated alignee as calculated in calcNodesFreeAngle
     65            //  - the alignee following the keep length rule
    5866            //  - the adjacent way segment
    59 
    60             EastNorth enAdj1 = alws.get(0).getFirstNode().getEastNorth();
    61             EastNorth enAdj2 = alws.get(0).getSecondNode().getEastNorth();
    62 
    63             // Update the calculated node for aligning while keeping angles
    64             AlignWaysGeomPoint isectPnt = alignedLineKeepLength.getIntersection(new AlignWaysGeomLine(enAdj1.getX(), enAdj1.getY(), enAdj2.getX(), enAdj2.getY()));
     67               
     68                EastNorth enAdjOther1 = getNonEqualEN(alws.get(0), endpoint);
     69                EastNorth enAdjOther2 = null;
     70
     71            if (alws.size() == 2) {
     72                enAdjOther2 = getNonEqualEN(alws.get(1), endpoint);
     73               
     74                // In order have a chance to align, (enAdjOther1, enAdjOther2 and endpoint) must be collinear
     75                ArrayList<EastNorth> enAdjPts = new ArrayList<EastNorth>(3);
     76                enAdjPts.add(enAdjOther1);
     77                enAdjPts.add(endpoint.getEastNorth());
     78                enAdjPts.add(enAdjOther2);
     79                if (!isEnSetCollinear(enAdjPts)) {
     80                        // Not collinear, no point to proceed
     81                    alignableStatKeepAngles = AlignableStatus.ALGN_INV_ANGLE_PRESERVING_CONFLICT;
     82                    return;
     83                }
     84               
     85            }
     86
     87            // Update the calculated node for angle preserving alignment
     88            AlignWaysGeomPoint isectPnt = alignedLineKeepLength.getIntersection(new AlignWaysGeomLine(enAdjOther1.getX(), enAdjOther1.getY(),
     89                                                                                                                                                                                          endpoint.getEastNorth().getX(), endpoint.getEastNorth().getY()));
     90            EastNorth enIsectPt = null;
    6591            // If the intersection is null, the adjacent and the alignee are parallel already:
    6692            // there's no need to update this node
    6793            if (isectPnt != null) {
    68                 calculatedNodes.put(endpoint, new EastNorth(isectPnt.getX(), isectPnt.getY()));
     94                enIsectPt = new EastNorth(isectPnt.getX(), isectPnt.getY());           
     95                calculatedNodes.put(endpoint, enIsectPt);
    6996            } else if (alignedLineKeepLength.getIntersectionStatus() == IntersectionStatus.LINES_PARALLEL) {
    7097                alignableStatKeepAngles = AlignableStatus.ALGN_INV_ANGLE_PRESERVING_CONFLICT;
    7198            }
     99           
     100            // For the case of two adjacent segments with collinear points, the new endpoint may 
     101            // not fall between enAdjOther1 and enAdjOther2; in this case one of them is redundant
     102            // and should be deleted from OSM
     103            if (alws.size() == 2 && enIsectPt != null) {
     104                int middlePtIdx = AlignWaysGeomPoint.getMiddleOf3(
     105                                new AlignWaysGeomPoint(enIsectPt),
     106                                new AlignWaysGeomPoint(enAdjOther1),
     107                                new AlignWaysGeomPoint(enAdjOther2));
     108                if (middlePtIdx != 0) {
     109                        EastNorth middlePt = null;
     110                        switch(middlePtIdx) {
     111                                case 1:
     112                                        middlePt = enIsectPt;
     113                                        break;
     114                                case 2:
     115                                        middlePt = enAdjOther1;
     116                                        break;
     117                                case 3:
     118                                        middlePt = enAdjOther2;
     119                                        break;
     120                        }
     121                        if (middlePt != null) {
     122                                double eps = 1E-6;
     123                                if (!middlePt.equalsEpsilon(enIsectPt, eps)) {
     124                                        // Intersection point didn't fall between the two adjacent points; something must go
     125                                        if (middlePt.equalsEpsilon(enAdjOther1, eps)) {
     126                                                // TODO Delete enAdjOther1
     127                                                if (true);
     128                                                // Main.map.
     129                                        } else
     130                                                // TODO Delete enAdjOther2
     131                                                if (true);
     132                                }
     133                        }
     134                }
     135            }
     136           
    72137        } else {
    73             if (!endpoint.getEastNorth().equals(pivot)) {
    74                 // Report non-pivot endpoints only
    75                 alignableStatKeepAngles = AlignableStatus.ALGN_INV_TOOMANY_CONNECTED_WS;
    76             }
     138                // angle preserving alignment not possible
     139            alignableStatKeepAngles = AlignableStatus.ALGN_INV_TOOMANY_CONNECTED_WS;
    77140        }
    78141
     
    80143
    81144
    82     /**
     145    private boolean isEnSetCollinear(ArrayList<EastNorth> enAdjPts) {
     146        ArrayList<AlignWaysGeomPoint> awAdjPts = new ArrayList<AlignWaysGeomPoint>();
     147       
     148        for (EastNorth en : enAdjPts) {
     149                AlignWaysGeomPoint pt = new AlignWaysGeomPoint(en.getX(), en.getY());
     150                awAdjPts.add(pt);
     151        }
     152       
     153                return AlignWaysGeomPoint.isSetCollinear(awAdjPts);
     154        }
     155
     156
     157        private EastNorth getNonEqualEN(WaySegment waySegment, Node endpoint) {
     158        if (waySegment.getFirstNode().equals(endpoint)) {
     159                return waySegment.getSecondNode().getEastNorth();
     160        } else if (waySegment.getSecondNode().equals(endpoint)) {
     161                return waySegment.getFirstNode().getEastNorth();
     162        } else
     163                return null;
     164        }
     165
     166
     167        /**
    83168     * Reports invalid alignable statuses on screen in dialog boxes.
    84169     *
     
    140225            return status;
    141226
    142         // Check the remainder of the potential problems: -
    143         // Check for the number of segments connecting to the alignee endpoints
    144         for (Node nA : displaceableNodes) {
    145             if (nA.getEastNorth().equals(pivot)) {
    146                 // Pivots set to endpoints are exempt from this check
    147                 continue;
    148             } else {
    149                 // This node should not connect to more than one other way segment
    150                 if (isReferredByNOtherWaySegments(nA, 2))
    151                     return AlignableStatus.ALGN_INV_TOOMANY_CONNECTED_WS;
    152             }
    153         }
     227        // Check the remainder of the potential problems: N/A
    154228
    155229        // In all other cases alignment is possible
  • applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/geometry/AlignWaysGeomLine.java

    r27348 r27588  
    7575    }
    7676
    77     /**
    78      * Returns the intersection point the line with another line.
     77    public AlignWaysGeomLine(AlignWaysGeomPoint awPt1, AlignWaysGeomPoint awPt2) {
     78        this(awPt1.getX(), awPt1.getY(), awPt2.getX(), awPt2.getY());
     79        }
     80
     81        /**
     82     * Returns the intersection point of the line with another line.
    7983     * If the lines are parallel or overlap, returns null.
    8084     * Use getIntersectionStatus() to determine the case.
     
    180184    }
    181185
     186        public boolean isPointOnLine(AlignWaysGeomPoint awPt) {
     187                // Method:
     188                // 1. create a new line from awPt and one point of 'this'
     189                // 2. check getIntersectionStatus of the two lines
     190                // 3. if status is LINES_OVERLAP, the point os one the line, otherwise not
     191               
     192                // Need an arbitrary point on this line; let it be (x, y)
     193                Double x = 0.0;
     194                Double y = getYonLine(x);
     195                if (y.isNaN()) y = 0.0;
     196               
     197                AlignWaysGeomLine line2 = new AlignWaysGeomLine(awPt, new AlignWaysGeomPoint(x, y));
     198                getIntersection(line2);
     199                if (getIntersectionStatus() == IntersectionStatus.LINES_OVERLAP)
     200                        return true;
     201                else
     202                        return false;
     203               
     204        }
     205
    182206}
  • applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/geometry/AlignWaysGeomPoint.java

    r27348 r27588  
    11package com.tilusnet.josm.plugins.alignways.geometry;
     2
     3import java.util.ArrayList;
     4import java.util.Arrays;
     5import java.util.Collections;
     6
     7import org.openstreetmap.josm.data.coor.EastNorth;
    28
    39public class AlignWaysGeomPoint {
     
    1016    }
    1117
    12     public double getX() {
     18    public AlignWaysGeomPoint(EastNorth eastNorth) {
     19        this.x = eastNorth.getX();
     20        this.y = eastNorth.getY();
     21        }
     22
     23        public double getX() {
    1324        return x;
    1425    }
     
    2637    }
    2738
     39        public static boolean isSetCollinear(ArrayList<AlignWaysGeomPoint> awPts) {
     40                if (awPts.size() <= 1)
     41                        return false;
     42               
     43                if (awPts.size() == 2)
     44                        return true;
     45                else {
     46                        // at least 3 points
     47                        // First create a line of the first two points in the set
     48                        AlignWaysGeomLine line = new AlignWaysGeomLine(awPts.get(0), awPts.get(1));
     49                        // ...then check the subsequent points whether they are on the line
     50                        for (int i = 2; i < awPts.size(); i++) {
     51                                if (!line.isPointOnLine(awPts.get(i))) {
     52                                        return false;
     53                                }
     54                        }
     55                        return true;
     56                }
     57        }
     58
     59        /**
     60         * Determines which (EastNorth) point falls between the other two.
     61         * Ideally to be used with collinear points.
     62         *
     63         * @return 1, 2 or 3 for pt1, pt2 and pt3, respectively.
     64         * 0 if middle value cannot be determined (i.e. some values are equal).
     65         */
     66        public static int getMiddleOf3(
     67                        AlignWaysGeomPoint pt1,
     68                        AlignWaysGeomPoint pt2,
     69                        AlignWaysGeomPoint pt3) {
     70               
     71                int midPtXIdx = getMiddleOf3(pt1.x, pt2.x, pt3.x);
     72                int midPtYIdx = getMiddleOf3(pt1.y, pt2.y, pt3.y);
     73               
     74                if ((midPtXIdx == 0) && (midPtYIdx == 0))
     75                        // All 3 points overlap:
     76                        // Design decision: return the middle point (could be any other or none)
     77                        return 2;
     78               
     79                if (midPtXIdx == 0) return midPtYIdx;
     80                if (midPtYIdx == 0) return midPtXIdx;
     81               
     82                // Both x and y middle points could be determined;
     83                // their indexes must coincide
     84                if (midPtXIdx == midPtYIdx)
     85                        // Success
     86                        return midPtXIdx; // (or midPtYIdx)
     87                else
     88                        // Fail
     89                        return 0;
     90               
     91        }
     92
     93        /**
     94         * Determine which value, d1, d2 or d3 falls in the middle of the other two.
     95         * @return 1, 2 or 3 for d1, d2 and d3, respectively.
     96         * 0 if middle value cannot be determined (i.e. some values are equal).
     97         */
     98        private static int getMiddleOf3(double d1, double d2, double d3) {
     99               
     100                Double[] dValues = {d1, d2, d3};
     101                ArrayList<Double> alValues = new ArrayList<Double>(Arrays.asList(dValues));
     102                Collections.sort(alValues);
     103               
     104                if ((Math.abs(alValues.get(1) - alValues.get(0)) < 0.01) ||
     105                    (Math.abs(alValues.get(1) - alValues.get(2)) < 0.01))
     106                        // Cannot determine absolute middle value
     107                        return 0;
     108                else {
     109                        if (Math.abs(alValues.get(1) - d1) < 0.01) return 1;
     110                        if (Math.abs(alValues.get(1) - d2) < 0.01) return 2;
     111                        if (Math.abs(alValues.get(1) - d3) < 0.01) return 3;
     112                }
     113               
     114                // Should never happen
     115                return 0;
     116        }
    28117}
Note: See TracChangeset for help on using the changeset viewer.