Class RectangleContains

  1 /*
  2  * Copyright (c) 2016 Vivid Solutions.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12  
 13 package org.locationtech.jts.operation.predicate;
 14  
 15 import org.locationtech.jts.geom.Coordinate;
 16 import org.locationtech.jts.geom.CoordinateSequence;
 17 import org.locationtech.jts.geom.Envelope;
 18 import org.locationtech.jts.geom.Geometry;
 19 import org.locationtech.jts.geom.GeometryCollection;
 20 import org.locationtech.jts.geom.LineString;
 21 import org.locationtech.jts.geom.Point;
 22 import org.locationtech.jts.geom.Polygon;
 23  
 24 /**
 25  * Optimized implementation of the <tt>contains</tt> spatial predicate 
 26  * for cases where the first {@link Geometry} is a rectangle.
 27  * This class works for all input geometries, including
 28  * {@link GeometryCollection}s.
 29  * <p>
 30  * As a further optimization,
 31  * this class can be used to test 
 32  * many geometries against a single
 33  * rectangle in a slightly more efficient way.
 34  *
 35  * @version 1.7
 36  */
 37 public class RectangleContains {
 38  
 39   /**
 40    * Tests whether a rectangle contains a given geometry.
 41    * 
 42    * @param rectangle a rectangular Polygon
 43    * @param b a Geometry of any type
 44    * @return true if the geometries intersect
 45    */
 46   public static boolean contains(Polygon rectangle, Geometry b)
 47   {
 48     RectangleContains rc = new RectangleContains(rectangle);
 49     return rc.contains(b);
 50   }
 51  
 52   private Envelope rectEnv;
 53  
 54   /**
 55    * Create a new contains computer for two geometries.
 56    *
 57    * @param rectangle a rectangular geometry
 58    */
 59   public RectangleContains(Polygon rectangle) {
 60     rectEnv = rectangle.getEnvelopeInternal();
 61   }
 62  
 63   public boolean contains(Geometry geom)
 64   {
 65     // the test geometry must be wholly contained in the rectangle envelope
 66     if (! rectEnv.contains(geom.getEnvelopeInternal()))
 67       return false;
 68     
 69     /**
 70      * Check that geom is not contained entirely in the rectangle boundary.
 71      * According to the somewhat odd spec of the SFS, if this
 72      * is the case the geometry is NOT contained.
 73      */
 74     if (isContainedInBoundary(geom))
 75       return false;
 76     return true;
 77   }
 78  
 79   private boolean isContainedInBoundary(Geometry geom)
 80   {
 81     // polygons can never be wholely contained in the boundary
 82     if (geom instanceof Polygon) return false;
 83     if (geom instanceof Point) return isPointContainedInBoundary((Point) geom);
 84     if (geom instanceof LineString) return isLineStringContainedInBoundary((LineString) geom);
 85  
 86     for (int i = 0; i < geom.getNumGeometries(); i++) {
 87       Geometry comp = geom.getGeometryN(i);
 88       if (! isContainedInBoundary(comp))
 89         return false;
 90     }
 91     return true;
 92   }
 93  
 94   private boolean isPointContainedInBoundary(Point point)
 95   {
 96     return isPointContainedInBoundary(point.getCoordinate());
 97   }
 98  
 99   /**
100    * Tests if a point is contained in the boundary of the target rectangle.
101    * 
102    * @param pt the point to test
103    * @return true if the point is contained in the boundary
104    */
105   private boolean isPointContainedInBoundary(Coordinate pt)
106   {
107     /**
108      * contains = false iff the point is properly contained in the rectangle.
109      * 
110      * This code assumes that the point lies in the rectangle envelope
111      */ 
112     return pt.x == rectEnv.getMinX() 
113             || pt.x == rectEnv.getMaxX()
114             || pt.y == rectEnv.getMinY()
115             || pt.y == rectEnv.getMaxY();
116   }
117  
118   /**
119    * Tests if a linestring is completely contained in the boundary of the target rectangle.
120    * @param line the linestring to test
121    * @return true if the linestring is contained in the boundary
122    */
123   private boolean isLineStringContainedInBoundary(LineString line)
124   {
125     CoordinateSequence seq = line.getCoordinateSequence();
126     Coordinate p0 = new Coordinate();
127     Coordinate p1 = new Coordinate();
128     for (int i = 0; i < seq.size() - 1; i++) {
129       seq.getCoordinate(i, p0);
130       seq.getCoordinate(i + 1, p1);
131  
132       if (! isLineSegmentContainedInBoundary(p0, p1))
133         return false;
134     }
135     return true;
136   }
137  
138   /**
139    * Tests if a line segment is contained in the boundary of the target rectangle.
140    * @param p0 an endpoint of the segment
141    * @param p1 an endpoint of the segment
142    * @return true if the line segment is contained in the boundary
143    */
144   private boolean isLineSegmentContainedInBoundary(Coordinate p0, Coordinate p1)
145   {
146     if (p0.equals(p1))
147       return isPointContainedInBoundary(p0);
148  
149     // we already know that the segment is contained in the rectangle envelope
150     if (p0.x == p1.x) {
151       if (p0.x == rectEnv.getMinX() ||
152           p0.x == rectEnv.getMaxX() )
153         return true;
154     }
155     else if (p0.y == p1.y) {
156       if (p0.y == rectEnv.getMinY() ||
157           p0.y == rectEnv.getMaxY() )
158         return true;
159     }
160     /**
161      * Either
162      *   both x and y values are different
163      * or
164      *   one of x and y are the same, but the other ordinate is not the same as a boundary ordinate
165      *
166      * In either case, the segment is not wholely in the boundary
167      */
168     return false;
169   }
170  
171 }
172