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