[OSM-dev] JOSM patch: align nodes in a circle
matthew-osm at newtoncomputing.co.uk
matthew-osm at newtoncomputing.co.uk
Sat Sep 16 01:27:42 BST 2006
Hi,
I've put together a patch for JOSM that will align the selected nodes onto a
circle. It is intended for careful use on roundabouts. Of course, not all
roundabouts are circular, but the user has to determine if this feature is
applicable :-).
The algorithm is quite straightforward:
1. find average position of selected nodes ("C")
2. find average distance from all selected nodes to "C"
3. move each node to average distance from "C", at same angle as before
It does not move nodes around the circle, it just moves them on to it. Therefore
it is reasonably safe from moving roads that join a roundabout.
This is very much a "firsts" e-mail. First patch to JOSM, first code written in
Java, first post to the dev mailing list. Hope I'm doing things right :-).
Can't upload the patch to the trac server as I get an Internal Server Error when
I try and log in, so I've attached it to this e-mail.
Known issues:
* No icon; using the "clock" icon temporarily (it's round ;)).
* No translatons; sorry, I only speak English. Hope I've done the code right.
* I might have done things in ways that can be improved on a lot!
* "Align Nodes in Circle" sounds a bit long-winded. "Circularise" is shorter,
but not a real word as far as I know. Any suggestions?!
The patch was originally based on the reverse segments function.
Thanks!
--
Matthew
-------------- next part --------------
Index: src/org/openstreetmap/josm/actions/AlignInCircleAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/AlignInCircleAction.java (revision 0)
+++ src/org/openstreetmap/josm/actions/AlignInCircleAction.java (revision 0)
@@ -0,0 +1,93 @@
+/**
+ *
+ */
+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.AddCommand;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.command.MoveCommand;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Segment;
+
+public final class AlignInCircleAction extends JosmAction {
+
+ public AlignInCircleAction() {
+ super(tr("Align Nodes in Circle"), "clock", tr("Move the selected nodes into a circle."), KeyEvent.VK_O, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ Collection<OsmPrimitive> sel = Main.ds.getSelected();
+ int hasNodes = 0;
+ for (OsmPrimitive osm : sel) {
+ if (osm instanceof Node) {
+ hasNodes++;
+ }
+ }
+ if (hasNodes < 2) {
+ JOptionPane.showMessageDialog(Main.parent, tr("Please select at least two nodes."));
+ return;
+ }
+ Collection<Command> cmds = new LinkedList<Command>();
+
+ // Get average position of all nodes
+ Node avn = new Node(new LatLon(0,0));
+ int nodecount = 0;
+ for (OsmPrimitive osm : sel) {
+ if (!(osm instanceof Node))
+ continue;
+ Node n = (Node)osm;
+ avn.eastNorth = new EastNorth(avn.eastNorth.east()+n.eastNorth.east(), avn.eastNorth.north()+n.eastNorth.north());
+ avn.coor = Main.proj.eastNorth2latlon(avn.eastNorth);
+ nodecount++;
+ }
+ avn.eastNorth = new EastNorth(avn.eastNorth.east()/nodecount, avn.eastNorth.north()/nodecount);
+ avn.coor = Main.proj.eastNorth2latlon(avn.eastNorth);
+ // Node "avn" now is central to all selected nodes.
+ // Not normally needed, but can be "inserted" to see where
+ // the centre is:
+ // cmds.add(new AddCommand(avn));
+
+ // Now calculate the average distance to each node from the
+ // centre.
+ double avdist = 0;
+ for (OsmPrimitive osm : sel) {
+ if (!(osm instanceof Node))
+ continue;
+ Node n = (Node)osm;
+ avdist += Math.sqrt(avn.eastNorth.distance(n.eastNorth));
+ }
+ avdist = avdist / nodecount;
+
+ // Move each node to that distance from the centre.
+ for (OsmPrimitive osm : sel) {
+ if (!(osm instanceof Node))
+ continue;
+ Node n = (Node)osm;
+ double dx = n.eastNorth.east() - avn.eastNorth.east();
+ double dy = n.eastNorth.north() - avn.eastNorth.north();
+ double dist = Math.sqrt(avn.eastNorth.distance(n.eastNorth));
+ Collection<OsmPrimitive> nodesel = new LinkedList<OsmPrimitive>();
+ nodesel.add(n);
+ cmds.add(new MoveCommand(nodesel, (dx * (avdist / dist)) - dx, (dy * (avdist / dist)) - dy));
+ }
+
+ Main.main.editLayer().add(new SequenceCommand(tr("Align Nodes in Circle"), cmds));
+ Main.ds.setSelected(avn);
+ Main.map.repaint();
+ }
+}
Index: src/org/openstreetmap/josm/Main.java
===================================================================
--- src/org/openstreetmap/josm/Main.java (revision 141)
+++ src/org/openstreetmap/josm/Main.java (working copy)
@@ -29,6 +29,7 @@
import javax.swing.UIManager;
import org.openstreetmap.josm.actions.AboutAction;
+import org.openstreetmap.josm.actions.AlignInCircleAction;
import org.openstreetmap.josm.actions.DownloadAction;
import org.openstreetmap.josm.actions.ExitAction;
import org.openstreetmap.josm.actions.GpxExportAction;
@@ -162,6 +163,7 @@
annotationTesterAction.putValue(Action.NAME, tr("Annotation Preset Tester"));
annotationTesterAction.putValue(Action.SMALL_ICON, ImageProvider.get("annotation-tester"));
final Action reverseSegmentAction = new ReverseSegmentAction();
+ final Action alignInCircleAction = new AlignInCircleAction();
final Action uploadAction = new UploadAction();
final Action saveAction = new SaveAction();
final Action saveAsAction = new SaveAsAction();
@@ -199,6 +201,7 @@
editMenu.add(redoAction);
editMenu.addSeparator();
editMenu.add(reverseSegmentAction);
+ editMenu.add(alignInCircleAction);
editMenu.addSeparator();
editMenu.add(preferencesAction);
mainMenu.add(editMenu);
@@ -225,6 +228,7 @@
toolBar.add(redoAction);
toolBar.addSeparator();
toolBar.add(reverseSegmentAction);
+ toolBar.add(alignInCircleAction);
toolBar.addSeparator();
toolBar.add(preferencesAction);
contentPane.add(toolBar, BorderLayout.NORTH);
More information about the dev
mailing list