/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.revolsys.geometry.geomgraph;
import com.revolsys.geometry.algorithm.LineIntersector;
import com.revolsys.geometry.geomgraph.index.MonotoneChainEdge;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.IntersectionMatrix;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.impl.LineStringDouble;
import com.revolsys.util.Exceptions;
/**
* @version 1.7
*/
public class Edge extends GraphComponent implements LineString {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Updates an IM from the label for an edge.
* Handles edges from both L and A geometries.
*/
public static void updateIM(final Label label, final IntersectionMatrix im) {
im.setAtLeastIfValid(label.getLocation(0, Position.ON), label.getLocation(1, Position.ON), 1);
if (label.isArea()) {
im.setAtLeastIfValid(label.getLocation(0, Position.LEFT), label.getLocation(1, Position.LEFT),
2);
im.setAtLeastIfValid(label.getLocation(0, Position.RIGHT),
label.getLocation(1, Position.RIGHT), 2);
}
}
private final Depth depth = new Depth();
private int depthDelta = 0; // the change in area depth from the R to L side
private final EdgeIntersectionList eiList = new EdgeIntersectionList(this);
private BoundingBox env;
private boolean isIsolated = true;
private MonotoneChainEdge mce;
private String name;
private final LineString line;
public Edge(final LineString points) {
this(points, null);
}
public Edge(final LineString line, final Label label) {
this.line = line;
this.label = label;
}
/**
* Add an EdgeIntersection for intersection intIndex.
* An intersection that falls exactly on a vertex of the edge is normalized
* to use the higher of the two possible segmentIndexes
*/
public void addIntersection(final LineIntersector li, final int segmentIndex, final int geomIndex,
final int intIndex) {
final Point intPt = li.getIntersection(intIndex);
int normalizedSegmentIndex = segmentIndex;
double dist = li.getEdgeDistance(geomIndex, intIndex);
// normalize the intersection point location
final int nextSegIndex = normalizedSegmentIndex + 1;
if (nextSegIndex < getVertexCount()) {
final double nextX = getX(nextSegIndex);
final double nextY = getY(nextSegIndex);
// Normalize segment index if intPt falls on vertex
// The check for point equality is 2D only - Z values are ignored
if (intPt.equalsVertex(nextX, nextY)) {
normalizedSegmentIndex = nextSegIndex;
dist = 0.0;
}
}
/**
* Add the intersection point to edge intersection list.
*/
this.eiList.add(intPt.getX(), intPt.getY(), normalizedSegmentIndex, dist);
}
/**
* Adds EdgeIntersections for one or both
* intersections found for a segment of an edge to the edge intersection list.
*/
public void addIntersections(final LineIntersector li, final int segmentIndex,
final int geomIndex) {
for (int i = 0; i < li.getIntersectionCount(); i++) {
addIntersection(li, segmentIndex, geomIndex, i);
}
}
@Override
public Edge clone() {
try {
return (Edge)super.clone();
} catch (final CloneNotSupportedException e) {
throw Exceptions.wrap(e);
}
}
// of this edge
/**
* Update the IM with the contribution for this component.
* A component only contributes if it has a labelling for both parent geometries
*/
@Override
public void computeIM(final IntersectionMatrix im) {
updateIM(this.label, im);
}
/**
* equals is defined to be:
* <p>
* e1 equals e2
* <b>iff</b>
* the coordinates of e1 are the same or the reverse of the coordinates in e2
*/
@Override
public boolean equals(final Object o) {
if (!(o instanceof Edge)) {
return false;
}
final Edge e = (Edge)o;
if (getVertexCount() != e.getVertexCount()) {
return false;
}
boolean isEqualForward = true;
boolean isEqualReverse = true;
int iRev = getVertexCount();
for (int i = 0; i < getVertexCount(); i++) {
if (!getPoint(i).equals(2, e.getPoint(i))) {
isEqualForward = false;
}
if (!getPoint(i).equals(2, e.getPoint(--iRev))) {
isEqualReverse = false;
}
if (!isEqualForward && !isEqualReverse) {
return false;
}
}
return true;
}
@Override
public int getAxisCount() {
return this.line.getAxisCount();
}
@Override
public BoundingBox getBoundingBox() {
if (this.env == null) {
this.env = this.line.getBoundingBox();
}
return this.env;
}
public Edge getCollapsedEdge() {
final LineString points = new LineStringDouble(getPoint(0), getPoint(1));
final Label lineLabel = Label.toLineLabel(this.label);
final Edge edge = new Edge(points, lineLabel);
return edge;
}
@Override
public double getCoordinate(final int vertexIndex, final int axisIndex) {
return this.line.getCoordinate(vertexIndex, axisIndex);
}
@Override
public double[] getCoordinates() {
return this.line.getCoordinates();
}
public Depth getDepth() {
return this.depth;
}
/**
* The depthDelta is the change in depth as an edge is crossed from R to L
* @return the change in depth as the edge is crossed from R to L
*/
public int getDepthDelta() {
return this.depthDelta;
}
public EdgeIntersectionList getEdgeIntersectionList() {
return this.eiList;
}
@Override
public GeometryFactory getGeometryFactory() {
return this.line.getGeometryFactory();
}
public LineString getLine() {
return this.line;
}
public int getMaximumSegmentIndex() {
return getVertexCount() - 1;
}
public MonotoneChainEdge getMonotoneChainEdge() {
if (this.mce == null) {
this.mce = new MonotoneChainEdge(this);
}
return this.mce;
}
@Override
public Point getPoint() {
return this.line.getPoint();
}
@Override
public Point getPoint(final int i) {
return this.line.getPoint(i);
}
@Override
public int getVertexCount() {
return this.line.getVertexCount();
}
@Override
public double getX(final int i) {
return this.line.getX(i);
}
@Override
public double getY(final int i) {
return this.line.getY(i);
}
/**
* An Edge is collapsed if it is an Area edge and it consists of
* two segments which are equal and opposite (eg a zero-width V).
*/
public boolean isCollapsed() {
if (!this.label.isArea()) {
return false;
}
if (getVertexCount() != 3) {
return false;
}
if (getPoint(0).equals(getPoint(2))) {
return true;
}
return false;
}
@Override
public boolean isIsolated() {
return this.isIsolated;
}
/**
* @return true if the coordinate sequences of the Edges are identical
*/
public boolean isPointwiseEqual(final Edge e) {
if (getVertexCount() != e.getVertexCount()) {
return false;
}
for (int i = 0; i < getVertexCount(); i++) {
if (!getPoint(i).equals(2, e.getPoint(i))) {
return false;
}
}
return true;
}
public void setDepthDelta(final int depthDelta) {
this.depthDelta = depthDelta;
}
public void setIsolated(final boolean isIsolated) {
this.isIsolated = isIsolated;
}
public void setName(final String name) {
this.name = name;
}
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append("edge " + this.name + ": ");
buf.append(this.line.toString());
buf.append(" " + this.label + " " + this.depthDelta);
return buf.toString();
}
}