/*
* 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.operation.predicate;
import java.util.Iterator;
import java.util.List;
import com.revolsys.geometry.algorithm.RectangleLineIntersector;
import com.revolsys.geometry.algorithm.locate.SimplePointInAreaLocator;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.Polygon;
import com.revolsys.geometry.model.util.ShortCircuitedGeometryVisitor;
/**
* Tests whether it can be concluded that a rectangle intersects a geometry,
* based on the relationship of the envelope(s) of the geometry.
*
* @author Martin Davis
* @version 1.7
*/
class EnvelopeIntersectsVisitor extends ShortCircuitedGeometryVisitor {
private boolean intersects = false;
private final BoundingBox rectEnv;
public EnvelopeIntersectsVisitor(final BoundingBox rectEnv) {
this.rectEnv = rectEnv;
}
/**
* Reports whether it can be concluded that an intersection occurs,
* or whether further testing is required.
*
* @return true if an intersection must occur
* or false if no conclusion about intersection can be made
*/
public boolean intersects() {
return this.intersects;
}
@Override
protected boolean isDone() {
return this.intersects == true;
}
@Override
protected void visit(final Geometry element) {
final BoundingBox elementEnv = element.getBoundingBox();
// disjoint => no intersection
if (!this.rectEnv.intersects(elementEnv)) {
return;
}
// rectangle contains target env => must intersect
if (this.rectEnv.covers(elementEnv)) {
this.intersects = true;
return;
}
/**
* Since the envelopes intersect and the test element is connected, if the
* test envelope is completely bisected by an edge of the rectangle the
* element and the rectangle must touch (This is basically an application of
* the Jordan Curve Theorem). The alternative situation is that the test
* envelope is "on a corner" of the rectangle envelope, i.e. is not
* completely bisected. In this case it is not possible to make a conclusion
* about the presence of an intersection.
*/
if (elementEnv.getMinX() >= this.rectEnv.getMinX()
&& elementEnv.getMaxX() <= this.rectEnv.getMaxX()) {
this.intersects = true;
return;
}
if (elementEnv.getMinY() >= this.rectEnv.getMinY()
&& elementEnv.getMaxY() <= this.rectEnv.getMaxY()) {
this.intersects = true;
return;
}
}
}
/**
* A visitor which tests whether it can be
* concluded that a geometry contains a vertex of
* a query geometry.
*
* @author Martin Davis
* @version 1.7
*/
class GeometryContainsPointVisitor extends ShortCircuitedGeometryVisitor {
private boolean containsPoint = false;
private final BoundingBox rectEnv;
private final LineString rectSeq;
public GeometryContainsPointVisitor(final Polygon rectangle) {
this.rectSeq = rectangle.getShell();
this.rectEnv = rectangle.getBoundingBox();
}
/**
* Reports whether it can be concluded that a corner point of the rectangle is
* contained in the geometry, or whether further testing is required.
*
* @return true if a corner point is contained
* or false if no conclusion about intersection can be made
*/
public boolean containsPoint() {
return this.containsPoint;
}
@Override
protected boolean isDone() {
return this.containsPoint == true;
}
@Override
protected void visit(final Geometry geom) {
// if test geometry is not polygonal this check is not needed
if (!(geom instanceof Polygon)) {
return;
}
// skip if envelopes do not intersect
final BoundingBox elementEnv = geom.getBoundingBox();
if (!this.rectEnv.intersects(elementEnv)) {
return;
}
// test each corner of rectangle for inclusion
for (int i = 0; i < 4; i++) {
final Point rectPt = this.rectSeq.getPoint(i);
if (!elementEnv.covers(rectPt)) {
continue;
}
// check rect point in poly (rect is known not to touch polygon at this
// point)
if (SimplePointInAreaLocator.containsPointInPolygon(rectPt, (Polygon)geom)) {
this.containsPoint = true;
return;
}
}
}
}
/**
* Implementation of the <tt>intersects</tt> spatial predicate
* optimized for the case where one {@link Geometry} is a rectangle.
* This class works for all
* input geometries.
* <p>
* As a further optimization,
* this class can be used in batch style
* to test many geometries
* against a single rectangle.
*
* @version 1.7
*/
public class RectangleIntersects {
/**
* Tests whether a rectangle intersects a given geometry.
*
* @param rectangle
* a rectangular Polygon
* @param b
* a Geometry of any type
* @return true if the geometries intersect
*/
public static boolean intersects(final Polygon rectangle, final Geometry b) {
final RectangleIntersects rp = new RectangleIntersects(rectangle);
return rp.intersects(b);
}
private final Polygon rectangle;
private final BoundingBox rectEnv;
/**
* Construct a new new intersects computer for a rectangle.
*
* @param rectangle
* a rectangular Polygon
*/
public RectangleIntersects(final Polygon rectangle) {
this.rectangle = rectangle;
this.rectEnv = rectangle.getBoundingBox();
}
/**
* Tests whether the given Geometry intersects
* the query rectangle.
*
* @param geom the Geometry to test (may be of any type)
* @return true if the geometry intersects the query rectangle
*/
public boolean intersects(final Geometry geom) {
if (!this.rectEnv.intersects(geom.getBoundingBox())) {
return false;
}
/**
* Test if rectangle envelope intersects any component envelope.
* This handles Point components as well
*/
final EnvelopeIntersectsVisitor visitor = new EnvelopeIntersectsVisitor(this.rectEnv);
visitor.applyTo(geom);
if (visitor.intersects()) {
return true;
}
/**
* Test if any rectangle vertex is contained in the target geometry
*/
final GeometryContainsPointVisitor ecpVisitor = new GeometryContainsPointVisitor(
this.rectangle);
ecpVisitor.applyTo(geom);
if (ecpVisitor.containsPoint()) {
return true;
}
/**
* Test if any target geometry line segment intersects the rectangle
*/
final RectangleIntersectsSegmentVisitor riVisitor = new RectangleIntersectsSegmentVisitor(
this.rectangle);
riVisitor.applyTo(geom);
if (riVisitor.intersects()) {
return true;
}
return false;
}
}
/**
* A visitor to test for intersection between the query
* rectangle and the line segments of the geometry.
*
* @author Martin Davis
*
*/
class RectangleIntersectsSegmentVisitor extends ShortCircuitedGeometryVisitor {
private boolean hasIntersection = false;
private final BoundingBox rectEnv;
private final RectangleLineIntersector rectIntersector;
/**
* Creates a visitor for checking rectangle intersection
* with segments
*
* @param rectangle the query rectangle
*/
public RectangleIntersectsSegmentVisitor(final Polygon rectangle) {
this.rectEnv = rectangle.getBoundingBox();
this.rectIntersector = new RectangleLineIntersector(this.rectEnv);
}
private void checkIntersectionWithLineStrings(final List lines) {
for (final Iterator i = lines.iterator(); i.hasNext();) {
final LineString testLine = (LineString)i.next();
checkIntersectionWithSegments(testLine);
if (this.hasIntersection) {
return;
}
}
}
private void checkIntersectionWithSegments(final LineString testLine) {
final LineString seq1 = testLine;
for (int j = 1; j < seq1.getVertexCount(); j++) {
final Point p0 = seq1.getPoint(j - 1);
final Point p1 = seq1.getPoint(j);
if (this.rectIntersector.intersects(p0, p1)) {
this.hasIntersection = true;
return;
}
}
}
/**
* Reports whether any segment intersection exists.
*
* @return true if a segment intersection exists
* or false if no segment intersection exists
*/
public boolean intersects() {
return this.hasIntersection;
}
@Override
protected boolean isDone() {
return this.hasIntersection == true;
}
@Override
protected void visit(final Geometry geom) {
/**
* It may be the case that the rectangle and the
* envelope of the geometry component are disjoint,
* so it is worth checking this simple condition.
*/
final BoundingBox elementEnv = geom.getBoundingBox();
if (!this.rectEnv.intersects(elementEnv)) {
return;
}
// check segment intersections
// get all lines from geometry component
// (there may be more than one if it's a multi-ring polygon)
final List lines = geom.getGeometryComponents(LineString.class);
checkIntersectionWithLineStrings(lines);
}
}