001 //License: GPL. Copyright 2007 by Immanuel Scholz and others
002 package org.openstreetmap.josm.actions;
003
004 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005 import static org.openstreetmap.josm.tools.I18n.tr;
006
007 import java.awt.event.ActionEvent;
008 import java.awt.event.KeyEvent;
009 import java.util.ArrayList;
010 import java.util.Collection;
011 import java.util.Collections;
012 import java.util.HashMap;
013 import java.util.HashSet;
014 import java.util.LinkedList;
015 import java.util.List;
016 import java.util.Map;
017
018 import org.openstreetmap.josm.Main;
019 import org.openstreetmap.josm.command.ChangeCommand;
020 import org.openstreetmap.josm.command.Command;
021 import org.openstreetmap.josm.command.SequenceCommand;
022 import org.openstreetmap.josm.data.osm.Node;
023 import org.openstreetmap.josm.data.osm.OsmPrimitive;
024 import org.openstreetmap.josm.data.osm.Way;
025 import org.openstreetmap.josm.data.osm.WaySegment;
026 import org.openstreetmap.josm.tools.Shortcut;
027
028 public class JoinNodeWayAction extends JosmAction {
029 public JoinNodeWayAction() {
030 super(tr("Join Node to Way"), "joinnodeway", tr("Include a node into the nearest way segments"),
031 Shortcut.registerShortcut("tools:joinnodeway", tr("Tool: {0}", tr("Join Node to Way")), KeyEvent.VK_J, Shortcut.DIRECT), true);
032 putValue("help", ht("/Action/JoinNodeWay"));
033 }
034
035 public void actionPerformed(ActionEvent e) {
036 if (!isEnabled())
037 return;
038 Collection<Node> selectedNodes = getCurrentDataSet().getSelectedNodes();
039 // Allow multiple selected nodes too?
040 if (selectedNodes.size() != 1) return;
041
042 Node node = selectedNodes.iterator().next();
043
044 Collection<Command> cmds = new LinkedList<Command>();
045
046 // If the user has selected some ways, only join the node to these.
047 boolean restrictToSelectedWays =
048 getCurrentDataSet().getSelectedWays().size() > 0;
049
050 List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(
051 Main.map.mapView.getPoint(node), OsmPrimitive.isSelectablePredicate);
052 HashMap<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>();
053 for (WaySegment ws : wss) {
054 // Maybe cleaner to pass a "isSelected" predicate to getNearestWaySegements, but this is atm. less invasive.
055 if(restrictToSelectedWays && !ws.way.isSelected()) {
056 continue;
057 }
058
059 List<Integer> is;
060 if (insertPoints.containsKey(ws.way)) {
061 is = insertPoints.get(ws.way);
062 } else {
063 is = new ArrayList<Integer>();
064 insertPoints.put(ws.way, is);
065 }
066
067 if (ws.way.getNode(ws.lowerIndex) != node
068 && ws.way.getNode(ws.lowerIndex+1) != node) {
069 is.add(ws.lowerIndex);
070 }
071 }
072
073 for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) {
074 List<Integer> is = insertPoint.getValue();
075 if (is.size() == 0) {
076 continue;
077 }
078
079 Way w = insertPoint.getKey();
080 List<Node> nodesToAdd = w.getNodes();
081 pruneSuccsAndReverse(is);
082 for (int i : is) {
083 nodesToAdd.add(i+1, node);
084 }
085 Way wnew = new Way(w);
086 wnew.setNodes(nodesToAdd);
087 cmds.add(new ChangeCommand(w, wnew));
088 }
089 if (cmds.size() == 0) return;
090 Main.main.undoRedo.add(new SequenceCommand(tr("Join Node and Line"), cmds));
091 Main.map.repaint();
092 }
093
094 private static void pruneSuccsAndReverse(List<Integer> is) {
095 //if (is.size() < 2) return;
096
097 HashSet<Integer> is2 = new HashSet<Integer>();
098 for (int i : is) {
099 if (!is2.contains(i - 1) && !is2.contains(i + 1)) {
100 is2.add(i);
101 }
102 }
103 is.clear();
104 is.addAll(is2);
105 Collections.sort(is);
106 Collections.reverse(is);
107 }
108
109 @Override
110 protected void updateEnabledState() {
111 if (getCurrentDataSet() == null) {
112 setEnabled(false);
113 } else {
114 updateEnabledState(getCurrentDataSet().getSelected());
115 }
116 }
117
118 @Override
119 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
120 setEnabled(selection != null && !selection.isEmpty());
121 }
122 }