/* * 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 v1.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-v10.html * and the Eclipse Distribution License is available at * * http://www.eclipse.org/org/documents/edl-v10.php. */ package test.jts.perf.algorithm; import java.util.Iterator; import java.util.List; import org.locationtech.jts.algorithm.RayCrossingCounter; import org.locationtech.jts.algorithm.locate.PointOnGeometryLocator; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineSegment; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.Location; import org.locationtech.jts.geom.Polygonal; import org.locationtech.jts.geom.util.LinearComponentExtracter; import org.locationtech.jts.index.SpatialIndex; import org.locationtech.jts.index.chain.MonotoneChain; import org.locationtech.jts.index.chain.MonotoneChainBuilder; import org.locationtech.jts.index.chain.MonotoneChainSelectAction; import org.locationtech.jts.index.strtree.STRtree; import org.locationtech.jts.noding.BasicSegmentString; import org.locationtech.jts.noding.SegmentString; /** * Determines the location of {@link Coordinate}s relative to * a {@link Polygonal} geometry, using indexing for efficiency. * This algorithm is suitable for use in cases where * many points will be tested against a given area. * * @author Martin Davis * */ public class MCIndexedPointInAreaLocator implements PointOnGeometryLocator { private Geometry areaGeom; private MCIndexedGeometry index; private double maxXExtent; public MCIndexedPointInAreaLocator(Geometry g) { areaGeom = g; if (! (g instanceof Polygonal)) throw new IllegalArgumentException("Argument must be Polygonal"); buildIndex(g); Envelope env = g.getEnvelopeInternal(); maxXExtent = env.getMaxX() + 1.0; } private void buildIndex(Geometry g) { index = new MCIndexedGeometry(g); } /** * Determines the {@link Location} of a point in an areal {@link Geometry}. * * @param p the point to test * @return the location of the point in the geometry */ public int locate(Coordinate p) { RayCrossingCounter rcc = new RayCrossingCounter(p); MCSegmentCounter mcSegCounter = new MCSegmentCounter(rcc); Envelope rayEnv = new Envelope(p.x, maxXExtent, p.y, p.y); List mcs = index.query(rayEnv); countSegs(rcc, rayEnv, mcs, mcSegCounter); return rcc.getLocation(); } private void countSegs(RayCrossingCounter rcc, Envelope rayEnv, List monoChains, MCSegmentCounter mcSegCounter) { for (Iterator i = monoChains.iterator(); i.hasNext(); ) { MonotoneChain mc = (MonotoneChain) i.next(); mc.select(rayEnv, mcSegCounter); // short-circuit if possible if (rcc.isOnSegment()) return; } } static class MCSegmentCounter extends MonotoneChainSelectAction { RayCrossingCounter rcc; public MCSegmentCounter(RayCrossingCounter rcc) { this.rcc = rcc; } public void select(LineSegment ls) { rcc.countSegment(ls.getCoordinate(0), ls.getCoordinate(1)); } } } class MCIndexedGeometry { private SpatialIndex index= new STRtree(); public MCIndexedGeometry(Geometry geom) { init(geom); } private void init(Geometry geom) { List lines = LinearComponentExtracter.getLines(geom); for (Iterator i = lines.iterator(); i.hasNext(); ) { LineString line = (LineString) i.next(); Coordinate[] pts = line.getCoordinates(); addLine(pts); } } private void addLine(Coordinate[] pts) { SegmentString segStr = new BasicSegmentString(pts, null); List segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr); for (Iterator i = segChains.iterator(); i.hasNext(); ) { MonotoneChain mc = (MonotoneChain) i.next(); index.insert(mc.getEnvelope(), mc); } } public List query(Envelope searchEnv) { return index.query(searchEnv); } }