// License: GPL. Copyright 2007 by Immanuel Scholz and others package org.openstreetmap.josm.actions; import static org.openstreetmap.josm.tools.I18n.tr; import static org.openstreetmap.josm.gui.help.HelpUtil.ht; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.Collection; import java.util.LinkedList; import javax.swing.JOptionPane; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.command.Command; import org.openstreetmap.josm.command.MoveCommand; import org.openstreetmap.josm.command.SequenceCommand; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.tools.Shortcut; /** * Aligns all selected nodes into a straight line (useful for * roads that should be straight, but have side roads and * therefore need multiple nodes) * * @author Matthew Newton */ public final class AlignInLineAction extends JosmAction { public AlignInLineAction() { super(tr("Align Nodes in Line"), "alignline", tr("Move the selected nodes in to a line."), Shortcut.registerShortcut("tools:alignline", tr("Tool: {0}", tr("Align Nodes in Line")), KeyEvent.VK_L, Shortcut.GROUP_EDIT), true); putValue("help", ht("/Action/AlignInLine")); } /** * The general algorithm here is to find the two selected nodes * that are furthest apart, and then to align all other selected * nodes onto the straight line between these nodes. */ public void actionPerformed(ActionEvent e) { if (!isEnabled()) return; Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected(); Collection<Node> nodes = new LinkedList<Node>(); Collection<Node> itnodes = new LinkedList<Node>(); for (OsmPrimitive osm : sel) if (osm instanceof Node) { nodes.add((Node)osm); itnodes.add((Node)osm); } // special case if no single nodes are selected and exactly one way is: // then use the way's nodes if ((nodes.size() == 0) && (sel.size() == 1)) { for (OsmPrimitive osm : sel) if (osm instanceof Way) { nodes.addAll(((Way)osm).getNodes()); itnodes.addAll(((Way)osm).getNodes()); } } if (nodes.size() < 3) { JOptionPane.showMessageDialog( Main.parent, tr("Please select at least three nodes."), tr("Information"), JOptionPane.INFORMATION_MESSAGE ); return; } // Find from the selected nodes two that are the furthest apart. // Let's call them A and B. double distance = 0; Node nodea = null; Node nodeb = null; for (Node n : nodes) { itnodes.remove(n); for (Node m : itnodes) { double dist = Math.sqrt(n.getEastNorth().distance(m.getEastNorth())); if (dist > distance) { nodea = n; nodeb = m; distance = dist; } } } // Remove the nodes A and B from the list of nodes to move nodes.remove(nodea); nodes.remove(nodeb); // Find out co-ords of A and B double ax = nodea.getEastNorth().east(); double ay = nodea.getEastNorth().north(); double bx = nodeb.getEastNorth().east(); double by = nodeb.getEastNorth().north(); // A list of commands to do Collection<Command> cmds = new LinkedList<Command>(); // OK, for each node to move, work out where to move it! for (Node n : nodes) { // Get existing co-ords of node to move double nx = n.getEastNorth().east(); double ny = n.getEastNorth().north(); if (ax == bx) { // Special case if AB is vertical... nx = ax; } else if (ay == by) { // ...or horizontal ny = ay; } else { // Otherwise calculate position by solving y=mx+c double m1 = (by - ay) / (bx - ax); double c1 = ay - (ax * m1); double m2 = (-1) / m1; double c2 = n.getEastNorth().north() - (n.getEastNorth().east() * m2); nx = (c2 - c1) / (m1 - m2); ny = (m1 * nx) + c1; } // Add the command to move the node to its new position. cmds.add(new MoveCommand(n, nx - n.getEastNorth().east(), ny - n.getEastNorth().north() )); } // Do it! Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds)); Main.map.repaint(); } @Override protected void updateEnabledState() { setEnabled(getCurrentDataSet() != null && !getCurrentDataSet().getSelected().isEmpty()); } @Override protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) { setEnabled(selection != null && !selection.isEmpty()); } }