1 /*
2  * Copyright (c) 2019 Martin Davis.
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
8  * and the Eclipse Distribution License is available at
9  *
10  * http://www.eclipse.org/org/documents/edl-v10.php.
11  */
12 package org.locationtech.jts.operation.overlayng;
13 
14 import java.util.Comparator;
15 import java.util.List;
16 
17 import org.locationtech.jts.edgegraph.HalfEdge;
18 import org.locationtech.jts.geom.Coordinate;
19 import org.locationtech.jts.geom.CoordinateArrays;
20 import org.locationtech.jts.geom.CoordinateList;
21 import org.locationtech.jts.io.WKTWriter;
22 
23 class OverlayEdge extends HalfEdge {
24 
25   /**
26    * Creates a single OverlayEdge.
27    *
28    * @param pts
29    * @param lbl
30    * @param direction
31    *
32    * @return a new edge based on the given coordinates and direction
33    */
createEdge(Coordinate[] pts, OverlayLabel lbl, boolean direction)34   public static OverlayEdge createEdge(Coordinate[] pts, OverlayLabel lbl, boolean direction)
35   {
36     Coordinate origin;
37     Coordinate dirPt;
38     if (direction) {
39       origin = pts[0];
40       dirPt = pts[1];
41     }
42     else {
43       int ilast = pts.length - 1;
44       origin = pts[ilast];
45       dirPt = pts[ilast-1];
46     }
47     return new OverlayEdge(origin, dirPt, direction, lbl, pts);
48   }
49 
createEdgePair(Coordinate[] pts, OverlayLabel lbl)50   public static OverlayEdge createEdgePair(Coordinate[] pts, OverlayLabel lbl)
51   {
52     OverlayEdge e0 = OverlayEdge.createEdge(pts, lbl, true);
53     OverlayEdge e1 = OverlayEdge.createEdge(pts, lbl, false);
54     e0.link(e1);
55     return e0;
56   }
57 
58   /**
59    * Gets a {@link Comparator} which sorts by the origin Coordinates.
60    *
61    * @return a Comparator sorting by origin coordinate
62    */
nodeComparator()63   public static Comparator<OverlayEdge> nodeComparator() {
64     return new Comparator<OverlayEdge>() {
65       @Override
66       public int compare(OverlayEdge e1, OverlayEdge e2) {
67         return e1.orig().compareTo(e2.orig());
68       }
69     };
70   }
71 
72   private Coordinate[] pts;
73 
74   /**
75    * <code>true</code> indicates direction is forward along segString
76    * <code>false</code> is reverse direction
77    * The label must be interpreted accordingly.
78    */
79   private boolean direction;
80   private Coordinate dirPt;
81   private OverlayLabel label;
82 
83   private boolean isInResultArea = false;
84   private boolean isInResultLine = false;
85   private boolean isVisited = false;
86 
87   /**
88    * Link to next edge in the result ring.
89    * The origin of the edge is the dest of this edge.
90    */
91   private OverlayEdge nextResultEdge;
92 
93   private OverlayEdgeRing edgeRing;
94 
95   private MaximalEdgeRing maxEdgeRing;
96 
97   private OverlayEdge nextResultMaxEdge;
98 
99 
100   public OverlayEdge(Coordinate orig, Coordinate dirPt, boolean direction, OverlayLabel label, Coordinate[] pts) {
101     super(orig);
102     this.dirPt = dirPt;
103     this.direction = direction;
104     this.pts = pts;
105     this.label = label;
106   }
107 
108   public boolean isForward() {
109     return direction;
110   }
111   public Coordinate directionPt() {
112     return dirPt;
113   }
114 
115   public OverlayLabel getLabel() {
116     return label;
117   }
118 
119   public int getLocation(int index, int position) {
120     return label.getLocation(index, position, direction);
121   }
122 
123   public Coordinate getCoordinate() {
124     return orig();
125   }
126 
127   public Coordinate[] getCoordinates() {
128     return pts;
129   }
130 
131   public Coordinate[] getCoordinatesOriented() {
132     if (direction) {
133       return pts;
134     }
135     Coordinate[] copy = pts.clone();
136     CoordinateArrays.reverse(copy);
137     return copy;
138   }
139 
140   /**
141    * Adds the coordinates of this edge to the given list,
142    * in the direction of the edge.
143    * Duplicate coordinates are removed
144    * (which means that this is safe to use for a path
145    * of connected edges in the topology graph).
146    *
147    * @param coords the coordinate list to add to
148    */
149   public void addCoordinates(CoordinateList coords)
150   {
151     boolean isFirstEdge = coords.size() > 0;
152     if (direction) {
153       int startIndex = 1;
154       if (isFirstEdge) startIndex = 0;
155       for (int i = startIndex; i < pts.length; i++) {
156         coords.add(pts[i], false);
157       }
158     }
159     else { // is backward
160       int startIndex = pts.length - 2;
161       if (isFirstEdge) startIndex = pts.length - 1;
162       for (int i = startIndex; i >= 0; i--) {
163         coords.add(pts[i], false);
164       }
165     }
166   }
167 
168   /**
169    * Gets the symmetric pair edge of this edge.
170    *
171    * @return the symmetric pair edge
172    */
173   public OverlayEdge symOE() {
174     return (OverlayEdge) sym();
175   }
176 
177   /**
178    * Gets the next edge CCW around the origin of this edge,
179    * with the same origin.
180    * If the origin vertex has degree 1 then this is the edge itself.
181    *
182    * @return the next edge around the origin
183    */
184   public OverlayEdge oNextOE() {
185     return (OverlayEdge) oNext();
186   }
187 
188   public boolean isInResultArea() {
189     return isInResultArea;
190   }
191 
192   public boolean isInResultAreaBoth() {
193     return isInResultArea && symOE().isInResultArea;
194   }
195 
196   public void unmarkFromResultAreaBoth() {
197     isInResultArea = false;
198     symOE().isInResultArea = false;
199   }
200 
201   public void markInResultArea() {
202     isInResultArea  = true;
203   }
204 
205   public void markInResultAreaBoth() {
206     isInResultArea  = true;
207     symOE().isInResultArea = true;
208   }
209 
210   public boolean isInResultLine() {
211     return isInResultLine;
212   }
213 
214   public void markInResultLine() {
215     isInResultLine  = true;
216     symOE().isInResultLine = true;
217   }
218 
219   public boolean isInResult() {
220     return isInResultArea || isInResultLine;
221   }
222 
223   public boolean isInResultEither() {
224     return isInResult() || symOE().isInResult();
225   }
226 
227   void setNextResult(OverlayEdge e) {
228     // Assert: e.orig() == this.dest();
229     nextResultEdge = e;
230   }
231 
232   public OverlayEdge nextResult() {
233     return nextResultEdge;
234   }
235 
236   public boolean isResultLinked() {
237     return nextResultEdge != null;
238   }
239 
240   void setNextResultMax(OverlayEdge e) {
241     // Assert: e.orig() == this.dest();
242     nextResultMaxEdge = e;
243   }
244 
245   public OverlayEdge nextResultMax() {
246     return nextResultMaxEdge;
247   }
248 
249   public boolean isResultMaxLinked() {
250     return nextResultMaxEdge != null;
251   }
252 
253   public boolean isVisited() {
254     return isVisited;
255   }
256 
257   private void markVisited() {
258     isVisited = true;
259   }
260 
261   public void markVisitedBoth() {
262     markVisited();
263     symOE().markVisited();
264   }
265 
266   public void setEdgeRing(OverlayEdgeRing edgeRing) {
267     this.edgeRing = edgeRing;
268   }
269 
270   public OverlayEdgeRing getEdgeRing() {
271     return edgeRing;
272   }
273 
274   public MaximalEdgeRing getEdgeRingMax() {
275     return maxEdgeRing;
276   }
277 
278   public void setEdgeRingMax(MaximalEdgeRing maximalEdgeRing) {
279     maxEdgeRing = maximalEdgeRing;
280   }
281 
282   public String toString() {
283     Coordinate orig = orig();
284     Coordinate dest = dest();
285     String dirPtStr = (pts.length > 2)
286         ? ", " + WKTWriter.format(directionPt())
287             : "";
288 
289     return "OE( "+ WKTWriter.format(orig)
290         + dirPtStr
291         + " .. " + WKTWriter.format(dest)
292         + " ) "
293         + label.toString(direction)
294         + resultSymbol()
295         + " / Sym: " + symOE().getLabel().toString(symOE().direction)
296         + symOE().resultSymbol()
297         ;
298   }
299 
300   private String resultSymbol() {
301     if (isInResultArea) return " resA";
302     if (isInResultLine) return " resL";
303     return "";
304   }
305 
306 
307 
308 
309 }
310