source: josm/trunk/src/org/openstreetmap/josm/actions/CreateCircleAction.java@ 1640

Last change on this file since 1640 was 1640, checked in by stoecker, 15 years ago

little bit more refactoring of coordinate access - patch by jttt

File size: 6.7 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.KeyEvent;
8import java.util.Collection;
9import java.util.LinkedList;
10
11import javax.swing.JOptionPane;
12
13import org.openstreetmap.josm.Main;
14import org.openstreetmap.josm.command.Command;
15import org.openstreetmap.josm.command.AddCommand;
16import org.openstreetmap.josm.command.ChangeCommand;
17import org.openstreetmap.josm.command.SequenceCommand;
18import org.openstreetmap.josm.data.coor.EastNorth;
19import org.openstreetmap.josm.data.osm.Node;
20import org.openstreetmap.josm.data.osm.OsmPrimitive;
21import org.openstreetmap.josm.data.osm.Way;
22import org.openstreetmap.josm.tools.Shortcut;
23
24/**
25 * Create a new circle from three selected nodes--or a way with 3 nodes. (Useful for roundabouts)
26 *
27 * 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!
29 *
30 * BTW: Someone might want to implement projection corrections for this...
31 *
32 * @author Henry Loenwind, based on much copy&Paste from other Actions.
33 */
34public final class CreateCircleAction extends JosmAction {
35
36 public CreateCircleAction() {
37 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);
39 }
40
41 private double calcang(double xc, double yc, double x, double y) {
42 // calculate the angle from xc|yc to x|y
43 if (xc == x && yc == y) {
44 return 0; // actually invalid, but we won't have this case in this context
45 }
46 double yd = Math.abs(y - yc);
47 if (yd == 0 && xc < x) {
48 return 0;
49 }
50 if (yd == 0 && xc > x) {
51 return Math.PI;
52 }
53 double xd = Math.abs(x - xc);
54 double a = Math.atan2(xd, yd);
55 if (y > yc) {
56 a = Math.PI - a;
57 }
58 if (x < xc) {
59 a = -a;
60 }
61 a = 1.5*Math.PI + a;
62 if (a < 0) {
63 a += 2*Math.PI;
64 }
65 if (a >= 2*Math.PI) {
66 a -= 2*Math.PI;
67 }
68 return a;
69 }
70
71 public void actionPerformed(ActionEvent e) {
72 int numberOfNodesInCircle = Main.pref.getInteger("createcircle.nodecount", 8);
73 if (numberOfNodesInCircle < 1) {
74 numberOfNodesInCircle = 1;
75 } else if (numberOfNodesInCircle > 100) {
76 numberOfNodesInCircle = 100;
77 }
78
79 Collection<OsmPrimitive> sel = Main.ds.getSelected();
80 Collection<Node> nodes = new LinkedList<Node>();
81 Way existingWay = null;
82
83 for (OsmPrimitive osm : sel)
84 if (osm instanceof Node)
85 nodes.add((Node)osm);
86
87 // special case if no single nodes are selected and exactly one way is:
88 // then use the way's nodes
89 if ((nodes.size() == 0) && (sel.size() == 1))
90 for (OsmPrimitive osm : sel)
91 if (osm instanceof Way) {
92 existingWay = ((Way)osm);
93 for (Node n : ((Way)osm).nodes)
94 {
95 if(!nodes.contains(n))
96 nodes.add(n);
97 }
98 }
99
100 if (nodes.size() != 3) {
101 JOptionPane.showMessageDialog(Main.parent, tr("Please select exactly three nodes or one way with exactly three nodes."));
102 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));
180 }
181
182 Main.main.undoRedo.add(new SequenceCommand(tr("Create Circle"), cmds));
183 Main.map.repaint();
184 }
185}
Note: See TracBrowser for help on using the repository browser.