Changeset 1741 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2009-07-06T17:53:10+02:00 (15 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.