/*
* 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.noding;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.revolsys.geometry.model.coordinates.list.CoordinatesListUtil;
import com.revolsys.geometry.model.impl.LineStringDouble;
import com.revolsys.geometry.util.Assert;
// INCOMPLETE!
class NodeVertexIterator implements Iterator<SegmentNode> {
private SegmentNode currNode = null;
private SegmentNode nextNode = null;
private final Iterator<SegmentNode> nodeIt;
NodeVertexIterator(final SegmentNodeList nodeList) {
this.nodeIt = nodeList.iterator();
readNextNode();
}
@Override
public boolean hasNext() {
if (this.nextNode == null) {
return false;
}
return true;
}
@Override
public SegmentNode next() {
if (this.currNode == null) {
this.currNode = this.nextNode;
readNextNode();
return this.currNode;
}
// check for trying to read too far
if (this.nextNode == null) {
return null;
}
if (this.nextNode.getSegmentIndex() == this.currNode.getSegmentIndex()) {
this.currNode = this.nextNode;
readNextNode();
return this.currNode;
}
if (this.nextNode.getSegmentIndex() > this.currNode.getSegmentIndex()) {
}
return null;
}
private void readNextNode() {
if (this.nodeIt.hasNext()) {
this.nextNode = this.nodeIt.next();
} else {
this.nextNode = null;
}
}
/**
* Not implemented.
*
*@throws UnsupportedOperationException This method is not implemented.
*/
@Override
public void remove() {
throw new UnsupportedOperationException(getClass().getName());
}
}
/**
* A list of the {@link SegmentNode}s present along a noded {@link SegmentString}.
*
* @version 1.7
*/
public class SegmentNodeList implements Iterable<SegmentNode> {
private final NodedSegmentString edge;
private final Map<SegmentNode, SegmentNode> nodeMap = new TreeMap<>();
public SegmentNodeList(final NodedSegmentString edge) {
this.edge = edge;
}
/**
* Adds an intersection into the list, if it isn't already there.
* The input segmentIndex and dist are expected to be normalized.
*
* @return the SegmentIntersection found or added
*/
public SegmentNode add(final double x, final double y, final int segmentIndex) {
if (!Double.isFinite(x) || !Double.isFinite(y)) {
throw new IllegalArgumentException("Cannot add an empty point to a SegmentNodeList");
} else {
final int segmentOctant = this.edge.getSegmentOctant(segmentIndex);
final SegmentNode newNode = new SegmentNode(this.edge, x, y, segmentIndex, segmentOctant);
SegmentNode node = this.nodeMap.get(newNode);
if (node == null) {
node = newNode;
this.nodeMap.put(node, node);
} else {
// debugging sanity check
final boolean equals = node.equalsVertex(x, y);
if (!equals) {
Assert.isTrue(equals, "Found equal nodes with different coordinates");
}
}
return node;
}
}
/**
* Adds nodes for any collapsed edge pairs.
* Collapsed edge pairs can be caused by inserted nodes, or they can be
* pre-existing in the edge vertex list.
* In order to provide the correct fully noded semantics,
* the vertex at the base of a collapsed pair must also be added as a node.
*/
private void addCollapsedNodes() {
final List<Integer> collapsedVertexIndexes = new ArrayList<>();
findCollapsesFromInsertedNodes(collapsedVertexIndexes);
findCollapsesFromExistingVertices(collapsedVertexIndexes);
// node the collapses
for (final int vertexIndex : collapsedVertexIndexes) {
final double x = this.edge.getX(vertexIndex);
final double y = this.edge.getY(vertexIndex);
add(x, y, vertexIndex);
}
}
/**
* Adds nodes for the first and last points of the edge
*/
private void addEndpoints() {
final int maxSegIndex = this.edge.size() - 1;
final double fromPointX = this.edge.getX(0);
final double fromPointY = this.edge.getY(0);
add(fromPointX, fromPointY, 0);
final double toPointX = this.edge.getX(maxSegIndex);
final double toPointY = this.edge.getY(maxSegIndex);
add(toPointX, toPointY, maxSegIndex);
}
/**
* Creates new edges for all the edges that the intersections in this
* list split the parent edge into.
* Adds the edges to the provided argument list
* (this is so a single list can be used to accumulate all split edges
* for a set of {@link SegmentString}s).
*/
public void addSplitEdges(final Collection edgeList) {
// ensure that the list has entries for the first and last point of the edge
addEndpoints();
addCollapsedNodes();
final Iterator<SegmentNode> it = iterator();
// there should always be at least two entries in the list, since the
// endpoints are nodes
SegmentNode eiPrev = it.next();
while (it.hasNext()) {
final SegmentNode ei = it.next();
final SegmentString newEdge = newSplitEdge(eiPrev, ei);
/*
* if (newEdge.size() < 2) throw new RuntimeException( "created single point edge: " +
* newEdge.toString());
*/
edgeList.add(newEdge);
eiPrev = ei;
}
// checkSplitEdgesCorrectness(testingSplitEdges);
}
private boolean findCollapseIndex(final SegmentNode ei0, final SegmentNode ei1,
final int[] collapsedVertexIndex) {
// only looking for equal nodes
if (!ei0.equals(2, ei1)) {
return false;
}
int numVerticesBetween = ei1.getSegmentIndex() - ei0.getSegmentIndex();
if (!ei1.isInterior()) {
numVerticesBetween--;
}
// if there is a single vertex between the two equal nodes, this is a
// collapse
if (numVerticesBetween == 1) {
collapsedVertexIndex[0] = ei0.getSegmentIndex() + 1;
return true;
}
return false;
}
/**
* Adds nodes for any collapsed edge pairs
* which are pre-existing in the vertex list.
*/
private void findCollapsesFromExistingVertices(final List<Integer> collapsedVertexIndexes) {
for (int i = 0; i < this.edge.size() - 2; i++) {
if (this.edge.equalsVertex2d(i, i + 2)) {
// add base of collapse as node
collapsedVertexIndexes.add(new Integer(i + 1));
}
}
}
/**
* Adds nodes for any collapsed edge pairs caused by inserted nodes
* Collapsed edge pairs occur when the same coordinate is inserted as a node
* both before and after an existing edge vertex.
* To provide the correct fully noded semantics,
* the vertex must be added as a node as well.
*/
private void findCollapsesFromInsertedNodes(final List<Integer> collapsedVertexIndexes) {
final int[] collapsedVertexIndex = new int[1];
final Iterator<SegmentNode> it = iterator();
// there should always be at least two entries in the list, since the
// endpoints are nodes
SegmentNode eiPrev = it.next();
while (it.hasNext()) {
final SegmentNode ei = it.next();
final boolean isCollapsed = findCollapseIndex(eiPrev, ei, collapsedVertexIndex);
if (isCollapsed) {
collapsedVertexIndexes.add(collapsedVertexIndex[0]);
}
eiPrev = ei;
}
}
public NodedSegmentString getEdge() {
return this.edge;
}
/**
* returns an iterator of SegmentNodes
*/
@Override
public Iterator<SegmentNode> iterator() {
return this.nodeMap.values().iterator();
}
/**
* Construct a new new "split edge" with the section of points between
* (and including) the two intersections.
* The label for the new edge is the same as the label for the parent edge.
*/
SegmentString newSplitEdge(final SegmentNode ei0, final SegmentNode ei1) {
// Debug.println("\ncreateSplitEdge"); Debug.print(ei0); Debug.print(ei1);
int npts = ei1.getSegmentIndex() - ei0.getSegmentIndex() + 2;
// if the last intersection point is not equal to the its segment start pt,
// add it to the points list as well.
// (This check is needed because the distance metric is not totally
// reliable!)
// The check for point equality is 2D only - Z values are ignored
final boolean useIntPt1 = ei1.isInterior()
|| !this.edge.equalsVertex(ei1.getSegmentIndex(), ei1.getX(), ei1.getY());
if (!useIntPt1) {
npts--;
}
final int axisCount = this.edge.getPoints().getAxisCount();
final double[] coordinates = new double[npts * axisCount];
int ipt = 0;
CoordinatesListUtil.setCoordinates(coordinates, axisCount, ipt++, ei0);
for (int i = ei0.getSegmentIndex() + 1; i <= ei1.getSegmentIndex(); i++) {
CoordinatesListUtil.setCoordinates(coordinates, axisCount, ipt++, this.edge, i);
}
if (useIntPt1) {
CoordinatesListUtil.setCoordinates(coordinates, axisCount, ipt++, ei1);
}
final LineStringDouble points = new LineStringDouble(axisCount, coordinates);
return new NodedSegmentString(points, this.edge.getData());
}
public void print(final PrintStream out) {
out.println("Intersections:");
for (final SegmentNode node : this) {
node.print(out);
}
}
}