Changeset 1741 in josm


Ignore:
Timestamp:
Jul 6, 2009 5:53:10 PM (4 years ago)
Author:
stoecker
Message:

fixed #2847 - patch by Landwirt - better circle creation using two point to specify diameter

File:
1 edited

Legend:

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

    r1640 r1741  
    1414import org.openstreetmap.josm.command.Command; 
    1515import org.openstreetmap.josm.command.AddCommand; 
     16import org.openstreetmap.josm.command.DeleteCommand; 
    1617import org.openstreetmap.josm.command.ChangeCommand; 
    1718import org.openstreetmap.josm.command.SequenceCommand; 
     
    2223import org.openstreetmap.josm.tools.Shortcut; 
    2324 
     25import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor; 
     26 
    2427/** 
    25  * Create a new circle from three selected nodes--or a way with 3 nodes. (Useful for roundabouts) 
     28 * - Create a new circle from two selected nodes or a way with 2 nodes which represent the diameter of the circle. 
     29 * - Create a new circle from three selected nodes--or a way with 3 nodes. 
     30 * - Useful for roundabouts 
    2631 * 
    2732 * Note: If a way is selected, it is changed. If nodes are selected a new way is created. 
    28  *       So if you've got a way with 3 nodes it makes a difference between running this on the way or the nodes! 
     33 *       So if you've got a way with nodes it makes a difference between running this on the way or the nodes! 
    2934 * 
    3035 * BTW: Someone might want to implement projection corrections for this... 
    3136 * 
    3237 * @author Henry Loenwind, based on much copy&Paste from other Actions. 
     38 * @author Sebastian Masch 
    3339 */ 
    3440public final class CreateCircleAction extends JosmAction { 
     
    3642    public CreateCircleAction() { 
    3743        super(tr("Create Circle"), "createcircle", tr("Create a circle from three selected nodes."), 
    38         Shortcut.registerShortcut("tools:createcircle", tr("Tool: {0}", tr("Create Circle")), KeyEvent.VK_O, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true); 
     44                Shortcut.registerShortcut("tools:createcircle", tr("Tool: {0}", tr("Create Circle")), KeyEvent.VK_O, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true); 
    3945    } 
    4046 
    4147    private double calcang(double xc, double yc, double x, double y) { 
    4248        // calculate the angle from xc|yc to x|y 
    43         if (xc == x && yc == y) { 
     49        if (xc == x && yc == y) 
    4450            return 0; // actually invalid, but we won't have this case in this context 
    45         } 
    4651        double yd = Math.abs(y - yc); 
    47         if (yd == 0 && xc < x) { 
     52        if (yd == 0 && xc < x) 
    4853            return 0; 
    49         } 
    50         if (yd == 0 && xc > x) { 
     54        if (yd == 0 && xc > x) 
    5155            return Math.PI; 
    52         } 
    5356        double xd = Math.abs(x - xc); 
    5457        double a = Math.atan2(xd, yd); 
     
    8285 
    8386        for (OsmPrimitive osm : sel) 
    84             if (osm instanceof Node) 
     87            if (osm instanceof Node) { 
    8588                nodes.add((Node)osm); 
     89            } 
    8690 
    8791        // special case if no single nodes are selected and exactly one way is: 
    8892        // then use the way's nodes 
    89         if ((nodes.size() == 0) && (sel.size() == 1)) 
     93        if ((nodes.size() == 0) && (sel.size() == 1)) { 
    9094            for (OsmPrimitive osm : sel) 
    9195                if (osm instanceof Way) { 
     
    9397                    for (Node n : ((Way)osm).nodes) 
    9498                    { 
    95                         if(!nodes.contains(n)) 
     99                        if(!nodes.contains(n)) { 
    96100                            nodes.add(n); 
     101                        } 
    97102                    } 
    98103                } 
    99  
    100         if (nodes.size() != 3) { 
    101             JOptionPane.showMessageDialog(Main.parent, tr("Please select exactly three nodes or one way with exactly three nodes.")); 
     104        } 
     105 
     106        // now we can start doing things to OSM data 
     107        Collection<Command> cmds = new LinkedList<Command>(); 
     108 
     109        if (nodes.size() == 2) { 
     110            // diameter: two single nodes needed or a way with two nodes 
     111 
     112            Node   n1 = ((Node)nodes.toArray()[0]); 
     113            double x1 = n1.getEastNorth().east(); 
     114            double y1 = n1.getEastNorth().north(); 
     115            Node   n2 = ((Node)nodes.toArray()[1]); 
     116            double x2 = n2.getEastNorth().east(); 
     117            double y2 = n2.getEastNorth().north(); 
     118 
     119            // calculate the center (xc/yc) 
     120            double xc = 0.5 * (x1 + x2); 
     121            double yc = 0.5 * (y1 + y2); 
     122 
     123            // calculate the radius (r) 
     124            double r = Math.sqrt(Math.pow(xc-x1,2) + Math.pow(yc-y1,2)); 
     125 
     126            // find where to put the existing nodes 
     127            double a1 = calcang(xc, yc, x1, y1); 
     128            double a2 = calcang(xc, yc, x2, y2); 
     129            if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 
     130 
     131            // build a way for the circle 
     132            Way wayToAdd; 
     133            if (existingWay == null) { 
     134                wayToAdd = new Way(); 
     135            } else { 
     136                // re-use existing way if it was selected 
     137                wayToAdd = new Way(existingWay); 
     138                wayToAdd.nodes.clear(); 
     139            } 
     140 
     141            for (int i = 1; i <= numberOfNodesInCircle; i++) { 
     142                double a = a2 + 2*Math.PI*(1.0 - i/(double)numberOfNodesInCircle); // "1-" to get it clock-wise 
     143 
     144                // insert existing nodes if they fit before this new node (999 means "already added this node") 
     145                if ((a1 < 999) && (a1 > a - 1E-9) && (a1 < a + 1E-9)) { 
     146                    wayToAdd.nodes.add(n1); 
     147                    a1 = 999; 
     148                } 
     149                else if ((a2 < 999) && (a2 > a - 1E-9) && (a2 < a + 1E-9)) { 
     150                    wayToAdd.nodes.add(n2); 
     151                    a2 = 999; 
     152                } 
     153                else { 
     154                    // get the position of the new node and insert it 
     155                    double x = xc + r*Math.cos(a); 
     156                    double y = yc + r*Math.sin(a); 
     157                    Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y))); 
     158                    wayToAdd.nodes.add(n); 
     159                    cmds.add(new AddCommand(n)); 
     160                } 
     161            } 
     162            wayToAdd.nodes.add(wayToAdd.nodes.get(0)); // close the circle 
     163            if (existingWay == null) { 
     164                cmds.add(new AddCommand(wayToAdd)); 
     165            } else { 
     166                cmds.add(new ChangeCommand(existingWay, wayToAdd)); 
     167            } 
     168 
     169            // the first node may be unused/abandoned if createcircle.nodecount is odd 
     170            if (a1 < 999) { 
     171                // if it is, delete it 
     172                CollectBackReferencesVisitor refs = new CollectBackReferencesVisitor(Main.ds); 
     173                refs.visit(n1); 
     174                if (refs.data.isEmpty() || ((refs.data.size() == 1) && (refs.data.contains(existingWay)))) { 
     175                    cmds.add(new DeleteCommand(n1)); 
     176                } 
     177 
     178                // or insert it 
     179                // wayToAdd.nodes.add((numberOfNodesInCircle - 1) / 2, n1); 
     180            } 
     181 
     182        } else if (nodes.size() == 3) { 
     183            // triangle: three single nodes needed or a way with three nodes 
     184 
     185            // let's get some shorter names 
     186            Node   n1 = ((Node)nodes.toArray()[0]); 
     187            double x1 = n1.getEastNorth().east(); 
     188            double y1 = n1.getEastNorth().north(); 
     189            Node   n2 = ((Node)nodes.toArray()[1]); 
     190            double x2 = n2.getEastNorth().east(); 
     191            double y2 = n2.getEastNorth().north(); 
     192            Node   n3 = ((Node)nodes.toArray()[2]); 
     193            double x3 = n3.getEastNorth().east(); 
     194            double y3 = n3.getEastNorth().north(); 
     195 
     196            // calculate the center (xc/yc) 
     197            double s = 0.5*((x2 - x3)*(x1 - x3) - (y2 - y3)*(y3 - y1)); 
     198            double sUnder = (x1 - x2)*(y3 - y1) - (y2 - y1)*(x1 - x3); 
     199 
     200            if (sUnder == 0) { 
     201                JOptionPane.showMessageDialog(Main.parent, tr("Those nodes are not in a circle.")); 
     202                return; 
     203            } 
     204 
     205            s /= sUnder; 
     206 
     207            double xc = 0.5*(x1 + x2) + s*(y2 - y1); 
     208            double yc = 0.5*(y1 + y2) + s*(x1 - x2); 
     209 
     210            // calculate the radius (r) 
     211            double r = Math.sqrt(Math.pow(xc-x1,2) + Math.pow(yc-y1,2)); 
     212 
     213            // find where to put the existing nodes 
     214            double a1 = calcang(xc, yc, x1, y1); 
     215            double a2 = calcang(xc, yc, x2, y2); 
     216            double a3 = calcang(xc, yc, x3, y3); 
     217            if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 
     218            if (a2 < a3) { double at = a2; Node nt = n2; a2 = a3; n2 = n3; a3 = at; n3 = nt; } 
     219            if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 
     220 
     221            // build a way for the circle 
     222            Way wayToAdd; 
     223            if (existingWay == null) { 
     224                wayToAdd = new Way(); 
     225            } else { 
     226                // re-use existing way if it was selected 
     227                wayToAdd = new Way(existingWay); 
     228                wayToAdd.nodes.clear(); 
     229            } 
     230            for (int i = 1; i <= numberOfNodesInCircle; i++) { 
     231                double a = 2*Math.PI*(1.0 - i/(double)numberOfNodesInCircle); // "1-" to get it clock-wise 
     232                // insert existing nodes if they fit before this new node (999 means "already added this node") 
     233                if (a1 < 999 && a1 > a) { 
     234                    wayToAdd.nodes.add(n1); 
     235                    a1 = 999; 
     236                } 
     237                if (a2 < 999 && a2 > a) { 
     238                    wayToAdd.nodes.add(n2); 
     239                    a2 = 999; 
     240                } 
     241                if (a3 < 999 && a3 > a) { 
     242                    wayToAdd.nodes.add(n3); 
     243                    a3 = 999; 
     244                } 
     245                // get the position of the new node and insert it 
     246                double x = xc + r*Math.cos(a); 
     247                double y = yc + r*Math.sin(a); 
     248                Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y))); 
     249                wayToAdd.nodes.add(n); 
     250                cmds.add(new AddCommand(n)); 
     251            } 
     252            wayToAdd.nodes.add(wayToAdd.nodes.get(0)); // close the circle 
     253            if (existingWay == null) { 
     254                cmds.add(new AddCommand(wayToAdd)); 
     255            } else { 
     256                cmds.add(new ChangeCommand(existingWay, wayToAdd)); 
     257            } 
     258 
     259        } else { 
     260            JOptionPane.showMessageDialog(Main.parent, tr("Please select exactly two or three nodes or one way with exactly two or three nodes.")); 
    102261            return; 
    103         } 
    104  
    105         // let's get some shorter names 
    106         Node   n1 = ((Node)nodes.toArray()[0]); 
    107         double x1 = n1.getEastNorth().east(); 
    108         double y1 = n1.getEastNorth().north(); 
    109         Node   n2 = ((Node)nodes.toArray()[1]); 
    110         double x2 = n2.getEastNorth().east(); 
    111         double y2 = n2.getEastNorth().north(); 
    112         Node   n3 = ((Node)nodes.toArray()[2]); 
    113         double x3 = n3.getEastNorth().east(); 
    114         double y3 = n3.getEastNorth().north(); 
    115  
    116         // calculate the center (xc/yc) 
    117         double s = 0.5*((x2 - x3)*(x1 - x3) - (y2 - y3)*(y3 - y1)); 
    118         double sUnder = (x1 - x2)*(y3 - y1) - (y2 - y1)*(x1 - x3); 
    119  
    120         if (sUnder == 0) { 
    121             JOptionPane.showMessageDialog(Main.parent, tr("Those nodes are not in a circle.")); 
    122             return; 
    123         } 
    124  
    125         s /= sUnder; 
    126  
    127         double xc = 0.5*(x1 + x2) + s*(y2 - y1); 
    128         double yc = 0.5*(y1 + y2) + s*(x1 - x2); 
    129  
    130         // calculate the radius (r) 
    131         double r = Math.sqrt(Math.pow(xc-x1,2) + Math.pow(yc-y1,2)); 
    132  
    133         // find where to put the existing nodes 
    134         double a1 = calcang(xc, yc, x1, y1); 
    135         double a2 = calcang(xc, yc, x2, y2); 
    136         double a3 = calcang(xc, yc, x3, y3); 
    137         if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 
    138         if (a2 < a3) { double at = a2; Node nt = n2; a2 = a3; n2 = n3; a3 = at; n3 = nt; } 
    139         if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 
    140  
    141         // now we can start doing thigs to OSM data 
    142         Collection<Command> cmds = new LinkedList<Command>(); 
    143  
    144         // build a way for the circle 
    145         Way wayToAdd; 
    146         if (existingWay == null) { 
    147             wayToAdd = new Way(); 
    148         } else { 
    149             // re-use existing way if it was selected 
    150             wayToAdd = new Way(existingWay); 
    151             wayToAdd.nodes.clear(); 
    152         } 
    153         for (int i = 1; i <= numberOfNodesInCircle; i++) { 
    154             double a = 2*Math.PI*(1.0 - i/(double)numberOfNodesInCircle); // "1-" to get it clock-wise 
    155             // insert existing nodes if they fit before this new node (999 means "already added this node") 
    156             if (a1 < 999 && a1 > a) { 
    157                 wayToAdd.nodes.add(n1); 
    158                 a1 = 999; 
    159             } 
    160             if (a2 < 999 && a2 > a) { 
    161                 wayToAdd.nodes.add(n2); 
    162                 a2 = 999; 
    163             } 
    164             if (a3 < 999 && a3 > a) { 
    165                 wayToAdd.nodes.add(n3); 
    166                 a3 = 999; 
    167             } 
    168             // get the position of the new node and insert it 
    169             double x = xc + r*Math.cos(a); 
    170             double y = yc + r*Math.sin(a); 
    171             Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y))); 
    172             wayToAdd.nodes.add(n); 
    173             cmds.add(new AddCommand(n)); 
    174         } 
    175         wayToAdd.nodes.add(wayToAdd.nodes.get(0)); // close the circle 
    176         if (existingWay == null) { 
    177             cmds.add(new AddCommand(wayToAdd)); 
    178         } else { 
    179             cmds.add(new ChangeCommand(existingWay, wayToAdd)); 
    180262        } 
    181263 
Note: See TracChangeset for help on using the changeset viewer.