RectangleIntersects.java
/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.operation.predicate;
import java.util.Iterator;
import java.util.List;
import org.locationtech.jts.algorithm.RectangleLineIntersector;
import org.locationtech.jts.algorithm.locate.SimplePointInAreaLocator;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.util.LinearComponentExtracter;
import org.locationtech.jts.geom.util.ShortCircuitedGeometryVisitor;
/**
* 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, including {@link GeometryCollection}s.
* <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(Polygon rectangle, Geometry b)
{
RectangleIntersects rp = new RectangleIntersects(rectangle);
return rp.intersects(b);
}
private Polygon rectangle;
private Envelope rectEnv;
/**
* Create a new intersects computer for a rectangle.
*
* @param rectangle
* a rectangular Polygon
*/
public RectangleIntersects(Polygon rectangle)
{
this.rectangle = rectangle;
rectEnv = rectangle.getEnvelopeInternal();
}
/**
* 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(Geometry geom)
{
if (!rectEnv.intersects(geom.getEnvelopeInternal()))
return false;
/**
* Test if rectangle envelope intersects any component envelope.
* This handles Point components as well
*/
EnvelopeIntersectsVisitor visitor = new EnvelopeIntersectsVisitor(rectEnv);
visitor.applyTo(geom);
if (visitor.intersects())
return true;
/**
* Test if any rectangle vertex is contained in the target geometry
*/
GeometryContainsPointVisitor ecpVisitor = new GeometryContainsPointVisitor(rectangle);
ecpVisitor.applyTo(geom);
if (ecpVisitor.containsPoint())
return true;
/**
* Test if any target geometry line segment intersects the rectangle
*/
RectangleIntersectsSegmentVisitor riVisitor = new RectangleIntersectsSegmentVisitor(rectangle);
riVisitor.applyTo(geom);
if (riVisitor.intersects())
return true;
return false;
}
}
/**
* 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 Envelope rectEnv;
private boolean intersects = false;
public EnvelopeIntersectsVisitor(Envelope 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 intersects;
}
protected void visit(Geometry element)
{
Envelope elementEnv = element.getEnvelopeInternal();
// disjoint => no intersection
if (!rectEnv.intersects(elementEnv)) {
return;
}
// rectangle contains target env => must intersect
if (rectEnv.contains(elementEnv)) {
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() >= rectEnv.getMinX()
&& elementEnv.getMaxX() <= rectEnv.getMaxX()) {
intersects = true;
return;
}
if (elementEnv.getMinY() >= rectEnv.getMinY()
&& elementEnv.getMaxY() <= rectEnv.getMaxY()) {
intersects = true;
return;
}
}
protected boolean isDone()
{
return intersects;
}
}
/**
* 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 CoordinateSequence rectSeq;
private Envelope rectEnv;
private boolean containsPoint = false;
public GeometryContainsPointVisitor(Polygon rectangle)
{
this.rectSeq = rectangle.getExteriorRing().getCoordinateSequence();
rectEnv = rectangle.getEnvelopeInternal();
}
/**
* 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 containsPoint;
}
protected void visit(Geometry geom)
{
// if test geometry is not polygonal this check is not needed
if (!(geom instanceof Polygon))
return;
// skip if envelopes do not intersect
Envelope elementEnv = geom.getEnvelopeInternal();
if (!rectEnv.intersects(elementEnv))
return;
// test each corner of rectangle for inclusion
Coordinate rectPt = new Coordinate();
for (int i = 0; i < 4; i++) {
rectSeq.getCoordinate(i, rectPt);
if (!elementEnv.contains(rectPt))
continue;
// check rect point in poly (rect is known not to touch polygon at this
// point)
if (SimplePointInAreaLocator.containsPointInPolygon(rectPt,
(Polygon) geom)) {
containsPoint = true;
return;
}
}
}
protected boolean isDone()
{
return containsPoint;
}
}
/**
* 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 Envelope rectEnv;
private RectangleLineIntersector rectIntersector;
private boolean hasIntersection = false;
/**
* Creates a visitor for checking rectangle intersection
* with segments
*
* @param rectangle the query rectangle
*/
public RectangleIntersectsSegmentVisitor(Polygon rectangle)
{
rectEnv = rectangle.getEnvelopeInternal();
rectIntersector = new RectangleLineIntersector(rectEnv);
}
/**
* Reports whether any segment intersection exists.
*
* @return true if a segment intersection exists
* or false if no segment intersection exists
*/
public boolean intersects()
{
return hasIntersection;
}
protected void visit(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.
*/
Envelope elementEnv = geom.getEnvelopeInternal();
if (!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)
List lines = LinearComponentExtracter.getLines(geom);
checkIntersectionWithLineStrings(lines);
}
private void checkIntersectionWithLineStrings(List lines)
{
for (Iterator i = lines.iterator(); i.hasNext(); ) {
LineString testLine = (LineString) i.next();
checkIntersectionWithSegments(testLine);
if (hasIntersection)
return;
}
}
private void checkIntersectionWithSegments(LineString testLine)
{
CoordinateSequence seq1 = testLine.getCoordinateSequence();
Coordinate p0 = seq1.createCoordinate();
Coordinate p1 = seq1.createCoordinate();
for (int j = 1; j < seq1.size(); j++) {
seq1.getCoordinate(j - 1, p0);
seq1.getCoordinate(j, p1);
if (rectIntersector.intersects(p0, p1)) {
hasIntersection = true;
return;
}
}
}
protected boolean isDone()
{
return hasIntersection;
}
}