Ticket #5577: intersect_way.patch
| File intersect_way.patch, 24.8 KB (added by , 15 years ago) |
|---|
-
src/org/openstreetmap/josm/actions/AddIntersectionsAction.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.actions; 3 4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 7 import java.awt.event.ActionEvent; 8 import java.awt.event.KeyEvent; 9 import java.awt.geom.Line2D; 10 import java.util.ArrayList; 11 import java.util.Collection; 12 import java.util.Comparator; 13 import java.util.LinkedHashSet; 14 import java.util.LinkedList; 15 import java.util.List; 16 import java.util.Set; 17 18 import javax.swing.JOptionPane; 19 20 import org.openstreetmap.josm.Main; 21 import org.openstreetmap.josm.command.AddCommand; 22 import org.openstreetmap.josm.command.ChangeCommand; 23 import org.openstreetmap.josm.command.Command; 24 import org.openstreetmap.josm.command.SequenceCommand; 25 import org.openstreetmap.josm.data.coor.EastNorth; 26 import org.openstreetmap.josm.data.coor.LatLon; 27 import org.openstreetmap.josm.data.osm.Node; 28 import org.openstreetmap.josm.data.osm.NodePositionComparator; 29 import org.openstreetmap.josm.data.osm.OsmPrimitive; 30 import org.openstreetmap.josm.data.osm.Way; 31 import org.openstreetmap.josm.tools.Shortcut; 32 33 public class AddIntersectionsAction extends JosmAction { 34 public AddIntersectionsAction() { 35 super(tr("Add nodes at intersections"), "addintersect", tr("Add missing nodes at intersections of selected ways."), 36 Shortcut.registerShortcut("tools:addintersect", tr("Tool: {0}", tr("Add nodes at intersections")), KeyEvent.VK_I, Shortcut.GROUP_EDIT), true); 37 putValue("help", ht("/Action/AddIntersections")); 38 } 39 /** 40 * Will find all intersection and add nodes there for list of given ways. Handles self-intersections too. 41 * And make commands to add the intersection points to ways. 42 * @param List<Way> - a list of ways to test 43 * @return ArrayList<Node> List of new nodes 44 * Prerequisite: no two nodes have the same coordinates. 45 */ 46 public static ArrayList<Node> addIntersections(List<Way> ways, boolean test, List<Command> cmds) { 47 //TODO: this is a bit slow - O( (number of nodes)^2 + numberOfIntersections * numberOfNodes ) 48 49 //stupid java, cannot instantiate array of generic classes.. 50 @SuppressWarnings("unchecked") 51 ArrayList<Node>[] newNodes = new ArrayList[ways.size()]; 52 boolean[] changedWays = new boolean[ways.size()]; 53 54 Set<Node> intersectionNodes = new LinkedHashSet<Node>(); 55 56 for (int pos = 0; pos < ways.size(); pos ++) { 57 newNodes[pos] = new ArrayList<Node>(ways.get(pos).getNodes()); 58 changedWays[pos] = false; 59 } 60 61 //iterate over all segment pairs and introduce the intersections 62 63 Comparator<Node> coordsComparator = new NodePositionComparator(); 64 65 int seg1Way = 0; 66 int seg1Pos = -1; 67 68 while (true) { 69 //advance to next segment 70 seg1Pos++; 71 if (seg1Pos > newNodes[seg1Way].size() - 2) { 72 seg1Way++; 73 seg1Pos = 0; 74 75 if (seg1Way == ways.size()) { //finished 76 break; 77 } 78 } 79 80 81 //iterate over secondary segment 82 83 int seg2Way = seg1Way; 84 int seg2Pos = seg1Pos + 1;//skip the adjacent segment 85 86 while (true) { 87 88 //advance to next segment 89 seg2Pos++; 90 if (seg2Pos > newNodes[seg2Way].size() - 2) { 91 seg2Way++; 92 seg2Pos = 0; 93 94 if (seg2Way == ways.size()) { //finished 95 break; 96 } 97 } 98 99 //need to get them again every time, because other segments may be changed 100 Node seg1Node1 = newNodes[seg1Way].get(seg1Pos); 101 Node seg1Node2 = newNodes[seg1Way].get(seg1Pos + 1); 102 Node seg2Node1 = newNodes[seg2Way].get(seg2Pos); 103 Node seg2Node2 = newNodes[seg2Way].get(seg2Pos + 1); 104 105 int commonCount = 0; 106 //test if we have common nodes to add. 107 if (seg1Node1 == seg2Node1 || seg1Node1 == seg2Node2) { 108 commonCount ++; 109 110 if (seg1Way == seg2Way && 111 seg1Pos == 0 && 112 seg2Pos == newNodes[seg2Way].size() -2) { 113 //do not add - this is first and last segment of the same way. 114 } else { 115 intersectionNodes.add(seg1Node1); 116 } 117 } 118 119 if (seg1Node2 == seg2Node1 || seg1Node2 == seg2Node2) { 120 commonCount ++; 121 122 intersectionNodes.add(seg1Node2); 123 } 124 125 //no common nodes - find intersection 126 if (commonCount == 0) { 127 LatLon intersection = getLineLineIntersection( 128 seg1Node1.getEastNorth().east(), seg1Node1.getEastNorth().north(), 129 seg1Node2.getEastNorth().east(), seg1Node2.getEastNorth().north(), 130 seg2Node1.getEastNorth().east(), seg2Node1.getEastNorth().north(), 131 seg2Node2.getEastNorth().east(), seg2Node2.getEastNorth().north()); 132 133 if (intersection != null) { 134 if (test) { 135 intersectionNodes.add(seg2Node1); 136 return new ArrayList<Node>(intersectionNodes); 137 } 138 139 Node newNode = new Node(intersection); 140 Node intNode = newNode; 141 boolean insertInSeg1 = false; 142 boolean insertInSeg2 = false; 143 144 //find if the intersection point is at end point of one of the segments, if so use that point 145 146 //segment 1 147 if (coordsComparator.compare(newNode, seg1Node1) == 0) { 148 intNode = seg1Node1; 149 } else if (coordsComparator.compare(newNode, seg1Node2) == 0) { 150 intNode = seg1Node2; 151 } else { 152 insertInSeg1 = true; 153 } 154 155 //segment 2 156 if (coordsComparator.compare(newNode, seg2Node1) == 0) { 157 intNode = seg2Node1; 158 } else if (coordsComparator.compare(newNode, seg2Node2) == 0) { 159 intNode = seg2Node2; 160 } else { 161 insertInSeg2 = true; 162 } 163 164 if (insertInSeg1) { 165 newNodes[seg1Way].add(seg1Pos +1, intNode); 166 changedWays[seg1Way] = true; 167 168 //fix seg2 position, as indexes have changed, seg2Pos is always bigger than seg1Pos on the same segment. 169 if (seg2Way == seg1Way) { 170 seg2Pos ++; 171 } 172 } 173 174 if (insertInSeg2) { 175 newNodes[seg2Way].add(seg2Pos +1, intNode); 176 changedWays[seg2Way] = true; 177 178 //Do not need to compare again to already split segment 179 seg2Pos ++; 180 } 181 182 intersectionNodes.add(intNode); 183 184 if (intNode == newNode) { 185 cmds.add(new AddCommand(intNode)); 186 } 187 } 188 } 189 else if (test && intersectionNodes.size() > 0) 190 return new ArrayList<Node>(intersectionNodes); 191 } 192 } 193 194 for (int pos = 0; pos < ways.size(); pos ++) { 195 if (changedWays[pos] == false) { 196 continue; 197 } 198 199 Way way = ways.get(pos); 200 Way newWay = new Way(way); 201 newWay.setNodes(newNodes[pos]); 202 203 cmds.add(new ChangeCommand(way, newWay)); 204 } 205 206 return new ArrayList<Node>(intersectionNodes); 207 } 208 209 210 /** 211 * Finds the intersection of two lines 212 * @return LatLon null if no intersection was found, the LatLon coordinates of the intersection otherwise 213 */ 214 static private LatLon getLineLineIntersection( 215 double x1, double y1, double x2, double y2, 216 double x3, double y3, double x4, double y4) { 217 218 if (!Line2D.linesIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return null; 219 220 // Convert line from (point, point) form to ax+by=c 221 double a1 = y2 - y1; 222 double b1 = x1 - x2; 223 double c1 = x2*y1 - x1*y2; 224 225 double a2 = y4 - y3; 226 double b2 = x3 - x4; 227 double c2 = x4*y3 - x3*y4; 228 229 // Solve the equations 230 double det = a1*b2 - a2*b1; 231 if (det == 0) return null; // Lines are parallel 232 233 return Main.proj.eastNorth2latlon(new EastNorth( 234 (b1*c2 - b2*c1)/det, 235 (a2*c1 -a1*c2)/det 236 )); 237 } 238 239 @Override 240 public void actionPerformed(ActionEvent arg0) { 241 if (!isEnabled()) 242 return; 243 List<Way> ways = OsmPrimitive.getFilteredList(getCurrentDataSet().getSelected(), Way.class); 244 if (ways.isEmpty()) { 245 JOptionPane.showMessageDialog( 246 Main.parent, 247 tr("Please select one or more ways with intersections of segments."), 248 tr("Information"), 249 JOptionPane.INFORMATION_MESSAGE 250 ); 251 return; 252 } 253 254 LinkedList<Command> cmds = new LinkedList<Command>(); 255 addIntersections(ways, false, cmds); 256 if (!cmds.isEmpty()) { 257 Main.main.undoRedo.add(new SequenceCommand(tr("Add nodes at intersections"),cmds)); 258 } 259 } 260 261 @Override 262 protected void updateEnabledState() { 263 if (getCurrentDataSet() == null) { 264 setEnabled(false); 265 } else { 266 updateEnabledState(getCurrentDataSet().getSelected()); 267 } 268 } 269 270 @Override 271 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) { 272 setEnabled(selection != null && !selection.isEmpty()); 273 } 274 } -
src/org/openstreetmap/josm/actions/JoinAreasAction.java
1 1 // License: GPL. Copyright 2007 by Immanuel Scholz and others 2 2 package org.openstreetmap.josm.actions; 3 3 4 import static org.openstreetmap.josm.actions.AddIntersectionsAction.addIntersections; 4 5 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.combineTigerTags; 5 6 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.completeTagCollectionForEditing; 6 7 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing; … … 11 12 import java.awt.event.ActionEvent; 12 13 import java.awt.event.KeyEvent; 13 14 import java.awt.geom.Area; 14 import java.awt.geom.Line2D;15 15 import java.util.ArrayList; 16 16 import java.util.Collection; 17 17 import java.util.Collections; 18 import java.util.Comparator;19 18 import java.util.HashMap; 20 19 import java.util.HashSet; 21 20 import java.util.LinkedHashSet; … … 38 37 import org.openstreetmap.josm.corrector.UserCancelException; 39 38 import org.openstreetmap.josm.data.UndoRedoHandler; 40 39 import org.openstreetmap.josm.data.coor.EastNorth; 41 import org.openstreetmap.josm.data.coor.LatLon;42 40 import org.openstreetmap.josm.data.osm.DataSet; 43 41 import org.openstreetmap.josm.data.osm.Node; 42 import org.openstreetmap.josm.data.osm.NodePositionComparator; 44 43 import org.openstreetmap.josm.data.osm.OsmPrimitive; 45 44 import org.openstreetmap.josm.data.osm.Relation; 46 45 import org.openstreetmap.josm.data.osm.RelationMember; … … 289 288 } 290 289 } 291 290 292 /**293 * Provides some node order , based on coordinates, nodes with equal coordinates are equal.294 * @author viesturs295 *296 */297 private class NodePositionComparator implements Comparator<Node> {298 291 299 @Override300 public int compare(Node n1, Node n2) {301 302 double dLat = n1.getCoor().lat() - n2.getCoor().lat();303 double dLon = n1.getCoor().lon() - n2.getCoor().lon();304 305 if (dLat > 0)306 return 1;307 else if (dLat < 0)308 return -1;309 else if (dLon == 0) //dlat is 0 here310 return 0;311 else312 return dLon > 0 ? 1 : -1;313 }314 }315 316 317 292 /** 318 293 * Helper storage class for finding findOuterWays 319 294 * @author viesturs … … 429 404 } 430 405 431 406 //find intersection points 432 ArrayList<Node> nodes = addIntersections(allStartingWays, true );407 ArrayList<Node> nodes = addIntersections(allStartingWays, true, cmds); 433 408 return nodes.size() > 0; 434 409 } 435 410 … … 465 440 } 466 441 467 442 //find intersection points 468 ArrayList<Node> nodes = addIntersections(allStartingWays, false );443 ArrayList<Node> nodes = addIntersections(allStartingWays, false, cmds); 469 444 470 445 //no intersections, return. 471 446 if (nodes.size() == 0) return result; … … 660 635 } 661 636 662 637 663 664 638 /** 665 * Will find all intersection and add nodes there for list of given ways. Handles self-intersections too.666 * And make commands to add the intersection points to ways.667 * @param List<Way> - a list of ways to test668 * @return ArrayList<Node> List of new nodes669 * Prerequisite: no two nodes have the same coordinates.670 */671 private ArrayList<Node> addIntersections(List<Way> ways, boolean test) {672 //TODO: this is a bit slow - O( (number of nodes)^2 + numberOfIntersections * numberOfNodes )673 674 //stupid java, cannot instantiate array of generic classes..675 @SuppressWarnings("unchecked")676 ArrayList<Node>[] newNodes = new ArrayList[ways.size()];677 boolean[] changedWays = new boolean[ways.size()];678 679 Set<Node> intersectionNodes = new LinkedHashSet<Node>();680 681 for (int pos = 0; pos < ways.size(); pos ++) {682 newNodes[pos] = new ArrayList<Node>(ways.get(pos).getNodes());683 changedWays[pos] = false;684 }685 686 //iterate over all segment pairs and introduce the intersections687 688 Comparator<Node> coordsComparator = new NodePositionComparator();689 690 int seg1Way = 0;691 int seg1Pos = -1;692 693 while (true) {694 //advance to next segment695 seg1Pos++;696 if (seg1Pos > newNodes[seg1Way].size() - 2) {697 seg1Way++;698 seg1Pos = 0;699 700 if (seg1Way == ways.size()) { //finished701 break;702 }703 }704 705 706 //iterate over secondary segment707 708 int seg2Way = seg1Way;709 int seg2Pos = seg1Pos + 1;//skip the adjacent segment710 711 while (true) {712 713 //advance to next segment714 seg2Pos++;715 if (seg2Pos > newNodes[seg2Way].size() - 2) {716 seg2Way++;717 seg2Pos = 0;718 719 if (seg2Way == ways.size()) { //finished720 break;721 }722 }723 724 //need to get them again every time, because other segments may be changed725 Node seg1Node1 = newNodes[seg1Way].get(seg1Pos);726 Node seg1Node2 = newNodes[seg1Way].get(seg1Pos + 1);727 Node seg2Node1 = newNodes[seg2Way].get(seg2Pos);728 Node seg2Node2 = newNodes[seg2Way].get(seg2Pos + 1);729 730 int commonCount = 0;731 //test if we have common nodes to add.732 if (seg1Node1 == seg2Node1 || seg1Node1 == seg2Node2) {733 commonCount ++;734 735 if (seg1Way == seg2Way &&736 seg1Pos == 0 &&737 seg2Pos == newNodes[seg2Way].size() -2) {738 //do not add - this is first and last segment of the same way.739 } else {740 intersectionNodes.add(seg1Node1);741 }742 }743 744 if (seg1Node2 == seg2Node1 || seg1Node2 == seg2Node2) {745 commonCount ++;746 747 intersectionNodes.add(seg1Node2);748 }749 750 //no common nodes - find intersection751 if (commonCount == 0) {752 LatLon intersection = getLineLineIntersection(753 seg1Node1.getEastNorth().east(), seg1Node1.getEastNorth().north(),754 seg1Node2.getEastNorth().east(), seg1Node2.getEastNorth().north(),755 seg2Node1.getEastNorth().east(), seg2Node1.getEastNorth().north(),756 seg2Node2.getEastNorth().east(), seg2Node2.getEastNorth().north());757 758 if (intersection != null) {759 if (test) {760 intersectionNodes.add(seg2Node1);761 return new ArrayList<Node>(intersectionNodes);762 }763 764 Node newNode = new Node(intersection);765 Node intNode = newNode;766 boolean insertInSeg1 = false;767 boolean insertInSeg2 = false;768 769 //find if the intersection point is at end point of one of the segments, if so use that point770 771 //segment 1772 if (coordsComparator.compare(newNode, seg1Node1) == 0) {773 intNode = seg1Node1;774 } else if (coordsComparator.compare(newNode, seg1Node2) == 0) {775 intNode = seg1Node2;776 } else {777 insertInSeg1 = true;778 }779 780 //segment 2781 if (coordsComparator.compare(newNode, seg2Node1) == 0) {782 intNode = seg2Node1;783 } else if (coordsComparator.compare(newNode, seg2Node2) == 0) {784 intNode = seg2Node2;785 } else {786 insertInSeg2 = true;787 }788 789 if (insertInSeg1) {790 newNodes[seg1Way].add(seg1Pos +1, intNode);791 changedWays[seg1Way] = true;792 793 //fix seg2 position, as indexes have changed, seg2Pos is always bigger than seg1Pos on the same segment.794 if (seg2Way == seg1Way) {795 seg2Pos ++;796 }797 }798 799 if (insertInSeg2) {800 newNodes[seg2Way].add(seg2Pos +1, intNode);801 changedWays[seg2Way] = true;802 803 //Do not need to compare again to already split segment804 seg2Pos ++;805 }806 807 intersectionNodes.add(intNode);808 809 if (intNode == newNode) {810 cmds.add(new AddCommand(intNode));811 }812 }813 }814 else if (test && intersectionNodes.size() > 0)815 return new ArrayList<Node>(intersectionNodes);816 }817 }818 819 for (int pos = 0; pos < ways.size(); pos ++) {820 if (changedWays[pos] == false) {821 continue;822 }823 824 Way way = ways.get(pos);825 Way newWay = new Way(way);826 newWay.setNodes(newNodes[pos]);827 828 cmds.add(new ChangeCommand(way, newWay));829 }830 831 return new ArrayList<Node>(intersectionNodes);832 }833 834 /**835 * Finds the intersection of two lines836 * @return LatLon null if no intersection was found, the LatLon coordinates of the intersection otherwise837 */838 static private LatLon getLineLineIntersection(839 double x1, double y1, double x2, double y2,840 double x3, double y3, double x4, double y4) {841 842 if (!Line2D.linesIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return null;843 844 // Convert line from (point, point) form to ax+by=c845 double a1 = y2 - y1;846 double b1 = x1 - x2;847 double c1 = x2*y1 - x1*y2;848 849 double a2 = y4 - y3;850 double b2 = x3 - x4;851 double c2 = x4*y3 - x3*y4;852 853 // Solve the equations854 double det = a1*b2 - a2*b1;855 if (det == 0) return null; // Lines are parallel856 857 return Main.proj.eastNorth2latlon(new EastNorth(858 (b1*c2 - b2*c1)/det,859 (a2*c1 -a1*c2)/det860 ));861 }862 863 864 865 /**866 639 * Commits the command list with a description 867 640 * @param String The description of what the commands do 868 641 */ -
src/org/openstreetmap/josm/gui/MainMenu.java
16 16 17 17 import org.openstreetmap.josm.Main; 18 18 import org.openstreetmap.josm.actions.AboutAction; 19 import org.openstreetmap.josm.actions.AddIntersectionsAction; 19 20 import org.openstreetmap.josm.actions.AddNodeAction; 20 21 import org.openstreetmap.josm.actions.AlignInCircleAction; 21 22 import org.openstreetmap.josm.actions.AlignInLineAction; … … 51 52 import org.openstreetmap.josm.actions.OpenFileAction; 52 53 import org.openstreetmap.josm.actions.OpenLocationAction; 53 54 import org.openstreetmap.josm.actions.OrthogonalizeAction; 55 import org.openstreetmap.josm.actions.OrthogonalizeAction.Undo; 54 56 import org.openstreetmap.josm.actions.PasteAction; 55 57 import org.openstreetmap.josm.actions.PasteTagsAction; 56 58 import org.openstreetmap.josm.actions.PreferencesAction; … … 75 77 import org.openstreetmap.josm.actions.WireframeToggleAction; 76 78 import org.openstreetmap.josm.actions.ZoomInAction; 77 79 import org.openstreetmap.josm.actions.ZoomOutAction; 78 import org.openstreetmap.josm.actions.OrthogonalizeAction.Undo;79 80 import org.openstreetmap.josm.actions.audio.AudioBackAction; 80 81 import org.openstreetmap.josm.actions.audio.AudioFasterAction; 81 82 import org.openstreetmap.josm.actions.audio.AudioFwdAction; … … 152 153 public final JosmAction joinNodeWay = new JoinNodeWayAction(); 153 154 public final JosmAction unglueNodes = new UnGlueAction(); 154 155 public final JosmAction simplifyWay = new SimplifyWayAction(); 156 public final JosmAction addIntersections = new AddIntersectionsAction(); 155 157 public final JosmAction joinAreas = new JoinAreasAction(); 156 158 public final InfoAction info = new InfoAction(); 157 159 public final HistoryInfoAction historyinfo = new HistoryInfoAction(); … … 315 317 add(toolsMenu, mergeNodes); 316 318 add(toolsMenu, joinNodeWay); 317 319 add(toolsMenu, unglueNodes); 320 add(toolsMenu, addIntersections); 318 321 add(toolsMenu, joinAreas); 319 322 toolsMenu.addSeparator(); 320 323 add(toolsMenu, info); -
src/org/openstreetmap/josm/data/osm/NodePositionComparator.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.osm; 3 4 import java.util.Comparator; 5 6 /** 7 * Provides some node order , based on coordinates, nodes with equal coordinates are equal. 8 * @author viesturs 9 * 10 */ 11 public class NodePositionComparator implements Comparator<Node> { 12 13 @Override 14 public int compare(Node n1, Node n2) { 15 16 double dLat = n1.getCoor().lat() - n2.getCoor().lat(); 17 double dLon = n1.getCoor().lon() - n2.getCoor().lon(); 18 19 if (dLat > 0) 20 return 1; 21 else if (dLat < 0) 22 return -1; 23 else if (dLon == 0) //dlat is 0 here 24 return 0; 25 else 26 return dLon > 0 ? 1 : -1; 27 } 28 }
