1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.data.osm;
|
---|
3 |
|
---|
4 | import java.awt.geom.Line2D;
|
---|
5 | import java.lang.reflect.Constructor;
|
---|
6 | import java.lang.reflect.InvocationTargetException;
|
---|
7 | import java.util.Arrays;
|
---|
8 | import java.util.Objects;
|
---|
9 |
|
---|
10 | import 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 | */
|
---|
19 | public 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 | }
|
---|