source: josm/trunk/src/org/openstreetmap/josm/data/osm/IWaySegment.java@ 18870

Last change on this file since 18870 was 17986, checked in by Don-vip, 3 years ago

fix #20994 - OOBE when removing node of highlighted way (patch by Bjoeni)

File size: 6.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import java.awt.geom.Line2D;
5import java.lang.reflect.Constructor;
6import java.lang.reflect.InvocationTargetException;
7import java.util.Arrays;
8import java.util.Objects;
9
10import org.openstreetmap.josm.tools.Logging;
11
12/**
13 * A segment consisting of 2 consecutive nodes out of a way.
14 * @author Taylor Smock
15 * @param <N> The node type
16 * @param <W> The way type
17 * @since 17862
18 */
19public class IWaySegment<N extends INode, W extends IWay<N>> implements Comparable<IWaySegment<N, W>> {
20
21 private final W way;
22 private final int lowerIndex;
23
24 /**
25 * Returns the way.
26 * @return the way
27 */
28 public W getWay() {
29 return way;
30 }
31
32 /**
33 * Returns the index of the first of the 2 nodes in the way.
34 * @return index of the first of the 2 nodes in the way
35 * @see #getUpperIndex()
36 * @see #getFirstNode()
37 */
38 public int getLowerIndex() {
39 return lowerIndex;
40 }
41
42 /**
43 * Returns the index of the second of the 2 nodes in the way.
44 * @return the index of the second of the 2 nodes in the way
45 * @see #getLowerIndex()
46 * @see #getSecondNode()
47 */
48 public int getUpperIndex() {
49 return lowerIndex + 1;
50 }
51
52 /**
53 * Constructs a new {@code IWaySegment}.
54 * @param w The way
55 * @param i The node lower index
56 * @throws IllegalArgumentException in case of invalid index
57 */
58 public IWaySegment(W w, int i) {
59 way = w;
60 lowerIndex = i;
61 if (i < 0 || i >= w.getNodesCount() - 1) {
62 throw new IllegalArgumentException(toString());
63 }
64 }
65
66 /**
67 * Determines if the segment is usable (node not deleted).
68 * @return {@code true} if the segment is usable
69 * @since 17986
70 */
71 public boolean isUsable() {
72 return getUpperIndex() < way.getNodesCount();
73 }
74
75 /**
76 * Returns the first node of the way segment.
77 * @return the first node
78 */
79 public N getFirstNode() {
80 return way.getNode(getLowerIndex());
81 }
82
83 /**
84 * Returns the second (last) node of the way segment.
85 * @return the second node
86 */
87 public N getSecondNode() {
88 return way.getNode(getUpperIndex());
89 }
90
91 /**
92 * Determines and returns the way segment for the given way and node pair.
93 * @param <N> type of node
94 * @param <W> type of way
95 * @param way way
96 * @param first first node
97 * @param second second node
98 * @return way segment
99 * @throws IllegalArgumentException if the node pair is not part of way
100 */
101 public static <N extends INode, W extends IWay<N>> IWaySegment<N, W> forNodePair(W way, N first, N second) {
102 int endIndex = way.getNodesCount() - 1;
103 while (endIndex > 0) {
104 final int indexOfFirst = way.getNodes().subList(0, endIndex).lastIndexOf(first);
105 if (second.equals(way.getNode(indexOfFirst + 1))) {
106 return new IWaySegment<>(way, indexOfFirst);
107 }
108 endIndex--;
109 }
110 throw new IllegalArgumentException("Node pair is not part of way!");
111 }
112
113 /**
114 * Returns this way segment as complete way.
115 * @return the way segment as {@code Way}
116 * @throws IllegalAccessException See {@link Constructor#newInstance}
117 * @throws IllegalArgumentException See {@link Constructor#newInstance}
118 * @throws InstantiationException See {@link Constructor#newInstance}
119 * @throws InvocationTargetException See {@link Constructor#newInstance}
120 * @throws NoSuchMethodException See {@link Class#getConstructor}
121 */
122 public W toWay()
123 throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
124 // If the number of nodes is 2, then don't bother creating a new way
125 if (this.way.getNodes().size() == 2) {
126 return this.way;
127 }
128 // Since the way determines the generic class, this.way.getClass() is always Class<W>, assuming
129 // that way remains the defining element for the type, and remains final.
130 @SuppressWarnings("unchecked")
131 Class<W> clazz = (Class<W>) this.way.getClass();
132 Constructor<W> constructor;
133 W w;
134 try {
135 // Check for clone constructor
136 constructor = clazz.getConstructor(clazz);
137 w = constructor.newInstance(this.way);
138 } catch (NoSuchMethodException e) {
139 Logging.trace(e);
140 constructor = clazz.getConstructor();
141 w = constructor.newInstance();
142 }
143
144 w.setNodes(Arrays.asList(getFirstNode(), getSecondNode()));
145 return w;
146 }
147
148 @Override
149 public boolean equals(Object o) {
150 if (this == o) return true;
151 if (o == null || getClass() != o.getClass()) return false;
152 IWaySegment<?, ?> that = (IWaySegment<?, ?>) o;
153 return lowerIndex == that.lowerIndex &&
154 Objects.equals(way, that.way);
155 }
156
157 @Override
158 public int hashCode() {
159 return Objects.hash(way, lowerIndex);
160 }
161
162 @Override
163 public int compareTo(IWaySegment o) {
164 final W thisWay;
165 final IWay<?> otherWay;
166 try {
167 thisWay = toWay();
168 otherWay = o == null ? null : o.toWay();
169 } catch (ReflectiveOperationException e) {
170 Logging.error(e);
171 return -1;
172 }
173 return o == null ? -1 : (equals(o) ? 0 : thisWay.compareTo(otherWay));
174 }
175
176 /**
177 * Checks whether this segment crosses other segment
178 *
179 * @param s2 The other segment
180 * @return true if both segments crosses
181 */
182 public boolean intersects(IWaySegment<?, ?> s2) {
183 if (getFirstNode().equals(s2.getFirstNode()) || getSecondNode().equals(s2.getSecondNode()) ||
184 getFirstNode().equals(s2.getSecondNode()) || getSecondNode().equals(s2.getFirstNode()))
185 return false;
186
187 return Line2D.linesIntersect(
188 getFirstNode().getEastNorth().east(), getFirstNode().getEastNorth().north(),
189 getSecondNode().getEastNorth().east(), getSecondNode().getEastNorth().north(),
190 s2.getFirstNode().getEastNorth().east(), s2.getFirstNode().getEastNorth().north(),
191 s2.getSecondNode().getEastNorth().east(), s2.getSecondNode().getEastNorth().north());
192 }
193
194 /**
195 * Checks whether this segment and another way segment share the same points
196 * @param s2 The other segment
197 * @return true if other way segment is the same or reverse
198 */
199 public boolean isSimilar(IWaySegment<?, ?> s2) {
200 return (getFirstNode().equals(s2.getFirstNode()) && getSecondNode().equals(s2.getSecondNode()))
201 || (getFirstNode().equals(s2.getSecondNode()) && getSecondNode().equals(s2.getFirstNode()));
202 }
203
204 @Override
205 public String toString() {
206 return "IWaySegment [way=" + way.getUniqueId() + ", lowerIndex=" + lowerIndex + ']';
207 }
208}
Note: See TracBrowser for help on using the repository browser.