Changeset 1089 in josm


Ignore:
Timestamp:
Nov 20, 2008 7:33:47 PM (5 years ago)
Author:
framm
Message:
  • now really added the patch that *should* have been added in r1085.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java

    r1085 r1089  
    4747        } 
    4848 
    49         public void actionPerformed(ActionEvent e) { 
    50          
    51                 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 
    52          
     49    public void actionPerformed(ActionEvent e) { 
     50 
     51        Collection<OsmPrimitive> sel = Main.ds.getSelected(); 
     52 
    5353        ArrayList<Node> dirnodes = new ArrayList<Node>(); 
    54                  
     54 
    5555        // Check the selection if it is suitible for the orthogonalization 
    56                 for (OsmPrimitive osm : sel) { 
     56        for (OsmPrimitive osm : sel) { 
    5757            // Check if not more than two nodes in the selection 
    5858            if(osm instanceof Node) { 
     
    6565            } 
    6666            // Check if selection consists now only of ways 
    67                     if (!(osm instanceof Way)) { 
     67            if (!(osm instanceof Way)) { 
    6868                JOptionPane.showMessageDialog(Main.parent, tr("Selection must consist only of ways.")); 
    69                         return; 
     69                return; 
    7070            }  
    71              
     71 
    7272            // Check if every way is made of at least four segments and closed 
    7373            Way way = (Way)osm; 
     
    7676                return; 
    7777            } 
    78              
     78 
    7979            // Check if every edge in the way is a definite edge of at least 45 degrees of direction change 
    8080            // Otherwise, two segments could be turned into same direction and intersection would fail.  
    8181            // Or changes of shape would be too serious. 
    82             for (int i1=0; i1 < way.nodes.size()-1; i1++) {     
    83                int i2 = (i1+1) % (way.nodes.size()-1); 
    84                int i3 = (i1+2) % (way.nodes.size()-1); 
    85                double angle1  =Math.abs(way.nodes.get(i1).eastNorth.heading(way.nodes.get(i2).eastNorth)); 
    86                double angle2 = Math.abs(way.nodes.get(i2).eastNorth.heading(way.nodes.get(i3).eastNorth)); 
    87                double delta = Math.abs(angle2 - angle1); 
    88                while(delta > Math.PI) delta -= Math.PI; 
    89                if(delta < Math.PI/4) { 
    90                    JOptionPane.showMessageDialog(Main.parent, tr("Please select ways with almost right angles to orthogonalize.")); 
    91                    return; 
    92                } 
     82            for (int i1=0; i1 < way.nodes.size()-1; i1++) { 
     83                int i2 = (i1+1) % (way.nodes.size()-1); 
     84                int i3 = (i1+2) % (way.nodes.size()-1); 
     85                double angle1  =Math.abs(way.nodes.get(i1).eastNorth.heading(way.nodes.get(i2).eastNorth)); 
     86                double angle2 = Math.abs(way.nodes.get(i2).eastNorth.heading(way.nodes.get(i3).eastNorth)); 
     87                double delta = Math.abs(angle2 - angle1); 
     88                while(delta > Math.PI) delta -= Math.PI; 
     89                if(delta < Math.PI/4) { 
     90                    JOptionPane.showMessageDialog(Main.parent, tr("Please select ways with almost right angles to orthogonalize.")); 
     91                    return; 
     92                } 
    9393            } 
    9494        } 
     
    9696        if ("EPSG:4326".equals(Main.proj.toString())) { 
    9797            String msg = tr("<html>You are using the EPSG:4326 projection which might lead<br>" + 
    98                "to undesirable results when doing rectangular alignments.<br>" + 
    99                "Change your projection to get rid of this warning.<br>" + 
    100                "Do you want to continue?"); 
     98                    "to undesirable results when doing rectangular alignments.<br>" + 
     99                    "Change your projection to get rid of this warning.<br>" + 
     100                    "Do you want to continue?"); 
    101101 
    102102            if (!DontShowAgainInfo.show("align_rectangular_4326", msg, false)) { 
     
    108108            JOptionPane.showMessageDialog(Main.parent, tr("Only one node selected")); 
    109109            return; 
    110         }  
    111          
     110        } 
     111 
    112112        // Now all checks are done and we can now do the neccessary computations 
    113113        // From here it is assumed that the above checks hold 
    114114        Collection<Command> cmds = new LinkedList<Command>(); 
    115         double align_to_heading; 
    116  
    117          
    118         if(dirnodes.size() == 2) { // When selection contained two nodes, use the nodes to compute a direction to align to 
    119             double heading;         
    120             heading = dirnodes.get(0).eastNorth.heading(dirnodes.get(1).eastNorth); 
    121             while(heading > Math.PI/4) heading -= Math.PI/2; 
    122             align_to_heading=heading; 
    123         } else {   // Otherwise compute the alignment direction from the ways in the collection 
    124         // First, compute the weighted average of the headings of all segments 
    125         double sum_weighted_headings = 0.0; 
    126         double sum_weights = 0.0; 
     115        double align_to_heading = 0.0; 
     116        boolean use_dirnodes = false; 
     117 
     118        if(dirnodes.size() == 2) {  
     119            // When selection contains two nodes, use the nodes to compute a direction  
     120            // to align all ways to 
     121            align_to_heading = normalize_angle(dirnodes.get(0).eastNorth.heading(dirnodes.get(1).eastNorth)); 
     122            use_dirnodes = true; 
     123        } 
     124 
    127125        for (OsmPrimitive osm : sel) { 
    128                 if(!(osm instanceof Way))  
    129                     continue; 
     126            if(!(osm instanceof Way))  
     127                continue; 
     128 
    130129            Way way = (Way)osm; 
    131130            int nodes = way.nodes.size(); 
    132                 int sides = nodes - 1;             
    133                         // To find orientation of all segments, compute weighted average of all segment's headings 
    134             // all headings are mapped into [0, 3*4*PI) by PI/2 rotations so both main orientations are mapped into one 
    135             // the headings are weighted by the length of the segment establishing it, so a longer segment, that is more 
    136             // likely to have the correct orientation, has more influence in the computing than a short segment, that is easier to misalign. 
    137                 for (int i=0; i < sides; i++) { 
    138                 double heading;         
    139                 double weight; 
    140                 heading = way.nodes.get(i).eastNorth.heading(way.nodes.get(i+1).eastNorth); 
    141                 //Put into [0, PI/4) to find main direction 
    142                 while(heading > Math.PI/4) heading -= Math.PI/2; 
    143                 weight = way.nodes.get(i).eastNorth.distance(way.nodes.get(i+1).eastNorth); 
    144                 sum_weighted_headings += heading*weight; 
    145                         sum_weights += weight; 
    146                 } 
    147          } 
    148             align_to_heading = sum_weighted_headings/sum_weights;          
    149         } 
    150          
    151         for (OsmPrimitive osm : sel) {   
    152             if(!(osm instanceof Way))  
    153                 continue; 
    154             Way myWay = (Way)osm; 
    155             int nodes = myWay.nodes.size(); 
    156131            int sides = nodes - 1; 
    157              
    158132            // Copy necessary data into a more suitable data structure 
    159133            EastNorth en[] = new EastNorth[sides]; 
    160             for (int i=0; i < sides; i++) {     
    161                 en[i] = new EastNorth(myWay.nodes.get(i).eastNorth.east(), myWay.nodes.get(i).eastNorth.north()); 
    162             } 
    163   
     134            for (int i=0; i < sides; i++) { 
     135                en[i] = new EastNorth(way.nodes.get(i).eastNorth.east(), way.nodes.get(i).eastNorth.north()); 
     136            } 
     137 
     138            if (! use_dirnodes) { 
     139                // To find orientation of all segments, compute weighted average of all segment's headings 
     140                // all headings are mapped into [-PI/4, PI/4] by PI/2 rotations so both main orientations are mapped into one 
     141                // the headings are weighted by the length of the segment establishing it, so a longer segment, that is more 
     142                // likely to have the correct orientation, has more influence in the computing than a short segment, that is easier to misalign. 
     143                double headings[] = new double[sides]; 
     144                double weights[] = new double[sides]; 
     145                for (int i=0; i < sides; i++) { 
     146                    headings[i] = normalize_angle(way.nodes.get(i).eastNorth.heading(way.nodes.get(i+1).eastNorth)); 
     147                    weights[i] = way.nodes.get(i).eastNorth.distance(way.nodes.get(i+1).eastNorth); 
     148                } 
     149 
     150                // CAVEAT: for orientations near -PI/4 or PI/4 the mapping into ONE orientation fails 
     151                //         resulting in a heading-difference between adjacent sides of almost PI/2 
     152                //         and a totally wrong average 
     153                // check for this (use PI/3 as arbitray limit) and rotate into ONE orientation 
     154                double angle_diff_max = 0.0; 
     155                for (int i=0; i < sides; i++) { 
     156                    double diff = 0.0; 
     157                    if (i == 0) { 
     158                        diff = heading_diff(headings[i], headings[sides - 1]); 
     159                    } else { 
     160                        diff = heading_diff(headings[i], headings[i - 1]); 
     161                    } 
     162                    if (diff > angle_diff_max) angle_diff_max = diff; 
     163                } 
     164 
     165                if (angle_diff_max > Math.PI/3) { 
     166                    // rearrange headings: everything < 0 gets PI/2-rotated  
     167                    for (int i=0; i < sides; i++) { 
     168                        if (headings[i] < 0)  
     169                            headings[i] += Math.PI/2; 
     170                    } 
     171                } 
     172 
     173                // TODO: 
     174                // use angle_diff_max as an indicator that the way is already orthogonal 
     175                // e.g. if angle_diff_max is less then Math.toRadians(0.5) 
     176                // and do nothing in that case (?) 
     177 
     178                // Compute the weighted average of the headings of all segments 
     179                double sum_weighted_headings = 0.0; 
     180                double sum_weights = 0.0; 
     181                for (int i=0; i < sides; i++) { 
     182                    sum_weighted_headings += headings[i] * weights[i]; 
     183                    sum_weights += weights[i]; 
     184                } 
     185                align_to_heading = normalize_angle(sum_weighted_headings/sum_weights);  
     186            } 
     187 
     188 
    164189            for (int i=0; i < sides; i++) { 
    165190                // Compute handy indices of three nodes to be used in one loop iteration.  
     
    173198                double delta1, delta2; 
    174199                // Compute neccessary rotation of first segment to align it with main orientation 
    175                 heading1 = en[i1].heading(en[i2]); 
    176                 //Put into [-PI/4, PI/4) because we want a minimum of rotation so we don't swap node positions 
    177                 while(heading1 - align_to_heading > Math.PI/4) heading1 -= Math.PI/2; 
    178                 while(heading1 - align_to_heading < -Math.PI/4) heading1 += Math.PI/2; 
     200                heading1 = normalize_angle(en[i1].heading(en[i2]), align_to_heading); 
    179201                delta1 = align_to_heading - heading1; 
    180202                // Compute neccessary rotation of second segment to align it with main orientation 
    181                 heading2 = en[i2].heading(en[i3]); 
    182                 //Put into [-PI/4, PI/4) because we want a minimum of rotation so we don't swap node positions 
    183                 while(heading2 - align_to_heading > Math.PI/4) heading2 -= Math.PI/2; 
    184                 while(heading2 - align_to_heading < -Math.PI/4) heading2 += Math.PI/2; 
     203                heading2 = normalize_angle(en[i2].heading(en[i3]), align_to_heading); 
    185204                delta2 = align_to_heading - heading2; 
    186205                // To align a segment, rotate around its center 
     
    194213                // compute intersection of segments 
    195214                double u=det(B.east() - A.east(), B.north() - A.north(),  
    196                     C.east() - D.east(), C.north() - D.north()); 
    197                  
     215                        C.east() - D.east(), C.north() - D.north()); 
     216 
    198217                // Check for parallel segments and do nothing if they are 
    199218                // In practice this will probably only happen when a way has  
    200219                // been duplicated 
    201                  
     220 
    202221                if (u == 0) continue; 
    203                  
     222 
    204223                // q is a number between 0 and 1 
    205224                // It is the point in the segment where the intersection occurs 
    206225                // if the segment is scaled to length 1 
    207                  
     226 
    208227                double q = det(B.north() - C.north(), B.east() - C.east(),  
    209                     D.north() - C.north(), D.east() - C.east()) / u; 
     228                        D.north() - C.north(), D.east() - C.east()) / u; 
    210229                EastNorth intersection = new EastNorth( 
    211230                        B.east() + q * (A.east() - B.east()), 
    212231                        B.north() + q * (A.north() - B.north())); 
    213      
    214                 Node n = myWay.nodes.get(i2); 
     232 
     233                Node n = way.nodes.get(i2); 
    215234 
    216235                LatLon ill = Main.proj.eastNorth2latlon(intersection); 
     
    218237                    double dx = intersection.east()-n.eastNorth.east(); 
    219238                    double dy = intersection.north()-n.eastNorth.north(); 
    220                     cmds.add(new MoveCommand(n, dx, dy));         
    221                 } 
    222             }       
    223         } 
    224          
     239                    cmds.add(new MoveCommand(n, dx, dy)); 
     240                } 
     241            }   
     242        } 
     243 
    225244        if (cmds.size() > 0) { 
    226245            Main.main.undoRedo.add(new SequenceCommand(tr("Orthogonalize"), cmds)); 
    227246            Main.map.repaint(); 
    228247        } 
    229         } 
    230      
     248    } 
     249 
    231250    static double det(double a, double b, double c, double d) 
    232251    { 
     
    234253    } 
    235254 
     255    static double normalize_angle(double h) { 
     256        return normalize_angle(h, 0.0); 
     257    } 
     258    static double normalize_angle(double h, double align_to) { 
     259        double llimit = -Math.PI/4; 
     260        double ulimit = Math.PI/4; 
     261        while (h - align_to > ulimit) h -= Math.PI/2; 
     262        while (h - align_to < llimit) h += Math.PI/2; 
     263 
     264        return h; 
     265    } 
     266 
     267    static double heading_diff(double h1, double h2) { 
     268        double heading_delta = h1 > h2 ? h1 - h2 : h2 - h1; 
     269        return heading_delta; 
     270    } 
    236271} 
Note: See TracChangeset for help on using the changeset viewer.