source: josm/trunk/src/org/openstreetmap/josm/actions/DistributeAction.java@ 6272

Last change on this file since 6272 was 6130, checked in by bastiK, 11 years ago

see #6963 - converted popups to notifications for all actions in the Tools menu (TODO: Simplify Way)

  • Property svn:eol-style set to native
File size: 5.9 KB
Line 
1// License: GPL. Copyright 2009 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.event.ActionEvent;
8import java.awt.event.KeyEvent;
9import java.util.Collection;
10import java.util.HashSet;
11import java.util.Iterator;
12import java.util.LinkedList;
13import java.util.Set;
14
15import javax.swing.JOptionPane;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.command.Command;
19import org.openstreetmap.josm.command.MoveCommand;
20import org.openstreetmap.josm.command.SequenceCommand;
21import org.openstreetmap.josm.data.osm.Node;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.Way;
24import org.openstreetmap.josm.gui.Notification;
25import org.openstreetmap.josm.tools.Shortcut;
26
27/**
28 * Distributes the selected nodes to equal distances along a line.
29 *
30 * @author Teemu Koskinen
31 */
32public final class DistributeAction extends JosmAction {
33
34 /**
35 * Constructs a new {@code DistributeAction}.
36 */
37 public DistributeAction() {
38 super(tr("Distribute Nodes"), "distribute", tr("Distribute the selected nodes to equal distances along a line."),
39 Shortcut.registerShortcut("tools:distribute", tr("Tool: {0}", tr("Distribute Nodes")), KeyEvent.VK_B,
40 Shortcut.SHIFT), true);
41 putValue("help", ht("/Action/DistributeNodes"));
42 }
43
44 /**
45 * The general algorithm here is to find the two selected nodes
46 * that are furthest apart, and then to distribute all other selected
47 * nodes along the straight line between these nodes.
48 */
49 @Override
50 public void actionPerformed(ActionEvent e) {
51 if (!isEnabled())
52 return;
53 Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected();
54 Collection<Node> nodes = new LinkedList<Node>();
55 Collection<Node> itnodes = new LinkedList<Node>();
56 for (OsmPrimitive osm : sel)
57 if (osm instanceof Node) {
58 nodes.add((Node)osm);
59 itnodes.add((Node)osm);
60 }
61 // special case if no single nodes are selected and exactly one way is:
62 // then use the way's nodes
63 if (nodes.isEmpty() && (sel.size() == 1)) {
64 for (OsmPrimitive osm : sel)
65 if (osm instanceof Way) {
66 nodes.addAll(((Way)osm).getNodes());
67 itnodes.addAll(((Way)osm).getNodes());
68 }
69 }
70
71 Set<Node> ignoredNodes = removeNodesWithoutCoordinates(nodes);
72 ignoredNodes.addAll(removeNodesWithoutCoordinates(itnodes));
73 if (!ignoredNodes.isEmpty()) {
74 Main.warn(tr("Ignoring {0} nodes with null coordinates", ignoredNodes.size()));
75 ignoredNodes.clear();
76 }
77
78 if (nodes.size() < 3) {
79 new Notification(
80 tr("Please select at least three nodes."))
81 .setIcon(JOptionPane.INFORMATION_MESSAGE)
82 .setDuration(Notification.TIME_SHORT)
83 .show();
84 return;
85 }
86
87 // Find from the selected nodes two that are the furthest apart.
88 // Let's call them A and B.
89 double distance = 0;
90
91 Node nodea = null;
92 Node nodeb = null;
93
94 for (Node n : nodes) {
95 itnodes.remove(n);
96 for (Node m : itnodes) {
97 double dist = Math.sqrt(n.getEastNorth().distance(m.getEastNorth()));
98 if (dist > distance) {
99 nodea = n;
100 nodeb = m;
101 distance = dist;
102 }
103 }
104 }
105
106 // Remove the nodes A and B from the list of nodes to move
107 nodes.remove(nodea);
108 nodes.remove(nodeb);
109
110 // Find out co-ords of A and B
111 double ax = nodea.getEastNorth().east();
112 double ay = nodea.getEastNorth().north();
113 double bx = nodeb.getEastNorth().east();
114 double by = nodeb.getEastNorth().north();
115
116 // A list of commands to do
117 Collection<Command> cmds = new LinkedList<Command>();
118
119 // Amount of nodes between A and B plus 1
120 int num = nodes.size()+1;
121
122 // Current number of node
123 int pos = 0;
124 while (!nodes.isEmpty()) {
125 pos++;
126 Node s = null;
127
128 // Find the node that is furthest from B (i.e. closest to A)
129 distance = 0.0;
130 for (Node n : nodes) {
131 double dist = Math.sqrt(nodeb.getEastNorth().distance(n.getEastNorth()));
132 if (dist > distance) {
133 s = n;
134 distance = dist;
135 }
136 }
137
138 // First move the node to A's position, then move it towards B
139 double dx = ax - s.getEastNorth().east() + (bx-ax)*pos/num;
140 double dy = ay - s.getEastNorth().north() + (by-ay)*pos/num;
141
142 cmds.add(new MoveCommand(s, dx, dy));
143
144 //remove moved node from the list
145 nodes.remove(s);
146 }
147
148 // Do it!
149 Main.main.undoRedo.add(new SequenceCommand(tr("Distribute Nodes"), cmds));
150 Main.map.repaint();
151 }
152
153 private Set<Node> removeNodesWithoutCoordinates(Collection<Node> col) {
154 Set<Node> result = new HashSet<Node>();
155 for (Iterator<Node> it = col.iterator(); it.hasNext();) {
156 Node n = it.next();
157 if (n.getCoor() == null) {
158 it.remove();
159 result.add(n);
160 }
161 }
162 return result;
163 }
164
165 @Override
166 protected void updateEnabledState() {
167 if (getCurrentDataSet() == null) {
168 setEnabled(false);
169 } else {
170 updateEnabledState(getCurrentDataSet().getSelected());
171 }
172 }
173
174 @Override
175 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
176 setEnabled(selection != null && !selection.isEmpty());
177 }
178}
Note: See TracBrowser for help on using the repository browser.