[OSM-dev] Two patches for JOSM - Align in {circle,line}

matthew-osm at newtoncomputing.co.uk matthew-osm at newtoncomputing.co.uk
Mon Oct 23 03:27:14 BST 2006


Used the wrong from address... try again ;-)

----- Forwarded message from Me -----

To: Immanuel Scholz <immanuel.scholz at gmx.de>
Cc: dev at openstreetmap.org, clive at tux-it.co.uk
Subject: Two patches for JOSM - Align in {circle,line}

Hi Imi,

I've created two patches for JOSM here. The first is a very small bugfix for my
align-in-circle patch that stops the selection being cleared at the end of the
function. This was a mistake left over from some debug code. It is in the
attached josm166_circle_bugfix.patch file, and uploaded to your trac, number 39.

The second patch is for a new similar feature that aligns nodes into a straight
line. This is intended for (careful) use when a straight road has side-roads
leading off. The additional nodes along the straight road that are necessary for
the side-roads can cause the straight road to become non-straight very easily.
This feature moves all nodes that are between the two most-distant nodes onto
the line between the most distant nodes.

How's that for a badly worded explanation ;-).

Anyway, attached as josm166_align_in_line.patch and uploaded to your trac,
number 40. You might find a better way than the current double loop... I'd do

  for (a=0; a < nodes.size() - 1; a++) {
    for (b = a + 1; b < nodes.size(); b++) {
      /* use nodes[a] and nodes[b] */
      ...
    }
  }

in C, but I can't work out how to do it in Java. Therefore I added all nodes to
two collections, "nodes" and "itnodes", and then removed nodes from "itnodes"
one by one in the outer loop. I don't like it, as it's a waste of memory, but
I am sure you know a much better way of doing it!

Both patches apply to revision 166 in SVN.

Hope they're OK! *

Thanks,

-- 
Matthew


* Sorry, checking now and I think some tabs slipped through, so indentation may
  be a bit iffy.

-------------- next part --------------
Index: src/org/openstreetmap/josm/actions/AlignInLineAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/AlignInLineAction.java	(revision 0)
+++ src/org/openstreetmap/josm/actions/AlignInLineAction.java	(revision 0)
@@ -0,0 +1,117 @@
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+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.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * 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"), "clock", tr("Move the selected nodes onto a line."), KeyEvent.VK_L, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK);
+	}
+
+	public void actionPerformed(ActionEvent e) {
+/**
+ * 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.
+ */
+		Collection<OsmPrimitive> sel = Main.ds.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);
+      }
+		if (nodes.size() < 3) {
+			JOptionPane.showMessageDialog(Main.parent, tr("Please select at least three nodes."));
+			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.eastNorth.distance(m.eastNorth));
+          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.eastNorth.east();
+    double ay = nodea.eastNorth.north();
+    double bx = nodeb.eastNorth.east();
+    double by = nodeb.eastNorth.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.eastNorth.east();
+      double ny = n.eastNorth.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.eastNorth.north() - (n.eastNorth.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.eastNorth.east(), ny - n.eastNorth.north() ));
+    }
+
+    // Do it!
+		Main.main.editLayer().add(new SequenceCommand(tr("Align Nodes in Line"), cmds));
+
+		Main.map.repaint();
+	}
+}
Index: src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- src/org/openstreetmap/josm/gui/MainMenu.java	(revision 166)
+++ src/org/openstreetmap/josm/gui/MainMenu.java	(working copy)
@@ -9,6 +9,7 @@
 
 import org.openstreetmap.josm.actions.AboutAction;
 import org.openstreetmap.josm.actions.AlignInCircleAction;
+import org.openstreetmap.josm.actions.AlignInLineAction;
 import org.openstreetmap.josm.actions.DownloadAction;
 import org.openstreetmap.josm.actions.DownloadIncompleteAction;
 import org.openstreetmap.josm.actions.ExitAction;
@@ -40,6 +41,7 @@
 	public final DownloadAction download = new DownloadAction();
 	public final Action reverseSegment = new ReverseSegmentAction();
 	public final Action alignInCircle = new AlignInCircleAction();
+	public final Action alignInLine = new AlignInLineAction();
 	public final Action upload = new UploadAction();
 	public final Action save = new SaveAction();
 	public final Action saveAs = new SaveAsAction();
@@ -75,6 +77,7 @@
 		editMenu.addSeparator();
 		editMenu.add(reverseSegment);
 		editMenu.add(alignInCircle);
+		editMenu.add(alignInLine);
 		editMenu.addSeparator();
 		editMenu.add(preferences);
 		add(editMenu);
-------------- next part --------------
Index: src/org/openstreetmap/josm/actions/AlignInCircleAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/AlignInCircleAction.java	(revision 166)
+++ src/org/openstreetmap/josm/actions/AlignInCircleAction.java	(working copy)
@@ -67,7 +67,6 @@
 		}
 
 		Main.main.editLayer().add(new SequenceCommand(tr("Align Nodes in Circle"), cmds));
-		Main.ds.setSelected(avn);
 		Main.map.repaint();
 	}
 }


More information about the dev mailing list