/** * make sure outer ring is CCW and holes are CW * * @param p polygon to check */ Polygon makeGoodSHAPEPolygon(Polygon p) { LinearRing outer; LinearRing[] holes = new LinearRing[p.getNumInteriorRing()]; Coordinate[] coords; coords = p.getExteriorRing().getCoordinates(); if (cga.isCCW(coords)) { outer = reverseRing((LinearRing) p.getExteriorRing()); } else { outer = (LinearRing) p.getExteriorRing(); } for (int t = 0; t < p.getNumInteriorRing(); t++) { coords = p.getInteriorRingN(t).getCoordinates(); if (!(cga.isCCW(coords))) { holes[t] = reverseRing((LinearRing) p.getInteriorRingN(t)); } else { holes[t] = (LinearRing) p.getInteriorRingN(t); } } return new Polygon(outer, holes, new PrecisionModel(), 0); }
private void validate(final Geometry geom, final List<ValidationResult> validationErrors) { if (geom.isEmpty()) { return; } if (geom instanceof GeometryCollection) { final GeometryCollection gc = (GeometryCollection) geom; for (int numGeom = 0; numGeom < gc.getNumGeometries(); numGeom++) { validate(gc.getGeometryN(numGeom), validationErrors); } } final ValidationResult result = new ValidationResult(); result.setWkt(geom.toText()); final List<String> messages = new ArrayList<String>(); if (!geom.isValid()) { messages.add("Error en topología básica"); } if (!geom.isSimple()) { messages.add("No es una geometría simple"); } if (repeatedPointTester.hasRepeatedPoint(geom)) { messages.add("Se encuentran vértices repetidos"); } if (geom instanceof Polygon) { final Polygon polygon = (Polygon) geom; if (CGAlgorithms.isCCW(polygon.getExteriorRing().getCoordinates())) { messages.add("Error en orientación del polígono"); } else { for (int numRing = 0; numRing < polygon.getNumInteriorRing(); numRing++) { if (!CGAlgorithms.isCCW(polygon.getInteriorRingN(numRing).getCoordinates())) { messages.add("Error en orientación del polígono en anillos interiores"); break; } } } if (!validateMinPolygonArea(geom)) { messages.add("Error en validación mínima de area de un polígono"); } } if (!validateMinSegmentLength(geom)) { messages.add("Error en validación mínima de longitud de segmento"); } if (!messages.isEmpty()) { result.setMessages(messages); validationErrors.add(result); } }
/** * Determines the orientation of a LineSegment relative to this segment. The concept of * orientation is specified as follows: Given two line segments A and L, * * <ul * <li> * A is to the left of a segment L if A lies wholly in the closed half-plane lying to the left * of L * <li>A is to the right of a segment L if A lies wholly in the closed half-plane lying to the * right of L * <li>otherwise, A has indeterminate orientation relative to L. This happens if A is collinear * with L or if A crosses the line determined by L. * </ul> * * @param seg the LineSegment to compare * @return 1 if <code>seg</code> is to the left of this segment * @return -1 if <code>seg</code> is to the right of this segment * @return 0 if <code>seg</code> is collinear to or crosses this segment */ public int orientationIndex(LineSegment seg) { int orient0 = CGAlgorithms.orientationIndex(p0, p1, seg.p0); int orient1 = CGAlgorithms.orientationIndex(p0, p1, seg.p1); // this handles the case where the points are L or collinear if (orient0 >= 0 && orient1 >= 0) return Math.max(orient0, orient1); // this handles the case where the points are R or collinear if (orient0 <= 0 && orient1 <= 0) return Math.max(orient0, orient1); // points lie on opposite sides ==> indeterminate orientation return 0; }
private void findRightmostEdgeAtVertex() { /** * The rightmost point is an interior vertex, so it has a segment on either side of it. If these * segments are both above or below the rightmost point, we need to determine their relative * orientation to decide which is rightmost. */ Coordinate[] pts = minDe.getEdge().getCoordinates(); Assert.isTrue( minIndex > 0 && minIndex < pts.length, "rightmost point expected to be interior vertex of edge"); Coordinate pPrev = pts[minIndex - 1]; Coordinate pNext = pts[minIndex + 1]; int orientation = CGAlgorithms.computeOrientation(minCoord, pNext, pPrev); boolean usePrev = false; // both segments are below min point if (pPrev.y < minCoord.y && pNext.y < minCoord.y && orientation == CGAlgorithms.COUNTERCLOCKWISE) { usePrev = true; } else if (pPrev.y > minCoord.y && pNext.y > minCoord.y && orientation == CGAlgorithms.CLOCKWISE) { usePrev = true; } // if both segments are on the same side, do nothing - either is safe // to select as a rightmost segment if (usePrev) { minIndex = minIndex - 1; } }
/** * Find the innermost enclosing shell EdgeRing containing the argument EdgeRing, if any. The * innermost enclosing ring is the <i>smallest</i> enclosing ring. The algorithm used depends on * the fact that: <br> * ring A contains ring B iff envelope(ring A) contains envelope(ring B) <br> * This routine is only safe to use if the chosen point of the hole is known to be properly * contained in a shell (which is guaranteed to be the case if the hole does not touch its shell) * * @return containing EdgeRing, if there is one or null if no containing EdgeRing is found */ public static EdgeRing findEdgeRingContaining(EdgeRing testEr, List shellList) { LinearRing testRing = testEr.getRing(); Envelope testEnv = testRing.getEnvelopeInternal(); Coordinate testPt = testRing.getCoordinateN(0); EdgeRing minShell = null; Envelope minShellEnv = null; for (Iterator it = shellList.iterator(); it.hasNext(); ) { EdgeRing tryShell = (EdgeRing) it.next(); LinearRing tryShellRing = tryShell.getRing(); Envelope tryShellEnv = tryShellRing.getEnvelopeInternal(); // the hole envelope cannot equal the shell envelope // (also guards against testing rings against themselves) if (tryShellEnv.equals(testEnv)) continue; // hole must be contained in shell if (!tryShellEnv.contains(testEnv)) continue; testPt = CoordinateArrays.ptNotInList(testRing.getCoordinates(), tryShellRing.getCoordinates()); boolean isContained = false; if (CGAlgorithms.isPointInRing(testPt, tryShellRing.getCoordinates())) isContained = true; // check if this new containing ring is smaller than the current minimum ring if (isContained) { if (minShell == null || minShellEnv.contains(tryShellEnv)) { minShell = tryShell; minShellEnv = minShell.getRing().getEnvelopeInternal(); } } } return minShell; }
/** * Sets the orientation of an array of coordinates. * * @param coord the coordinates to inspect * @param desiredOrientation CGAlgorithms.CLOCKWISE or CGAlgorithms.COUNTERCLOCKWISE * @return a new array with entries in reverse order, if the orientation is incorrect; otherwise, * the original array */ public static Coordinate[] ensureOrientation(Coordinate[] coord, int desiredOrientation) { if (coord.length == 0) { return coord; } int orientation = cga.isCCW(coord) ? CGAlgorithms.COUNTERCLOCKWISE : CGAlgorithms.CLOCKWISE; if (orientation != desiredOrientation) { Coordinate[] reverse = (Coordinate[]) coord.clone(); CoordinateArrays.reverse(reverse); return reverse; } return coord; }
private void addBufferCoordinates() { if (m_segment0 == null || m_segment1 == null || m_buffer0 == null || m_buffer1 == null) return; final int orientation = CGAlgorithms.computeOrientation(m_segment0.p0, m_segment0.p1, m_segment1.p1); final boolean outsideTurn = (orientation == CGAlgorithms.CLOCKWISE && m_side == Position.LEFT) || (orientation == CGAlgorithms.COUNTERCLOCKWISE && m_side == Position.RIGHT); // REMARK: we add only points at the current buffer vertex (i.e. the common point of the two // segments); // the very first and very last vertex must be added separately // last crd of last segment is added separately final boolean addStartPoint = true; if (orientation == 0) { addCollinear(addStartPoint); } else if (outsideTurn) { addOutsideTurn(orientation, addStartPoint); } else { addInsideTurn(); } }
/** * Check if it's CW. * * @param linearRing * @return true if the ring has a clock wise orientation */ private boolean isCW(final LinearRing linearRing) { Coordinate[] ringCoord = linearRing.getCoordinates(); return !CGAlgorithms.isCCW(ringCoord); }
private static boolean isClockwiseRectangle(Geometry geo) { return geo != null && geo.isRectangle() && !CGAlgorithms.isCCW(geo.getCoordinates()); }
/** * Computes the perpendicular distance between the (infinite) line defined by this line segment * and a point. * * @return the perpendicular distance between the defined line and the given point */ public double distancePerpendicular(Coordinate p) { return CGAlgorithms.distancePointLinePerpendicular(p, p0, p1); }
/** * Computes the distance between this line segment and a given point. * * @return the distance from this segment to the given point */ public double distance(Coordinate p) { return CGAlgorithms.distancePointLine(p, p0, p1); }
/** * Computes the distance between this line segment and another segment. * * @return the distance to the other segment */ public double distance(LineSegment ls) { return CGAlgorithms.distanceLineLine(p0, p1, ls.p0, ls.p1); }
/** * Determines the orientation index of a {@link Coordinate} relative to this segment. The * orientation index is as defined in {@link CGAlgorithms#computeOrientation}. * * @param p the coordinate to compare * @return 1 (LEFT) if <code>p</code> is to the left of this segment * @return -1 (RIGHT) if <code>p</code> is to the right of this segment * @return 0 (COLLINEAR) if <code>p</code> is collinear with this segment * @see CGAlgorithms#computeOrientation(Coordinate, Coordinate, Coordinate) */ public int orientationIndex(Coordinate p) { return CGAlgorithms.orientationIndex(p0, p1, p); }
public static boolean edgesIntersect(final Edge e1, final Edge e2) { return CGAlgorithms.distanceLineLine(e1.start, e1.end, e2.start, e2.end) <= 0.0; }
/** * Tests whether this ring is a hole. Due to the way the edges in the polyongization graph are * linked, a ring is a hole if it is oriented counter-clockwise. * * @return <code>true</code> if this ring is a hole */ public boolean isHole() { LinearRing ring = getRing(); return CGAlgorithms.isCCW(ring.getCoordinates()); }