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

Last change on this file since 6233 was 6140, checked in by Don-vip, 11 years ago

fix #8951 - fix clearing of primitive metadata

  • Property svn:eol-style set to native
File size: 22.5 KB
RevLine 
[298]1// License: GPL. Copyright 2007 by Immanuel Scholz and others
[1]2package org.openstreetmap.josm.data.osm;
3
[2204]4import static org.openstreetmap.josm.tools.I18n.tr;
5
[1]6import java.util.ArrayList;
[86]7import java.util.Arrays;
[1]8import java.util.List;
[3630]9import java.util.Set;
[4671]10import java.util.HashSet;
[1]11
[3032]12import org.openstreetmap.josm.Main;
[4321]13import org.openstreetmap.josm.data.coor.LatLon;
[4100]14import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
[8]15import org.openstreetmap.josm.data.osm.visitor.Visitor;
[5059]16import org.openstreetmap.josm.gui.DefaultNameFormatter;
[1862]17import org.openstreetmap.josm.tools.CopyList;
[529]18import org.openstreetmap.josm.tools.Pair;
[8]19
[1]20/**
[5408]21 * One full way, consisting of a list of way {@link Node nodes}.
[1]22 *
23 * @author imi
[5408]24 * @since 64
[1]25 */
[4098]26public final class Way extends OsmPrimitive implements IWay {
[1]27
[1169]28 /**
29 * All way nodes in this way
[1910]30 *
[1169]31 */
[2204]32 private Node[] nodes = new Node[0];
[2437]33 private BBox bbox;
[7]34
[1862]35 /**
36 *
37 * You can modify returned list but changes will not be propagated back
38 * to the Way. Use {@link #setNodes(List)} to update this way
39 * @return Nodes composing the way
40 * @since 1862
41 */
42 public List<Node> getNodes() {
[2204]43 return new CopyList<Node>(nodes);
[1862]44 }
45
46 /**
[2204]47 * Set new list of nodes to way. This method is preferred to multiple calls to addNode/removeNode
48 * and similar methods because nodes are internally saved as array which means lower memory overhead
49 * but also slower modifying operations.
[1910]50 * @param nodes New way nodes. Can be null, in that case all way nodes are removed
[1862]51 * @since 1862
52 */
53 public void setNodes(List<Node> nodes) {
[3348]54 boolean locked = writeLock();
55 try {
56 for (Node node:this.nodes) {
57 node.removeReferrer(this);
[5552]58 node.clearCachedStyle();
[3348]59 }
[2407]60
[3348]61 if (nodes == null) {
62 this.nodes = new Node[0];
63 } else {
64 this.nodes = nodes.toArray(new Node[nodes.size()]);
65 }
[4053]66 for (Node node: this.nodes) {
[3348]67 node.addReferrer(this);
[5552]68 node.clearCachedStyle();
[3348]69 }
70
[3943]71 clearCachedStyle();
[3348]72 fireNodesChanged();
73 } finally {
74 writeUnlock(locked);
[1910]75 }
[1862]76 }
77
78 /**
[4053]79 * Prevent directly following identical nodes in ways.
80 */
81 private List<Node> removeDouble(List<Node> nodes) {
82 Node last = null;
83 int count = nodes.size();
[4054]84 for(int i = 0; i < count && count > 2;) {
[4053]85 Node n = nodes.get(i);
86 if(last == n) {
87 nodes.remove(i);
88 --count;
[4054]89 } else {
90 last = n;
91 ++i;
[4053]92 }
93 }
94 return nodes;
95 }
96
97 /**
[5847]98 * Replies the number of nodes in this way.
[2204]99 *
[5847]100 * @return the number of nodes in this way.
[1862]101 * @since 1862
102 */
[4098]103 @Override
[1862]104 public int getNodesCount() {
[2204]105 return nodes.length;
[1862]106 }
[6069]107
[5847]108 /**
109 * Replies the real number of nodes in this way (full number of nodes minus one if this way is closed)
110 *
111 * @return the real number of nodes in this way.
112 * @since 5847
[6069]113 *
[5847]114 * @see #getNodesCount()
115 * @see #isClosed()
116 */
117 public int getRealNodesCount() {
118 int count = getNodesCount();
119 return isClosed() ? count-1 : count;
120 }
[1862]121
122 /**
[1934]123 * Replies the node at position <code>index</code>.
[2204]124 *
[1934]125 * @param index the position
126 * @return the node at position <code>index</code>
127 * @exception IndexOutOfBoundsException thrown if <code>index</code> < 0
[5266]128 * or <code>index</code> >= {@link #getNodesCount()}
[1862]129 * @since 1862
130 */
131 public Node getNode(int index) {
[2204]132 return nodes[index];
[1862]133 }
134
[4098]135 @Override
136 public long getNodeId(int idx) {
137 return nodes[idx].getUniqueId();
138 }
139
[1911]140 /**
141 * Replies true if this way contains the node <code>node</code>, false
142 * otherwise. Replies false if <code>node</code> is null.
[2204]143 *
[1911]144 * @param node the node. May be null.
145 * @return true if this way contains the node <code>node</code>, false
146 * otherwise
[5408]147 * @since 1911
[1911]148 */
149 public boolean containsNode(Node node) {
150 if (node == null) return false;
[3348]151
152 Node[] nodes = this.nodes;
[6104]153 for (Node n : nodes) {
154 if (n.equals(node))
[2204]155 return true;
156 }
157 return false;
[1911]158 }
159
[4671]160 /**
161 * Return nodes adjacent to <code>node</code>
162 *
163 * @param node the node. May be null.
164 * @return Set of nodes adjacent to <code>node</code>
[5408]165 * @since 4671
[4671]166 */
167 public Set<Node> getNeighbours(Node node) {
168 HashSet<Node> neigh = new HashSet<Node>();
169
170 if (node == null) return neigh;
171
172 Node[] nodes = this.nodes;
173 for (int i=0; i<nodes.length; i++) {
174 if (nodes[i].equals(node)) {
175 if (i > 0)
176 neigh.add(nodes[i-1]);
177 if (i < nodes.length-1)
178 neigh.add(nodes[i+1]);
179 }
180 }
181 return neigh;
182 }
183
[5408]184 /**
[6069]185 * Replies the ordered {@link List} of chunks of this way. Each chunk is replied as a {@link Pair} of {@link Node nodes}.
186 * @param sort If true, the nodes of each pair are sorted as defined by {@link Pair#sort}.
187 * If false, Pair.a and Pair.b are in the way order (i.e for a given Pair(n), Pair(n-1).b == Pair(n).a, Pair(n).b == Pair(n+1).a, etc.)
[5408]188 * @return The ordered list of chunks of this way.
189 * @since 3348
190 */
[3348]191 public List<Pair<Node,Node>> getNodePairs(boolean sort) {
192 List<Pair<Node,Node>> chunkSet = new ArrayList<Pair<Node,Node>>();
[2578]193 if (isIncomplete()) return chunkSet;
[1169]194 Node lastN = null;
[3348]195 Node[] nodes = this.nodes;
196 for (Node n : nodes) {
[529]197 if (lastN == null) {
[1169]198 lastN = n;
199 continue;
200 }
201 Pair<Node,Node> np = new Pair<Node,Node>(lastN, n);
202 if (sort) {
203 Pair.sort(np);
204 }
205 chunkSet.add(np);
206 lastN = n;
207 }
208 return chunkSet;
209 }
[529]210
[6009]211 @Override public void accept(Visitor visitor) {
[1169]212 visitor.visit(this);
213 }
[4431]214
[6009]215 @Override public void accept(PrimitiveVisitor visitor) {
[4100]216 visitor.visit(this);
217 }
[66]218
[2284]219 protected Way(long id, boolean allowNegative) {
220 super(id, allowNegative);
221 }
222
[1169]223 /**
[5408]224 * Contructs a new {@code Way} with id 0.
225 * @since 86
[2070]226 */
[5408]227 public Way() {
[2284]228 super(0, false);
[2070]229 }
230
231 /**
[5408]232 * Contructs a new {@code Way} from an existing {@code Way}.
233 * @param original The original {@code Way} to be identically cloned. Must not be null
[6140]234 * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}. If {@code false}, does nothing
[5408]235 * @since 2410
[2410]236 */
[6140]237 public Way(Way original, boolean clearMetadata) {
[2410]238 super(original.getUniqueId(), true);
239 cloneFrom(original);
[6140]240 if (clearMetadata) {
241 clearOsmMetadata();
[2410]242 }
243 }
244
245 /**
[5408]246 * Contructs a new {@code Way} from an existing {@code Way} (including its id).
247 * @param original The original {@code Way} to be identically cloned. Must not be null
248 * @since 86
[1169]249 */
[1934]250 public Way(Way original) {
[2410]251 this(original, false);
[1169]252 }
253
254 /**
[5408]255 * Contructs a new {@code Way} for the given id. If the id > 0, the way is marked
[2620]256 * as incomplete. If id == 0 then way is marked as new
[2204]257 *
[2620]258 * @param id the id. >= 0 required
[5408]259 * @throws IllegalArgumentException if id < 0
260 * @since 343
[1169]261 */
[2070]262 public Way(long id) throws IllegalArgumentException {
[2284]263 super(id, false);
[1169]264 }
265
[2284]266 /**
[5408]267 * Contructs a new {@code Way} with given id and version.
268 * @param id the id. >= 0 required
269 * @param version the version
270 * @throws IllegalArgumentException if id < 0
271 * @since 2620
[2620]272 */
[5408]273 public Way(long id, int version) throws IllegalArgumentException {
[2932]274 super(id, version, false);
[2620]275 }
276
[2363]277 @Override
[2405]278 public void load(PrimitiveData data) {
[3348]279 boolean locked = writeLock();
280 try {
281 super.load(data);
[2284]282
[3348]283 WayData wayData = (WayData) data;
[2284]284
[3348]285 List<Node> newNodes = new ArrayList<Node>(wayData.getNodes().size());
286 for (Long nodeId : wayData.getNodes()) {
287 Node node = (Node)getDataSet().getPrimitiveById(nodeId, OsmPrimitiveType.NODE);
288 if (node != null) {
289 newNodes.add(node);
290 } else
291 throw new AssertionError("Data consistency problem - way with missing node detected");
292 }
293 setNodes(newNodes);
294 } finally {
295 writeUnlock(locked);
[2284]296 }
297 }
298
[5408]299 @Override
300 public WayData save() {
[2284]301 WayData data = new WayData();
302 saveCommonAttributes(data);
[2741]303 for (Node node:nodes) {
[2284]304 data.getNodes().add(node.getUniqueId());
305 }
306 return data;
307 }
308
[5408]309 @Override
310 public void cloneFrom(OsmPrimitive osm) {
[3348]311 boolean locked = writeLock();
312 try {
313 super.cloneFrom(osm);
314 Way otherWay = (Way)osm;
315 setNodes(otherWay.getNodes());
316 } finally {
317 writeUnlock(locked);
318 }
[1169]319 }
[86]320
[5408]321 @Override
322 public String toString() {
[2578]323 String nodesDesc = isIncomplete()?"(incomplete)":"nodes=" + Arrays.toString(nodes);
[2363]324 return "{Way id=" + getUniqueId() + " version=" + getVersion()+ " " + getFlagsAsString() + " " + nodesDesc + "}";
[86]325 }
326
[1690]327 @Override
328 public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
[6105]329 if (!(other instanceof Way))
[1690]330 return false;
331 if (! super.hasEqualSemanticAttributes(other))
332 return false;
333 Way w = (Way)other;
[2417]334 if (getNodesCount() != w.getNodesCount()) return false;
335 for (int i=0;i<getNodesCount();i++) {
336 if (! getNode(i).hasEqualSemanticAttributes(w.getNode(i)))
337 return false;
338 }
339 return true;
[1690]340 }
341
[4098]342 @Override
[1169]343 public int compareTo(OsmPrimitive o) {
[1598]344 if (o instanceof Relation)
[1169]345 return 1;
[2555]346 return o instanceof Way ? Long.valueOf(getUniqueId()).compareTo(o.getUniqueId()) : -1;
[1169]347 }
[357]348
[5408]349 /**
350 * Removes the given {@link Node} from this way. Ignored, if n is null.
351 * @param n The node to remove. Ignored, if null
352 * @since 1463
353 */
[1598]354 public void removeNode(Node n) {
[5408]355 if (n == null || isIncomplete()) return;
[3348]356 boolean locked = writeLock();
357 try {
358 boolean closed = (lastNode() == n && firstNode() == n);
359 int i;
360 List<Node> copy = getNodes();
361 while ((i = copy.indexOf(n)) >= 0) {
362 copy.remove(i);
363 }
364 i = copy.size();
365 if (closed && i > 2) {
366 copy.add(copy.get(0));
367 } else if (i >= 2 && i <= 3 && copy.get(0) == copy.get(i-1)) {
368 copy.remove(i-1);
369 }
[4053]370 setNodes(removeDouble(copy));
[5552]371 n.clearCachedStyle();
[3348]372 } finally {
373 writeUnlock(locked);
[1690]374 }
[1463]375 }
376
[5408]377 /**
378 * Removes the given set of {@link Node nodes} from this way. Ignored, if selection is null.
379 * @param selection The selection of nodes to remove. Ignored, if null
380 * @since 5408
381 */
382 public void removeNodes(Set<? extends Node> selection) {
383 if (selection == null || isIncomplete()) return;
[3348]384 boolean locked = writeLock();
385 try {
[3630]386 boolean closed = (lastNode() == firstNode() && selection.contains(lastNode()));
387 List<Node> copy = new ArrayList<Node>();
388
389 for (Node n: nodes) {
390 if (!selection.contains(n)) {
391 copy.add(n);
[3348]392 }
[1690]393 }
[3630]394
395 int i = copy.size();
396 if (closed && i > 2) {
397 copy.add(copy.get(0));
398 } else if (i >= 2 && i <= 3 && copy.get(0) == copy.get(i-1)) {
399 copy.remove(i-1);
400 }
[4053]401 setNodes(removeDouble(copy));
[5552]402 for (Node n : selection) {
403 n.clearCachedStyle();
404 }
[3348]405 } finally {
406 writeUnlock(locked);
[1690]407 }
[1463]408 }
409
[2077]410 /**
411 * Adds a node to the end of the list of nodes. Ignored, if n is null.
[2204]412 *
[5408]413 * @param n the node. Ignored, if null
[2077]414 * @throws IllegalStateException thrown, if this way is marked as incomplete. We can't add a node
415 * to an incomplete way
[5408]416 * @since 1313
[2077]417 */
418 public void addNode(Node n) throws IllegalStateException {
419 if (n==null) return;
[3348]420
421 boolean locked = writeLock();
422 try {
423 if (isIncomplete())
424 throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
[3943]425 clearCachedStyle();
[3348]426 n.addReferrer(this);
427 Node[] newNodes = new Node[nodes.length + 1];
428 System.arraycopy(nodes, 0, newNodes, 0, nodes.length);
429 newNodes[nodes.length] = n;
430 nodes = newNodes;
[5552]431 n.clearCachedStyle();
[3348]432 fireNodesChanged();
433 } finally {
434 writeUnlock(locked);
435 }
[1313]436 }
437
[2077]438 /**
439 * Adds a node at position offs.
[2204]440 *
[5408]441 * @param offs the offset
[2077]442 * @param n the node. Ignored, if null.
443 * @throws IllegalStateException thrown, if this way is marked as incomplete. We can't add a node
444 * to an incomplete way
445 * @throws IndexOutOfBoundsException thrown if offs is out of bounds
[5408]446 * @since 1313
[2077]447 */
448 public void addNode(int offs, Node n) throws IllegalStateException, IndexOutOfBoundsException {
449 if (n==null) return;
[3348]450
451 boolean locked = writeLock();
452 try {
453 if (isIncomplete())
454 throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
455
[3943]456 clearCachedStyle();
[3348]457 n.addReferrer(this);
458 Node[] newNodes = new Node[nodes.length + 1];
459 System.arraycopy(nodes, 0, newNodes, 0, offs);
460 System.arraycopy(nodes, offs, newNodes, offs + 1, nodes.length - offs);
461 newNodes[offs] = n;
462 nodes = newNodes;
[5552]463 n.clearCachedStyle();
[3348]464 fireNodesChanged();
465 } finally {
466 writeUnlock(locked);
467 }
[1313]468 }
469
[2407]470 @Override
471 public void setDeleted(boolean deleted) {
[3348]472 boolean locked = writeLock();
473 try {
474 for (Node n:nodes) {
475 if (deleted) {
476 n.removeReferrer(this);
477 } else {
478 n.addReferrer(this);
479 }
[5552]480 n.clearCachedStyle();
[2407]481 }
[3348]482 fireNodesChanged();
483 super.setDeleted(deleted);
484 } finally {
485 writeUnlock(locked);
[2407]486 }
487 }
488
[4100]489 @Override
[1598]490 public boolean isClosed() {
[2578]491 if (isIncomplete()) return false;
[3348]492
493 Node[] nodes = this.nodes;
494 return nodes.length >= 3 && nodes[nodes.length-1] == nodes[0];
[1190]495 }
[6069]496
[5490]497 /**
498 * Determines if this way denotes an area (closed way with at least three distinct nodes).
499 * @return {@code true} if this way is closed and contains at least three distinct nodes
500 * @see #isClosed
501 * @since 5490
502 */
503 public boolean isArea() {
504 if (this.nodes.length >= 4 && isClosed()) {
505 Node distinctNode = null;
506 for (int i=1; i<nodes.length-1; i++) {
507 if (distinctNode == null && nodes[i] != nodes[0]) {
508 distinctNode = nodes[i];
509 } else if (distinctNode != null && nodes[i] != nodes[0] && nodes[i] != distinctNode) {
510 return true;
511 }
512 }
513 }
514 return false;
515 }
[1423]516
[4682]517 /**
518 * Returns the last node of this way.
519 * The result equals <tt>{@link #getNode getNode}({@link #getNodesCount getNodesCount} - 1)</tt>.
520 * @return the last node of this way
[5408]521 * @since 1400
[4682]522 */
[1400]523 public Node lastNode() {
[3348]524 Node[] nodes = this.nodes;
[2578]525 if (isIncomplete() || nodes.length == 0) return null;
[2204]526 return nodes[nodes.length-1];
[1400]527 }
[1423]528
[4682]529 /**
530 * Returns the first node of this way.
531 * The result equals {@link #getNode getNode}{@code (0)}.
532 * @return the first node of this way
[5408]533 * @since 1400
[4682]534 */
[1400]535 public Node firstNode() {
[3348]536 Node[] nodes = this.nodes;
[2578]537 if (isIncomplete() || nodes.length == 0) return null;
[2204]538 return nodes[0];
[1400]539 }
[1423]540
[5408]541 /**
542 * Replies true if the given node is the first or the last one of this way, false otherwise.
543 * @param n The node to test
544 * @return true if the {@code n} is the first or the last node, false otherwise.
545 * @since 1400
546 */
[1400]547 public boolean isFirstLastNode(Node n) {
[3348]548 Node[] nodes = this.nodes;
[2578]549 if (isIncomplete() || nodes.length == 0) return false;
[3348]550 return n == nodes[0] || n == nodes[nodes.length -1];
[1400]551 }
[1990]552
[5408]553 /**
554 * Replies true if the given node is an inner node of this way, false otherwise.
555 * @param n The node to test
556 * @return true if the {@code n} is an inner node, false otherwise.
557 * @since 3515
558 */
[3515]559 public boolean isInnerNode(Node n) {
560 Node[] nodes = this.nodes;
561 if (isIncomplete() || nodes.length <= 2) return false;
562 /* circular ways have only inner nodes, so return true for them! */
563 if (n == nodes[0] && n == nodes[nodes.length-1]) return true;
[5408]564 for (int i = 1; i < nodes.length - 1; ++i) {
565 if (nodes[i] == n) return true;
[3515]566 }
567 return false;
568 }
569
[5408]570 @Override
[1990]571 public String getDisplayName(NameFormatter formatter) {
572 return formatter.format(this);
573 }
[2399]574
[4098]575 @Override
[2399]576 public OsmPrimitiveType getType() {
577 return OsmPrimitiveType.WAY;
578 }
[2419]579
[3844]580 @Override
581 public OsmPrimitiveType getDisplayType() {
582 return isClosed() ? OsmPrimitiveType.CLOSEDWAY : OsmPrimitiveType.WAY;
583 }
584
[2970]585 private void checkNodes() {
[2963]586 DataSet dataSet = getDataSet();
587 if (dataSet != null) {
[3348]588 Node[] nodes = this.nodes;
[2963]589 for (Node n: nodes) {
590 if (n.getDataSet() != dataSet)
[5059]591 throw new DataIntegrityProblemException("Nodes in way must be in the same dataset",
592 tr("Nodes in way must be in the same dataset"));
[3254]593 if (n.isDeleted())
[5059]594 throw new DataIntegrityProblemException("Deleted node referenced: " + toString(),
595 "<html>" + tr("Deleted node referenced by {0}", DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(this)) + "</html>");
[2963]596 }
[3254]597 if (Main.pref.getBoolean("debug.checkNullCoor", true)) {
[3032]598 for (Node n: nodes) {
[5408]599 if (n.isVisible() && !n.isIncomplete() && (n.getCoor() == null || n.getEastNorth() == null))
[5694]600 throw new DataIntegrityProblemException("Complete visible node with null coordinates: " + toString(),
[5059]601 "<html>" + tr("Complete node {0} with null coordinates in way {1}",
602 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(n),
603 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(this)) + "</html>");
[3032]604 }
605 }
[2970]606 }
607 }
608
609 private void fireNodesChanged() {
610 checkNodes();
611 if (getDataSet() != null) {
[2437]612 getDataSet().fireWayNodesChanged(this);
[2419]613 }
614 }
[2427]615
616 @Override
[2970]617 public void setDataset(DataSet dataSet) {
618 super.setDataset(dataSet);
619 checkNodes();
620 }
621
622 @Override
[2427]623 public BBox getBBox() {
[2437]624 if (getDataSet() == null)
625 return new BBox(this);
626 if (bbox == null) {
627 bbox = new BBox(this);
628 }
[3153]629 return new BBox(bbox);
[2427]630 }
[2437]631
632 @Override
633 public void updatePosition() {
634 bbox = new BBox(this);
635 }
[2587]636
[5408]637 /**
638 * Replies true if this way has incomplete nodes, false otherwise.
639 * @return true if this way has incomplete nodes, false otherwise.
640 * @since 2587
641 */
[2609]642 public boolean hasIncompleteNodes() {
[3348]643 Node[] nodes = this.nodes;
[5408]644 for (Node node : nodes) {
[2609]645 if (node.isIncomplete())
646 return true;
[2591]647 }
[2609]648 return false;
[2587]649 }
650
651 @Override
652 public boolean isUsable() {
653 return super.isUsable() && !hasIncompleteNodes();
654 }
[2678]655
656 @Override
657 public boolean isDrawable() {
658 return super.isDrawable() && !hasIncompleteNodes();
659 }
[4138]660
[5408]661 /**
662 * Replies the length of the way, in metres, as computed by {@link LatLon#greatCircleDistance}.
663 * @return The length of the way, in metres
664 * @since 4138
665 */
[4138]666 public double getLength() {
667 double length = 0;
668 Node lastN = null;
669 for (Node n:nodes) {
[4321]670 if (lastN != null) {
[5187]671 LatLon lastNcoor = lastN.getCoor();
[5490]672 LatLon coor = n.getCoor();
[5187]673 if (lastNcoor != null && coor != null) {
674 length += coor.greatCircleDistance(lastNcoor);
[4321]675 }
676 }
[4138]677 lastN = n;
678 }
679 return length;
680 }
[5199]681
682 /**
683 * Tests if this way is a oneway.
[6069]684 * @return {@code 1} if the way is a oneway,
[5408]685 * {@code -1} if the way is a reversed oneway,
686 * {@code 0} otherwise.
687 * @since 5199
[5199]688 */
689 public int isOneway() {
690 String oneway = get("oneway");
691 if (oneway != null) {
692 if ("-1".equals(oneway)) {
693 return -1;
694 } else {
695 Boolean isOneway = OsmUtils.getOsmBoolean(oneway);
696 if (isOneway != null && isOneway) {
697 return 1;
698 }
699 }
700 }
701 return 0;
702 }
703
[5408]704 /**
705 * Replies the first node of this way, respecting or not its oneway state.
[5694]706 * @param respectOneway If true and if this way is a reversed oneway, replies the last node. Otherwise, replies the first node.
[5408]707 * @return the first node of this way, according to {@code respectOneway} and its oneway state.
708 * @since 5199
709 */
[5199]710 public Node firstNode(boolean respectOneway) {
711 return !respectOneway || isOneway() != -1 ? firstNode() : lastNode();
712 }
713
[5408]714 /**
715 * Replies the last node of this way, respecting or not its oneway state.
[5694]716 * @param respectOneway If true and if this way is a reversed oneway, replies the first node. Otherwise, replies the last node.
[5408]717 * @return the last node of this way, according to {@code respectOneway} and its oneway state.
718 * @since 5199
719 */
[5199]720 public Node lastNode(boolean respectOneway) {
721 return !respectOneway || isOneway() != -1 ? lastNode() : firstNode();
722 }
[1]723}
Note: See TracBrowser for help on using the repository browser.