/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2001-2006 Vivid Solutions * (C) 2001-2008, Open Source Geospatial Foundation (OSGeo) * * 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; * version 2.1 of the License. * * 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. */ package org.geotools.geometry.iso.util.algorithm2D; import java.util.Iterator; import java.util.List; import org.geotools.geometry.iso.aggregate.MultiPrimitiveImpl; import org.geotools.geometry.iso.primitive.CurveImpl; import org.geotools.geometry.iso.primitive.PrimitiveImpl; import org.geotools.geometry.iso.primitive.RingImpl; import org.geotools.geometry.iso.primitive.RingImplUnsafe; import org.geotools.geometry.iso.primitive.SurfaceImpl; import org.geotools.geometry.iso.root.GeometryImpl; import org.geotools.geometry.iso.topograph2D.Coordinate; import org.geotools.geometry.iso.topograph2D.GeometryGraph; import org.geotools.geometry.iso.topograph2D.Location; import org.geotools.geometry.iso.topograph2D.util.CoordinateArrays; import org.opengis.geometry.Geometry; import org.opengis.geometry.primitive.Primitive; import org.opengis.geometry.primitive.Ring; /** * Computes the topological relationship ({@link Location}) of a single point * to a {@link Geometry}. The algorithm obeys the SFS Boundary Determination * Rule to determine whether the point lies on the boundary or not. * <p> * Notes: * <ul> * <li>{@link LinearRing}s do not enclose any area - points inside the ring * are still in the EXTERIOR of the ring. * </ul> * Instances of this class are not reentrant. * * * * * @source $URL$ */ public class PointLocator { private boolean isIn; // true if the point lies in or on any Geometry // element private int numBoundaries; // the number of sub-elements whose boundaries // the point lies in public PointLocator() { } /** * Convenience method to test a point for intersection with a Geometry * * @param p * the coordinate to test * @param geom * the Geometry to test * @return <code>true</code> if the point is in the interior or boundary * of the Geometry */ public boolean intersects(Coordinate p, GeometryImpl geom) { return locate(p, geom) != Location.EXTERIOR; } /** * Computes the topological relationship ({@link Location}) of a single * point to a Geometry. It handles both single-element and multi-element * Geometries. The algorithm for multi-part Geometries takes into account * the SFS Boundary Determination Rule. * * @return the {@link Location} of the point relative to the input Geometry */ public int locate(Coordinate p, GeometryImpl geom) { // TODO auskommentiert; checken! // if (geom.isEmpty()) // return Location.EXTERIOR; if (geom instanceof CurveImpl) { return locate(p, (CurveImpl) geom); } else if (geom instanceof SurfaceImpl) { return locate(p, (SurfaceImpl) geom); } isIn = false; numBoundaries = 0; computeLocation(p, geom); if (GeometryGraph.isInBoundary(numBoundaries)) return Location.BOUNDARY; if (numBoundaries > 0 || isIn) return Location.INTERIOR; return Location.EXTERIOR; } private void computeLocation(Coordinate p, GeometryImpl geom) { if (geom instanceof CurveImpl) { updateLocationInfo(locate(p, (CurveImpl) geom)); } else if (geom instanceof SurfaceImpl) { updateLocationInfo(locate(p, (SurfaceImpl) geom)); } else if (geom instanceof MultiPrimitiveImpl) { Iterator<? extends Primitive> iterator = ((MultiPrimitiveImpl)geom).getElements().iterator(); while (iterator.hasNext()) { PrimitiveImpl prim = ((PrimitiveImpl)iterator.next()); updateLocationInfo(locate(p, prim)); } } // else if (geom instanceof MultiLineString) { // MultiLineString ml = (MultiLineString) geom; // for (int i = 0; i < ml.getNumGeometries(); i++) { // LineString l = (LineString) ml.getGeometryN(i); // updateLocationInfo(locate(p, l)); // } // } else if (geom instanceof MultiPolygon) { // MultiPolygon mpoly = (MultiPolygon) geom; // for (int i = 0; i < mpoly.getNumGeometries(); i++) { // Polygon poly = (Polygon) mpoly.getGeometryN(i); // updateLocationInfo(locate(p, poly)); // } // } else if (geom instanceof GeometryCollection) { // Iterator geomi = new GeometryCollectionIterator( // (GeometryCollection) geom); // while (geomi.hasNext()) { // GeometryImpl g2 = (GeometryImpl) geomi.next(); // if (g2 != geom) // computeLocation(p, g2); // } // } } private void updateLocationInfo(int loc) { if (loc == Location.INTERIOR) isIn = true; if (loc == Location.BOUNDARY) numBoundaries++; } private int locate(Coordinate p, CurveImpl curve) { Coordinate[] pt = CoordinateArrays.toCoordinateArray(curve .asDirectPositions()); // Annahme: curve.isClosed() = curve.isCycle() ?? // if (!curve.isClosed()) { if (!curve.getStartPoint().equals(curve.getEndPoint())) { if (p.equals(pt[0]) || p.equals(pt[pt.length - 1])) { return Location.BOUNDARY; } } if (CGAlgorithms.isOnLine(p, pt)) return Location.INTERIOR; return Location.EXTERIOR; } private int locateInPolygonRing(Coordinate p, Ring ring) { // can this test be folded into isPointInRing ? Coordinate[] coord = CoordinateArrays.toCoordinateArray(((RingImplUnsafe)ring) .asDirectPositions()); if (CGAlgorithms.isOnLine(p, coord)) { return Location.BOUNDARY; } if (CGAlgorithms.isPointInRing(p, coord)) return Location.INTERIOR; return Location.EXTERIOR; } private int locate(Coordinate p, SurfaceImpl aSurface) { // if (poly.isEmpty()) // return Location.EXTERIOR; List<Ring> rings = aSurface.getBoundaryRings(); Ring shell = rings.get(0); int shellLoc = locateInPolygonRing(p, shell); if (shellLoc == Location.EXTERIOR) return Location.EXTERIOR; if (shellLoc == Location.BOUNDARY) return Location.BOUNDARY; // now test if the point lies in or on the holes for (int i = 1; i < rings.size(); i++) { Ring hole = rings.get(i); int holeLoc = locateInPolygonRing(p, hole); if (holeLoc == Location.INTERIOR) return Location.EXTERIOR; if (holeLoc == Location.BOUNDARY) return Location.BOUNDARY; } return Location.INTERIOR; // OLD CODE: // if (poly.isEmpty()) // return Location.EXTERIOR; // LinearRing shell = (LinearRing) poly.getExteriorRing(); // // int shellLoc = locateInPolygonRing(p, shell); // if (shellLoc == Location.EXTERIOR) // return Location.EXTERIOR; // if (shellLoc == Location.BOUNDARY) // return Location.BOUNDARY; // // now test if the point lies in or on the holes // for (int i = 0; i < poly.getNumInteriorRing(); i++) { // LinearRing hole = (LinearRing) poly.getInteriorRingN(i); // int holeLoc = locateInPolygonRing(p, hole); // if (holeLoc == Location.INTERIOR) // return Location.EXTERIOR; // if (holeLoc == Location.BOUNDARY) // return Location.BOUNDARY; // } // return Location.INTERIOR; } }