using System.Collections;
using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;
using GisSharpBlog.NetTopologySuite.GeometriesGraph;
namespace GisSharpBlog.NetTopologySuite.Algorithm
{
///
/// Computes the topological relationship (Location) of a single point to a Geometry.
/// The algorithm obeys the SFS boundaryDetermination rule to correctly determine
/// whether the point lies on the boundary or not.
/// Note that instances of this class are not reentrant.
///
public class PointLocator
{
private bool 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
///
/// Initializes a new instance of the class.
///
public PointLocator() {}
///
/// Convenience method to test a point for intersection with a Geometry
///
/// The coordinate to test.
/// The Geometry to test.
/// true if the point is in the interior or boundary of the Geometry.
public bool Intersects(ICoordinate p, IGeometry geom)
{
return Locate(p, geom) != Locations.Exterior;
}
///
/// Computes the topological relationship ({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 boundaryDetermination rule.
///
/// The Location of the point relative to the input Geometry.
public Locations Locate(ICoordinate p, IGeometry geom)
{
if (geom.IsEmpty)
{
return Locations.Exterior;
}
if (geom is ILineString)
{
return Locate(p, (ILineString) geom);
}
else if (geom is IPolygon)
{
return Locate(p, (IPolygon) geom);
}
isIn = false;
numBoundaries = 0;
ComputeLocation(p, geom);
if (GeometryGraph.IsInBoundary(numBoundaries))
{
return Locations.Boundary;
}
if (numBoundaries > 0 || isIn)
{
return Locations.Interior;
}
return Locations.Exterior;
}
///
///
///
///
///
private void ComputeLocation(ICoordinate p, IGeometry geom)
{
if (geom is ILineString)
{
UpdateLocationInfo(Locate(p, (ILineString) geom));
}
else if (geom is Polygon)
{
UpdateLocationInfo(Locate(p, (IPolygon) geom));
}
else if (geom is IMultiLineString)
{
IMultiLineString ml = (IMultiLineString) geom;
foreach (ILineString l in ml.Geometries)
{
UpdateLocationInfo(Locate(p, l));
}
}
else if (geom is IMultiPolygon)
{
IMultiPolygon mpoly = (IMultiPolygon) geom;
foreach (IPolygon poly in mpoly.Geometries)
{
UpdateLocationInfo(Locate(p, poly));
}
}
else if (geom is IGeometryCollection)
{
IEnumerator geomi = new GeometryCollectionEnumerator((IGeometryCollection) geom);
while (geomi.MoveNext())
{
IGeometry g2 = (IGeometry) geomi.Current;
if (g2 != geom)
{
ComputeLocation(p, g2);
}
}
}
}
///
///
///
///
private void UpdateLocationInfo(Locations loc)
{
if (loc == Locations.Interior)
{
isIn = true;
}
if (loc == Locations.Boundary)
{
numBoundaries++;
}
}
///
///
///
///
///
///
private Locations Locate(ICoordinate p, ILineString l)
{
ICoordinate[] pt = l.Coordinates;
if (!l.IsClosed)
{
if (p.Equals(pt[0]) || p.Equals(pt[pt.Length - 1]))
{
return Locations.Boundary;
}
}
if (CGAlgorithms.IsOnLine(p, pt))
{
return Locations.Interior;
}
return Locations.Exterior;
}
///
///
///
///
///
///
private Locations LocateInPolygonRing(ICoordinate p, ILinearRing ring)
{
// can this test be folded into IsPointInRing?
if (CGAlgorithms.IsOnLine(p, ring.Coordinates))
{
return Locations.Boundary;
}
if (CGAlgorithms.IsPointInRing(p, ring.Coordinates))
{
return Locations.Interior;
}
return Locations.Exterior;
}
///
///
///
///
///
///
private Locations Locate(ICoordinate p, IPolygon poly)
{
if (poly.IsEmpty)
{
return Locations.Exterior;
}
ILinearRing shell = poly.Shell;
Locations shellLoc = LocateInPolygonRing(p, shell);
if (shellLoc == Locations.Exterior)
{
return Locations.Exterior;
}
if (shellLoc == Locations.Boundary)
{
return Locations.Boundary;
}
// now test if the point lies in or on the holes
foreach (ILinearRing hole in poly.InteriorRings)
{
Locations holeLoc = LocateInPolygonRing(p, hole);
if (holeLoc == Locations.Interior)
{
return Locations.Exterior;
}
if (holeLoc == Locations.Boundary)
{
return Locations.Boundary;
}
}
return Locations.Interior;
}
}
}