/*
* 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.algorithm;
import java.util.Iterator;
import java.util.List;
import com.revolsys.geometry.index.bintree.Bintree;
import com.revolsys.geometry.index.bintree.Interval;
import com.revolsys.geometry.index.chain.MonotoneChain;
import com.revolsys.geometry.index.chain.MonotoneChainBuilder;
import com.revolsys.geometry.index.chain.MonotoneChainSelectAction;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.LinearRing;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.impl.BoundingBoxDoubleXY;
import com.revolsys.geometry.model.segment.LineSegment;
/**
* Implements {@link PointInRing}
* using {@link MonotoneChain}s and a {@link Bintree} index to
* increase performance.
*
* @version 1.7
*
* @see GeometryFactoryIndexedPointInAreaLocator for more general functionality
*/
public class MCPointInRing implements PointInRing {
class MCSelecter extends MonotoneChainSelectAction {
Point p;
public MCSelecter(final Point p) {
this.p = p;
}
@Override
public void select(final LineSegment ls) {
testLineSegment(this.p, ls);
}
}
private int crossings = 0; // number of segment/ray crossings
private final Interval interval = new Interval();
private final LinearRing ring;
private Bintree tree;
public MCPointInRing(final LinearRing ring) {
this.ring = ring;
buildIndex();
}
private void buildIndex() {
// BoundingBox env = ring.getEnvelopeInternal();
this.tree = new Bintree();
final LineString points = this.ring.removeDuplicatePoints();
final List<MonotoneChain> mcList = MonotoneChainBuilder.getChains(points);
for (int i = 0; i < mcList.size(); i++) {
final MonotoneChain mc = mcList.get(i);
final BoundingBox mcEnv = mc.getEnvelope();
this.interval.min = mcEnv.getMinY();
this.interval.max = mcEnv.getMaxY();
this.tree.insert(this.interval, mc);
}
}
@Override
public boolean isInside(final Point pt) {
this.crossings = 0;
// test all segments intersected by ray from pt in positive x direction
final double y = pt.getY();
final BoundingBox rayEnv = new BoundingBoxDoubleXY(-Double.MAX_VALUE, y, Double.MAX_VALUE, y);
this.interval.min = y;
this.interval.max = y;
final List segs = this.tree.query(this.interval);
// System.out.println("query size = " + segs.size());
final MCSelecter mcSelecter = new MCSelecter(pt);
for (final Iterator i = segs.iterator(); i.hasNext();) {
final MonotoneChain mc = (MonotoneChain)i.next();
testMonotoneChain(rayEnv, mcSelecter, mc);
}
/*
* p is inside if number of crossings is odd.
*/
if (this.crossings % 2 == 1) {
return true;
}
return false;
}
private void testLineSegment(final Point p, final LineSegment seg) {
double xInt; // x intersection of segment with ray
/*
* Test if segment crosses ray from test point in positive x direction.
*/
final double x = p.getX();
final double y = p.getY();
final double x1 = seg.getX(0) - x;
final double y1 = seg.getY(0) - y;
final double x2 = seg.getX(1) - x;
final double y2 = seg.getY(1) - y;
if (y1 > 0 && y2 <= 0 || y2 > 0 && y1 <= 0) {
/*
* segment straddles x axis, so compute intersection.
*/
xInt = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2) / (y2 - y1);
// xsave = xInt;
/*
* crosses ray if strictly positive intersection.
*/
if (0.0 < xInt) {
this.crossings++;
}
}
}
private void testMonotoneChain(final BoundingBox rayEnv, final MCSelecter mcSelecter,
final MonotoneChain mc) {
mc.select(rayEnv, mcSelecter);
}
}