001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import java.util.Collection; 005import java.util.LinkedList; 006import java.util.Set; 007import java.util.TreeSet; 008 009import org.openstreetmap.josm.data.osm.DataSet; 010import org.openstreetmap.josm.data.osm.Node; 011import org.openstreetmap.josm.data.osm.OsmPrimitive; 012import org.openstreetmap.josm.data.osm.Way; 013 014/** 015 * Auxiliary class for the {@link SelectNonBranchingWaySequencesAction}. 016 * 017 * @author Marko Mäkelä 018 */ 019public class SelectNonBranchingWaySequences { 020 /** 021 * outer endpoints of selected ways 022 */ 023 private Set<Node> outerNodes; 024 /** 025 * endpoints of selected ways 026 */ 027 private Set<Node> nodes; 028 029 /** 030 * Creates a way selection 031 * 032 * @param ways selection a selection of ways 033 */ 034 public SelectNonBranchingWaySequences(final Collection<Way> ways) { 035 if (ways.isEmpty()) { 036 // The selection cannot be extended. 037 outerNodes = null; 038 nodes = null; 039 } else { 040 nodes = new TreeSet<>(); 041 outerNodes = new TreeSet<>(); 042 043 for (Way way : ways) { 044 addNodes(way); 045 } 046 } 047 } 048 049 /** 050 * Add a way endpoint to nodes, outerNodes 051 * 052 * @param node a way endpoint 053 */ 054 private void addNodes(Node node) { 055 if (node == null) return; 056 else if (!nodes.add(node)) 057 outerNodes.remove(node); 058 else 059 outerNodes.add(node); 060 } 061 062 /** 063 * Add the endpoints of the way to nodes, outerNodes 064 * 065 * @param way a way whose endpoints are added 066 */ 067 private void addNodes(Way way) { 068 addNodes(way.firstNode()); 069 addNodes(way.lastNode()); 070 } 071 072 /** 073 * Find out if the selection can be extended 074 * 075 * @return true if the selection can be extended 076 */ 077 public boolean canExtend() { 078 return outerNodes != null && !outerNodes.isEmpty(); 079 } 080 081 /** 082 * Finds out if the current selection can be extended. 083 * 084 * @param selection current selection (ways and others) 085 * @param node perimeter node from which to extend the selection 086 * @return a way by which to extend the selection, or null 087 */ 088 private static Way findWay(Collection<OsmPrimitive> selection, Node node) { 089 Way foundWay = null; 090 091 for (Way way : OsmPrimitive.getFilteredList(node.getReferrers(), 092 Way.class)) { 093 if (way.getNodesCount() < 2 || !way.isFirstLastNode(node) 094 || !way.isSelectable() 095 || selection.contains(way)) 096 continue; 097 098 /* A previously unselected way was found that is connected 099 to the node. */ 100 if (foundWay != null) 101 /* This is not the only qualifying way. There is a 102 branch at the node, and we cannot extend the selection. */ 103 return null; 104 105 /* Remember the first found qualifying way. */ 106 foundWay = way; 107 } 108 109 /* Return the only way found, or null if none was found. */ 110 return foundWay; 111 } 112 113 /** 114 * Finds out if the current selection can be extended. 115 * <p> 116 * The members outerNodes, nodes must have been initialized. 117 * How to update these members when extending the selection, @see extend(). 118 * </p> 119 * @param selection current selection 120 * @return a way by which to extend the selection, or null 121 */ 122 private Way findWay(Collection<OsmPrimitive> selection) { 123 for (Node node : outerNodes) { 124 Way way = findWay(selection, node); 125 if (way != null) 126 return way; 127 } 128 129 return null; 130 } 131 132 /** 133 * Extend the current selection 134 * 135 * @param data the data set in which to extend the selection 136 */ 137 public void extend(DataSet data) { 138 if (!canExtend()) 139 return; 140 141 Collection<OsmPrimitive> currentSelection = data.getSelected(); 142 143 Way way = findWay(currentSelection); 144 145 if (way == null) 146 return; 147 148 boolean selectionChanged = false; 149 Collection<OsmPrimitive> selection = new LinkedList<>(); 150 for (OsmPrimitive primitive : currentSelection) { 151 selection.add(primitive); 152 } 153 154 do { 155 if (!selection.add(way)) 156 break; 157 158 selectionChanged = true; 159 addNodes(way); 160 161 way = findWay(selection); 162 } while (way != null); 163 164 if (selectionChanged) 165 data.setSelected(selection, true); 166 } 167}