/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2001-2006 Vivid Solutions
* (C) 2001-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotools.geometry.iso.topograph2D;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.geotools.geometry.iso.coordinate.DirectPositionImpl;
import org.geotools.geometry.iso.coordinate.LineStringImpl;
import org.geotools.geometry.iso.coordinate.PointArrayImpl;
import org.geotools.geometry.iso.coordinate.PositionImpl;
import org.geotools.geometry.iso.primitive.CurveImpl;
import org.geotools.geometry.iso.primitive.RingImpl;
import org.geotools.geometry.iso.primitive.SurfaceBoundaryImpl;
import org.geotools.geometry.iso.primitive.SurfaceImpl;
import org.geotools.geometry.iso.util.Assert;
import org.geotools.geometry.iso.util.algorithm2D.CGAlgorithms;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.coordinate.Position;
import org.opengis.geometry.primitive.CurveSegment;
import org.opengis.geometry.primitive.OrientableCurve;
import org.opengis.geometry.primitive.Ring;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
public abstract class EdgeRing {
protected DirectedEdge startDe; // the directed edge which starts the list
// of edges for this EdgeRing
private int maxNodeDegree = -1;
private List edges = new ArrayList(); // the DirectedEdges making up this
// EdgeRing
private List pts = new ArrayList();
private Label label = new Label(Location.NONE); // label stores the
// locations of each
// geometry on the face
// surrounded by this ring
// private LinearRing ring; // the ring created for this EdgeRing
private Ring ring; // the ring created for this EdgeRing
private boolean isHole;
// if non-null, the ring is a hole and this EdgeRing is its containing shell
private EdgeRing shell;
// a list of EdgeRings which are holes in this EdgeRing
private ArrayList holes = new ArrayList();
//protected FeatGeomFactoryImpl mFeatGeomFactory;
protected CoordinateReferenceSystem crs;
protected CGAlgorithms cga;
public EdgeRing(DirectedEdge start, CoordinateReferenceSystem crs,
CGAlgorithms cga) {
this.crs = crs;
this.cga = cga;
this.computePoints(start);
// Ring is now computed (and stored) only when first asked for in the getter method.
// This change fixed a bug where building up a polygon with holes that touch the
// outer ring cause a "non-simple" error to occur before the maximaledgerings can
// be changed to minimaledgerings. isHole still needs to be computed now though.
//this.computeRing();
// build list of direct positions and calculate hole
List<DirectPosition> dpList = new LinkedList<DirectPosition>();
for (int i = 0; i < this.pts.size(); i++) {
double[] doubleCoords = ((Coordinate) this.pts.get(i))
.getCoordinates();
DirectPositionImpl dp = new DirectPositionImpl(crs, doubleCoords);
dpList.add(dp);
}
// See if the Ring is counterclockwise oriented
this.isHole = this.cga.isCCW(dpList);
}
abstract public DirectedEdge getNext(DirectedEdge de);
abstract public void setEdgeRing(DirectedEdge de, EdgeRing er);
public boolean isIsolated() {
return (label.getGeometryCount() == 1);
}
public boolean isHole() {
// computePoints();
return isHole;
}
public Coordinate getCoordinate(int i) {
return (Coordinate) pts.get(i);
}
// If the ring obj is already computed, return it. Otherwise, compute the ring and
// store it for next time.
public Ring getRing() {
this.computeRing();
return this.ring;
}
public Label getLabel() {
return this.label;
}
public boolean isShell() {
return this.shell == null;
}
public EdgeRing getShell() {
return this.shell;
}
public void setShell(EdgeRing shell) {
this.shell = shell;
if (shell != null)
shell.addHole(this);
}
public void addHole(EdgeRing ring) {
this.holes.add(ring);
}
/**
* Creates a Surface based on the given ring and holes
*
* @return the created Surface
*/
// TODO don´t need the geomfactory parameter because it owns this parameter
// as member variable
public SurfaceImpl toPolygon() {
List<Ring> interiorRings = new ArrayList<Ring>();
for (int i = 0; i < holes.size(); i++) {
interiorRings.add(((EdgeRing) holes.get(i)).getRing());
}
SurfaceBoundaryImpl surfaceBoundary = new SurfaceBoundaryImpl(crs,
this.getRing(), interiorRings);
//aGeometryFactory.getPrimitiveFactory().createSurfaceBoundary(this.getRing(),interiorRings);
return new SurfaceImpl(surfaceBoundary); //aGeometryFactory.getPrimitiveFactory().createSurface(surfaceBoundary);
}
/**
* Compute a Ring from the point list previously collected. Test if the ring
* is a hole (i.e. if it is CCW) and set the hole flag accordingly.
*/
private void computeRing() {
if (this.ring != null)
return; // don't compute more than once
// OLD CODE:
// Coordinate[] coord = new Coordinate[pts.size()];
// for (int i = 0; i < pts.size(); i++) {
// coord[i] = (Coordinate) pts.get(i);
// }
//Coordinate[] coord = new Coordinate[this.pts.size()];
List<Position> dpList = new LinkedList<Position>();
for (int i = 0; i < this.pts.size(); i++) {
double[] doubleCoords = ((Coordinate) this.pts.get(i))
.getCoordinates();
Position dp = new PositionImpl( new DirectPositionImpl(crs, doubleCoords) );
dpList.add(dp);
}
// Create List of CurveSegment´s (LineString´s)
LineStringImpl lineString = new LineStringImpl(new PointArrayImpl(
dpList), 0.0);
List<CurveSegment> segments = new ArrayList<CurveSegment>();
segments.add(lineString);
// Create List of OrientableCurve´s (Curve´s)
OrientableCurve curve = new CurveImpl(crs, segments);
List<OrientableCurve> orientableCurves = new ArrayList<OrientableCurve>();
orientableCurves.add(curve);
this.ring = new RingImpl(orientableCurves);
// this.ring = (RingImpl) this.mFeatGeomFactory.getPrimitiveFactory().createRingByDirectPositions(dpList);
// isHole is now calculated in the constructor.
/*
// See if the Ring is counterclockwise oriented
this.isHole = this.cga.isCCW(CoordinateArrays
.toCoordinateArray(this.ring.asDirectPositions()));
*/
// Debug.println( (isHole ? "hole - " : "shell - ") +
// WKTWriter.toLineString(new
// CoordinateArraySequence(ring.getCoordinates())));
}
/**
* Returns the list of DirectedEdges that make up this EdgeRing
*/
public List getEdges() {
return edges;
}
/**
* Collect all the points from the DirectedEdges of this ring into a
* contiguous list
*/
protected void computePoints(DirectedEdge start) {
// System.out.println("buildRing");
startDe = start;
DirectedEdge de = start;
boolean isFirstEdge = true;
do {
Assert.isTrue(de != null, "found null Directed Edge");
if (de.getEdgeRing() == this)
throw new TopologyException(
"Directed Edge visited twice during ring-building at "
+ de.getCoordinate());
edges.add(de);
// Debug.println(de);
// Debug.println(de.getEdge());
Label label = de.getLabel();
Assert.isTrue(label.isArea());
mergeLabel(label);
addPoints(de.getEdge(), de.isForward(), isFirstEdge);
isFirstEdge = false;
setEdgeRing(de, this);
de = getNext(de);
} while (de != startDe);
}
public int getMaxNodeDegree() {
if (maxNodeDegree < 0)
computeMaxNodeDegree();
return maxNodeDegree;
}
private void computeMaxNodeDegree() {
maxNodeDegree = 0;
DirectedEdge de = startDe;
do {
Node node = de.getNode();
int degree = ((DirectedEdgeStar) node.getEdges())
.getOutgoingDegree(this);
if (degree > maxNodeDegree)
maxNodeDegree = degree;
de = getNext(de);
} while (de != startDe);
maxNodeDegree *= 2;
}
public void setInResult() {
DirectedEdge de = startDe;
do {
de.getEdge().setInResult(true);
de = de.getNext();
} while (de != startDe);
}
protected void mergeLabel(Label deLabel) {
mergeLabel(deLabel, 0);
mergeLabel(deLabel, 1);
}
/**
* Merge the RHS label from a DirectedEdge into the label for this EdgeRing.
* The DirectedEdge label may be null. This is acceptable - it results from
* a node which is NOT an intersection node between the Geometries (e.g. the
* end node of a LinearRing). In this case the DirectedEdge label does not
* contribute any information to the overall labelling, and is simply
* skipped.
*/
protected void mergeLabel(Label deLabel, int geomIndex) {
int loc = deLabel.getLocation(geomIndex, org.geotools.geometry.iso.topograph2D.Position.RIGHT);
// no information to be had from this label
if (loc == Location.NONE)
return;
// if there is no current RHS value, set it
if (label.getLocation(geomIndex) == Location.NONE) {
label.setLocation(geomIndex, loc);
return;
}
}
protected void addPoints(Edge edge, boolean isForward, boolean isFirstEdge) {
Coordinate[] edgePts = edge.getCoordinates();
if (isForward) {
int startIndex = 1;
if (isFirstEdge)
startIndex = 0;
for (int i = startIndex; i < edgePts.length; i++) {
pts.add(edgePts[i]);
}
} else { // is backward
int startIndex = edgePts.length - 2;
if (isFirstEdge)
startIndex = edgePts.length - 1;
for (int i = startIndex; i >= 0; i--) {
pts.add(edgePts[i]);
}
}
}
/**
* This method will cause the ring to be computed. It will also check any
* holes, if they have been assigned.
*/
public boolean containsPoint(Coordinate p) {
Ring shell = this.getRing();
// TODO: auskommentiert; anpassen!
// Envelope env = shell.getEnvelopeInternal();
// if (!env.contains(p))
// return false;
// if (!this.cga.isPointInRing(p, shell.getCoordinates()))
// return false;
//
// for (Iterator i = this.holes.iterator(); i.hasNext();) {
// EdgeRing hole = (EdgeRing) i.next();
// if (hole.containsPoint(p))
// return false;
// }
// return true;
return true;
}
}