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 }