/*
* 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.util.Collection;
import com.revolsys.geometry.algorithm.LineIntersector;
import com.revolsys.geometry.algorithm.RobustLineIntersector;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Point;
/**
* Validates that a collection of {@link SegmentString}s is correctly noded.
* Throws an appropriate exception if an noding error is found.
*
* @version 1.7
*/
public class NodingValidator {
private final LineIntersector li = new RobustLineIntersector();
private final Collection<NodedSegmentString> segStrings;
public NodingValidator(final Collection<NodedSegmentString> segStrings) {
this.segStrings = segStrings;
}
private void checkCollapse(final double x1, final double y1, final double x2, final double y2,
final double x3, final double y3) {
if (x1 == x3 && y1 == y3) {
throw new RuntimeException("found non-noded collapse at "
+ GeometryFactory.DEFAULT_2D.lineString(2, x1, y1, x2, y2, x3, y3));
}
}
/**
* Checks if a segment string contains a segment pattern a-b-a (which implies a self-intersection)
*/
private void checkCollapses() {
for (final NodedSegmentString segment : this.segStrings) {
checkCollapses(segment);
}
}
private void checkCollapses(final NodedSegmentString ss) {
final LineString points = ss.getPoints();
for (int i = 0; i < points.getVertexCount() - 2; i++) {
final double x1 = points.getX(i);
final double y1 = points.getY(i);
final double x2 = points.getX(i + 1);
final double y2 = points.getY(i + 1);
final double x3 = points.getX(i + 2);
final double y3 = points.getY(i + 2);
checkCollapse(x1, y1, x2, y2, x3, y3);
}
}
/**
* Checks for intersections between an endpoint of a segment string
* and an interior vertex of another segment string
*/
private void checkEndPtVertexIntersections() {
for (final NodedSegmentString ss : this.segStrings) {
final double x1 = ss.getX(0);
final double y1 = ss.getX(0);
checkEndPtVertexIntersections(x1, y1, this.segStrings);
final int endIndex = ss.size() - 1;
final double x2 = ss.getX(endIndex);
final double y2 = ss.getY(endIndex);
checkEndPtVertexIntersections(x2, y2, this.segStrings);
}
}
private void checkEndPtVertexIntersections(final double x, final double y,
final Collection<NodedSegmentString> segStrings) {
for (final NodedSegmentString ss : segStrings) {
final LineString pts = ss.getPoints();
for (int j = 1; j < pts.getVertexCount() - 1; j++) {
if (pts.equalsVertex2d(j, x, y)) {
throw new RuntimeException(
"found endpt/interior pt intersection at index " + j + " :pt " + x + "," + y);
}
}
}
}
/**
* Checks all pairs of segments for intersections at an interior point of a segment
*/
private void checkInteriorIntersections() {
for (final NodedSegmentString ss0 : this.segStrings) {
for (final NodedSegmentString ss1 : this.segStrings) {
checkInteriorIntersections(ss0, ss1);
}
}
}
private void checkInteriorIntersections(final NodedSegmentString e0, final int segIndex0,
final SegmentString e1, final int segIndex1) {
if (e0 == e1 && segIndex0 == segIndex1) {
return;
}
final double line1x1 = e0.getX(segIndex0);
final double line1y1 = e0.getY(segIndex0);
final double line1x2 = e0.getX(segIndex0 + 1);
final double line1y2 = e0.getY(segIndex0 + 1);
final double line2x1 = e1.getX(segIndex1);
final double line2y1 = e1.getY(segIndex1);
final double line2x2 = e1.getX(segIndex1 + 1);
final double line2y2 = e1.getY(segIndex1 + 1);
this.li.computeIntersection(line1x1, line1y1, line1x2, line1y2, line2x1, line2y1, line2x2,
line2y2);
if (this.li.hasIntersection()) {
if (this.li.isProper() || hasInteriorIntersection(this.li, line1x1, line1y1, line1x2, line1y2)
|| hasInteriorIntersection(this.li, line2x1, line2y1, line2x2, line2y2)) {
throw new RuntimeException(
"found non-noded intersection at " + line1x1 + "," + line1y1 + "-" + line1x2 + ","
+ line1y2 + " and " + line2x1 + "," + line2y1 + "-" + line2x2 + "," + line2y2);
}
}
}
private void checkInteriorIntersections(final NodedSegmentString ss0, final SegmentString ss1) {
for (int i0 = 0; i0 < ss0.size() - 1; i0++) {
for (int i1 = 0; i1 < ss1.size() - 1; i1++) {
checkInteriorIntersections(ss0, i0, ss1, i1);
}
}
}
public void checkValid() {
// MD - is this call required? Or could it be done in the Interior
// Intersection code?
checkEndPtVertexIntersections();
checkInteriorIntersections();
checkCollapses();
}
/**
*@return true if there is an intersection point which is not an endpoint of the segment p0-p1
*/
private boolean hasInteriorIntersection(final LineIntersector li, final double x1,
final double y1, final double x2, final double y2) {
for (int i = 0; i < li.getIntersectionCount(); i++) {
final Point intPt = li.getIntersection(i);
if (!(intPt.equalsVertex(x1, y1) || intPt.equalsVertex(x2, y2))) {
return true;
}
}
return false;
}
}