CoverageEdge.java
/*
* Copyright (c) 2022 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.coverage;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateArrays;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineSegment;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.io.WKTWriter;
/**
* An edge of a polygonal coverage formed from all or a section of a polygon ring.
* An edge may be a free ring, which is a ring which has no node points
* (i.e. does not share a vertex with any other rings in the parent coverage).
*
* @author mdavis
*
*/
class CoverageEdge {
public static final int RING_COUNT_INNER = 2;
public static final int RING_COUNT_OUTER = 1;
public static CoverageEdge createEdge(Coordinate[] ring, boolean isPrimary) {
Coordinate[] pts = extractEdgePoints(ring, 0, ring.length - 1);
CoverageEdge edge = new CoverageEdge(pts, isPrimary, true);
return edge;
}
public static CoverageEdge createEdge(Coordinate[] ring, int start, int end, boolean isPrimary) {
Coordinate[] pts = extractEdgePoints(ring, start, end);
CoverageEdge edge = new CoverageEdge(pts, isPrimary, false);
return edge;
}
private static Coordinate[] extractEdgePoints(Coordinate[] ring, int start, int end) {
int size = start < end
? end - start + 1
: ring.length - start + end;
Coordinate[] pts = new Coordinate[size];
int iring = start;
for (int i = 0; i < size; i++) {
pts[i] = ring[iring].copy();
iring += 1;
if (iring >= ring.length) iring = 1;
}
return pts;
}
/**
* Computes a key segment for a ring.
* The key is the segment starting at the lowest vertex,
* towards the lowest adjacent distinct vertex.
*
* @param ring a linear ring
* @return a LineSegment representing the key
*/
public static LineSegment key(Coordinate[] ring) {
// find lowest vertex index
int indexLow = 0;
for (int i = 1; i < ring.length - 1; i++) {
if (ring[indexLow].compareTo(ring[i]) < 0)
indexLow = i;
}
Coordinate key0 = ring[indexLow];
// find distinct adjacent vertices
Coordinate adj0 = findDistinctPoint(ring, indexLow, true, key0);
Coordinate adj1 = findDistinctPoint(ring, indexLow, false, key0);
Coordinate key1 = adj0.compareTo(adj1) < 0 ? adj0 : adj1;
return new LineSegment(key0, key1);
}
/**
* Computes a distinct key for a section of a linear ring.
*
* @param ring the linear ring
* @param start index of the start of the section
* @param end end index of the end of the section
* @return a LineSegment representing the key
*/
public static LineSegment key(Coordinate[] ring, int start, int end) {
//-- endpoints are distinct in a line edge
Coordinate end0 = ring[start];
Coordinate end1 = ring[end];
boolean isForward = 0 > end0.compareTo(end1);
Coordinate key0, key1;
if (isForward) {
key0 = end0;
key1 = findDistinctPoint(ring, start, true, key0);
}
else {
key0 = end1;
key1 = findDistinctPoint(ring, end, false, key0);
}
return new LineSegment(key0, key1);
}
private static Coordinate findDistinctPoint(Coordinate[] pts, int index, boolean isForward, Coordinate pt) {
int inc = isForward ? 1 : -1;
int i = index;
do {
if (! pts[i].equals2D(pt)) {
return pts[i];
}
// increment index with wrapping
i += inc;
if (i < 0) {
i = pts.length - 1;
}
else if (i > pts.length - 1) {
i = 0;
}
} while (i != index);
throw new IllegalStateException("Edge does not contain distinct points");
}
private Coordinate[] pts;
private int ringCount = 0;
private boolean isFreeRing = true;
private boolean isPrimary = true;
private int adjacentIndex0 = -1;
private int adjacentIndex1 = -1;
public CoverageEdge(Coordinate[] pts, boolean isPrimary, boolean isFreeRing) {
this.pts = pts;
this.isPrimary = isPrimary;
this.isFreeRing = isFreeRing;
}
public void incRingCount() {
ringCount++;
}
public int getRingCount() {
return ringCount;
}
public boolean isInner() {
return ringCount == RING_COUNT_INNER;
}
public boolean isOuter() {
return ringCount == RING_COUNT_OUTER;
}
public void setPrimary(boolean isPrimary) {
//-- preserve primary status if set
if (this.isPrimary)
return;
this.isPrimary = isPrimary;
}
public boolean isRemovableRing() {
boolean isRing = CoordinateArrays.isRing(pts);
return isRing && ! isPrimary;
}
/**
* Returns whether this edge is a free ring;
* i.e. one that does not have nodes
* which are anchored because they occur in another ring.
*
* @return true if this is a free ring
*/
public boolean isFreeRing() {
return isFreeRing;
}
public void setCoordinates(Coordinate[] pts) {
this.pts = pts;
}
public Coordinate[] getCoordinates() {
return pts;
}
public Coordinate getEndCoordinate() {
return pts[pts.length - 1];
}
public Coordinate getStartCoordinate() {
return pts[0];
}
public LineString toLineString(GeometryFactory geomFactory) {
return geomFactory.createLineString(getCoordinates());
}
public String toString() {
return WKTWriter.toLineString(pts);
}
public void addIndex(int index) {
//TODO: keep information about which element is L and R?
// assert: at least one elementIndex is unset (< 0)
if (adjacentIndex0 < 0) {
adjacentIndex0 = index;
}
else {
adjacentIndex1 = index;
}
}
public int getAdjacentIndex(int index) {
if (index == 0)
return adjacentIndex0;
return adjacentIndex1;
}
public boolean hasAdjacentIndex(int index) {
if (index == 0)
return adjacentIndex0 >= 0;
return adjacentIndex1 >= 0;
}
}