source: josm/trunk/src/org/openstreetmap/josm/data/osm/Way.java@ 2410

Last change on this file since 2410 was 2410, checked in by jttt, 14 years ago

Add clearId parameter to primitives copy constructor, replace some clearOsmId calls, throw exception if id is set after primitive was added to the dataset

  • Property svn:eol-style set to native
File size: 9.9 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data.osm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.Collection;
9import java.util.List;
10
11import org.openstreetmap.josm.data.osm.visitor.Visitor;
12import org.openstreetmap.josm.tools.CopyList;
13import org.openstreetmap.josm.tools.Pair;
14
15/**
16 * One full way, consisting of a list of way nodes.
17 *
18 * @author imi
19 */
20public final class Way extends OsmPrimitive {
21
22 /**
23 * All way nodes in this way
24 *
25 */
26 private Node[] nodes = new Node[0];
27
28 /**
29 *
30 * You can modify returned list but changes will not be propagated back
31 * to the Way. Use {@link #setNodes(List)} to update this way
32 * @return Nodes composing the way
33 * @since 1862
34 */
35 public List<Node> getNodes() {
36 return new CopyList<Node>(nodes);
37 }
38
39 /**
40 * Set new list of nodes to way. This method is preferred to multiple calls to addNode/removeNode
41 * and similar methods because nodes are internally saved as array which means lower memory overhead
42 * but also slower modifying operations.
43 * @param nodes New way nodes. Can be null, in that case all way nodes are removed
44 * @since 1862
45 */
46 public void setNodes(List<Node> nodes) {
47 for (Node node:this.nodes) {
48 node.removeReferrer(this);
49 }
50
51 if (nodes == null) {
52 this.nodes = new Node[0];
53 } else {
54 this.nodes = nodes.toArray(new Node[nodes.size()]);
55 }
56 for (Node node:this.nodes) {
57 node.addReferrer(this);
58 }
59
60 clearCached();
61 }
62
63 /**
64 * Replies the number of nodes in this ways.
65 *
66 * @return the number of nodes in this ways.
67 * @since 1862
68 */
69 public int getNodesCount() {
70 return nodes.length;
71 }
72
73 /**
74 * Replies the node at position <code>index</code>.
75 *
76 * @param index the position
77 * @return the node at position <code>index</code>
78 * @exception IndexOutOfBoundsException thrown if <code>index</code> < 0
79 * or <code>index</code> >= {@see #getNodesCount()}
80 * @since 1862
81 */
82 public Node getNode(int index) {
83 return nodes[index];
84 }
85
86 /**
87 * Replies true if this way contains the node <code>node</code>, false
88 * otherwise. Replies false if <code>node</code> is null.
89 *
90 * @param node the node. May be null.
91 * @return true if this way contains the node <code>node</code>, false
92 * otherwise
93 * @since 1909
94 */
95 public boolean containsNode(Node node) {
96 if (node == null) return false;
97 for (int i=0; i<nodes.length; i++) {
98 if (nodes[i].equals(node))
99 return true;
100 }
101 return false;
102 }
103
104 /* mappaint data */
105 public boolean isMappaintArea = false;
106 public Integer mappaintDrawnAreaCode = 0;
107 /* end of mappaint data */
108 @Override protected void clearCached() {
109 super.clearCached();
110 isMappaintArea = false;
111 mappaintDrawnAreaCode = 0;
112 }
113
114 public ArrayList<Pair<Node,Node>> getNodePairs(boolean sort) {
115 ArrayList<Pair<Node,Node>> chunkSet = new ArrayList<Pair<Node,Node>>();
116 if (incomplete) return chunkSet;
117 Node lastN = null;
118 for (Node n : this.nodes) {
119 if (lastN == null) {
120 lastN = n;
121 continue;
122 }
123 Pair<Node,Node> np = new Pair<Node,Node>(lastN, n);
124 if (sort) {
125 Pair.sort(np);
126 }
127 chunkSet.add(np);
128 lastN = n;
129 }
130 return chunkSet;
131 }
132
133
134 @Override public void visit(Visitor visitor) {
135 visitor.visit(this);
136 }
137
138 protected Way(long id, boolean allowNegative) {
139 super(id, allowNegative);
140 }
141
142 /**
143 * Creates a new way with id 0.
144 *
145 */
146 public Way(){
147 super(0, false);
148 }
149
150 /**
151 *
152 * @param original
153 * @param clearId
154 */
155 public Way(Way original, boolean clearId) {
156 super(original.getUniqueId(), true);
157 cloneFrom(original);
158 if (clearId) {
159 clearOsmId();
160 }
161 }
162
163 /**
164 * Create an identical clone of the argument (including the id).
165 *
166 * @param original the original way. Must not be null.
167 */
168 public Way(Way original) {
169 this(original, false);
170 }
171
172 /**
173 * Creates a new way for the given id. If the id > 0, the way is marked
174 * as incomplete.
175 *
176 * @param id the id. > 0 required
177 * @throws IllegalArgumentException thrown if id < 0
178 */
179 public Way(long id) throws IllegalArgumentException {
180 super(id, false);
181 }
182
183 /**
184 *
185 * @param data
186 */
187 @Override
188 public void load(PrimitiveData data) {
189 super.load(data);
190
191 WayData wayData = (WayData) data;
192
193 List<Node> newNodes = new ArrayList<Node>(wayData.getNodes().size());
194 for (Long nodeId : wayData.getNodes()) {
195 Node node = (Node)getDataSet().getPrimitiveById(nodeId, OsmPrimitiveType.NODE);
196 if (node != null) {
197 newNodes.add(node);
198 } else
199 throw new AssertionError("Data consistency problem - way with missing node detected");
200 }
201 setNodes(newNodes);
202 }
203
204 @Override public WayData save() {
205 WayData data = new WayData();
206 saveCommonAttributes(data);
207 for (Node node:getNodes()) {
208 data.getNodes().add(node.getUniqueId());
209 }
210 return data;
211 }
212
213 @Override public void cloneFrom(OsmPrimitive osm) {
214 super.cloneFrom(osm);
215 Way otherWay = (Way)osm;
216 setNodes(otherWay.getNodes());
217 }
218
219 @Override public String toString() {
220 String nodesDesc = incomplete?"(incomplete)":"nodes=" + Arrays.toString(nodes);
221 return "{Way id=" + getUniqueId() + " version=" + getVersion()+ " " + getFlagsAsString() + " " + nodesDesc + "}";
222 }
223
224 @Override
225 public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
226 if (other == null || ! (other instanceof Way) )
227 return false;
228 if (! super.hasEqualSemanticAttributes(other))
229 return false;
230 Way w = (Way)other;
231 return Arrays.equals(nodes, w.nodes);
232 }
233
234 public int compareTo(OsmPrimitive o) {
235 if (o instanceof Relation)
236 return 1;
237 return o instanceof Way ? Long.valueOf(getId()).compareTo(o.getId()) : -1;
238 }
239
240 public void removeNode(Node n) {
241 if (incomplete) return;
242 boolean closed = (lastNode() == n && firstNode() == n);
243 int i;
244 List<Node> copy = getNodes();
245 while ((i = copy.indexOf(n)) >= 0) {
246 copy.remove(i);
247 }
248 i = copy.size();
249 if (closed && i > 2) {
250 // TODO Should this be copy.addNode(firstNode)?
251 addNode(firstNode());
252 } else if (i >= 2 && i <= 3 && copy.get(0) == copy.get(i-1)) {
253 copy.remove(i-1);
254 }
255 setNodes(copy);
256 }
257
258 public void removeNodes(Collection<? extends OsmPrimitive> selection) {
259 if (incomplete) return;
260 for(OsmPrimitive p : selection) {
261 if (p instanceof Node) {
262 removeNode((Node)p);
263 }
264 }
265 }
266
267 /**
268 * Adds a node to the end of the list of nodes. Ignored, if n is null.
269 *
270 * @param n the node. Ignored, if null.
271 * @throws IllegalStateException thrown, if this way is marked as incomplete. We can't add a node
272 * to an incomplete way
273 */
274 public void addNode(Node n) throws IllegalStateException {
275 if (n==null) return;
276 if (incomplete)
277 throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
278 clearCached();
279 n.addReferrer(this);
280 Node[] newNodes = new Node[nodes.length + 1];
281 System.arraycopy(nodes, 0, newNodes, 0, nodes.length);
282 newNodes[nodes.length] = n;
283 nodes = newNodes;
284 }
285
286 /**
287 * Adds a node at position offs.
288 *
289 * @param int offs the offset
290 * @param n the node. Ignored, if null.
291 * @throws IllegalStateException thrown, if this way is marked as incomplete. We can't add a node
292 * to an incomplete way
293 * @throws IndexOutOfBoundsException thrown if offs is out of bounds
294 */
295 public void addNode(int offs, Node n) throws IllegalStateException, IndexOutOfBoundsException {
296 if (n==null) return;
297 if (incomplete)
298 throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
299 clearCached();
300 n.addReferrer(this);
301 Node[] newNodes = new Node[nodes.length + 1];
302 System.arraycopy(nodes, 0, newNodes, 0, offs);
303 System.arraycopy(nodes, offs, newNodes, offs + 1, nodes.length - offs);
304 newNodes[offs] = n;
305 nodes = newNodes;
306 }
307
308 @Override
309 public void setDeleted(boolean deleted) {
310 for (Node n:nodes) {
311 if (deleted) {
312 n.removeReferrer(this);
313 } else {
314 n.addReferrer(this);
315 }
316 }
317 super.setDeleted(deleted);
318 }
319
320
321 public boolean isClosed() {
322 if (incomplete) return false;
323 return nodes.length >= 3 && lastNode() == firstNode();
324 }
325
326 public Node lastNode() {
327 if (incomplete || nodes.length == 0) return null;
328 return nodes[nodes.length-1];
329 }
330
331 public Node firstNode() {
332 if (incomplete || nodes.length == 0) return null;
333 return nodes[0];
334 }
335
336 public boolean isFirstLastNode(Node n) {
337 if (incomplete || nodes.length == 0) return false;
338 return n == firstNode() || n == lastNode();
339 }
340
341 @Override
342 public String getDisplayName(NameFormatter formatter) {
343 return formatter.format(this);
344 }
345
346 public OsmPrimitiveType getType() {
347 return OsmPrimitiveType.WAY;
348 }
349}
Note: See TracBrowser for help on using the repository browser.