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 Set<Node> outerNodes; 024 /** 025 * endpoints of selected ways 026 */ 027 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 * Add a way endpoint to nodes, outerNodes 050 * 051 * @param node a way endpoint 052 */ 053 private void addNodes(Node node) { 054 if (node == null) return; 055 else if (!nodes.add(node)) 056 outerNodes.remove(node); 057 else 058 outerNodes.add(node); 059 } 060 061 /** 062 * Add the endpoints of the way to nodes, outerNodes 063 * 064 * @param way a way whose endpoints are added 065 */ 066 private void addNodes(Way way) { 067 addNodes(way.firstNode()); 068 addNodes(way.lastNode()); 069 } 070 071 /** 072 * Find out if the selection can be extended 073 * 074 * @return true if the selection can be extended 075 */ 076 public boolean canExtend() { 077 return outerNodes != null && !outerNodes.isEmpty(); 078 } 079 080 /** 081 * Finds out if the current selection can be extended. 082 * 083 * @param selection current selection (ways and others) 084 * @param node perimeter node from which to extend the selection 085 * @return a way by which to extend the selection, or null 086 */ 087 private static Way findWay(Collection<OsmPrimitive> selection, Node node) { 088 Way foundWay = null; 089 090 for (Way way : OsmPrimitive.getFilteredList(node.getReferrers(), 091 Way.class)) { 092 if (way.getNodesCount() < 2 || !way.isFirstLastNode(node) 093 || selection.contains(way)) 094 continue; 095 096 /* A previously unselected way was found that is connected 097 to the node. */ 098 if (foundWay != null) 099 /* This is not the only qualifying way. There is a 100 branch at the node, and we cannot extend the selection. */ 101 return null; 102 103 /* Remember the first found qualifying way. */ 104 foundWay = way; 105 } 106 107 /* Return the only way found, or null if none was found. */ 108 return foundWay; 109 } 110 111 /** 112 * Finds out if the current selection can be extended. 113 * <p> 114 * The members outerNodes, nodes must have been initialized. 115 * How to update these members when extending the selection, @see extend(). 116 * </p> 117 * @param selection current selection 118 * @return a way by which to extend the selection, or null 119 */ 120 private Way findWay(Collection<OsmPrimitive> selection) { 121 for (Node node : outerNodes) { 122 Way way = findWay(selection, node); 123 if (way != null) 124 return way; 125 } 126 127 return null; 128 } 129 130 /** 131 * Extend the current selection 132 * 133 * @param data the data set in which to extend the selection 134 */ 135 public void extend(DataSet data) { 136 Collection<OsmPrimitive> currentSelection; 137 LinkedList<OsmPrimitive> selection; 138 boolean selectionChanged = false; 139 Way way; 140 141 if (!canExtend()) 142 return; 143 144 currentSelection = data.getSelected(); 145 146 way = findWay(currentSelection); 147 148 if (way == null) 149 return; 150 151 selection = new LinkedList<>(); 152 for (OsmPrimitive primitive : currentSelection) 153 selection.add(primitive); 154 155 do { 156 if (!selection.add(way)) 157 break; 158 159 selectionChanged = true; 160 addNodes(way); 161 162 way = findWay(selection); 163 } while (way != null); 164 165 if (selectionChanged) 166 data.setSelected(selection, true); 167 } 168}