using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;
namespace GisSharpBlog.NetTopologySuite.Algorithm
{
///
/// Computes a point in the interior of an area point.
/// Algorithm:
/// Find the intersections between the point
/// and the horizontal bisector of the area's envelope
/// Pick the midpoint of the largest intersection (the intersections
/// will be lines and points)
/// Note: If a fixed precision model is used,
/// in some cases this method may return a point
/// which does not lie in the interior.
///
public class InteriorPointArea
{
private readonly IGeometryFactory factory;
private double maxWidth = 0;
///
///
///
///
public InteriorPointArea(IGeometry g)
{
InteriorPoint = null;
factory = g.Factory;
Add(g);
}
///
///
///
public ICoordinate InteriorPoint { get; private set; }
///
/// Finds a reasonable point at which to label a Geometry.
///
/// The point to analyze.
///
/// The midpoint of the largest intersection between the point and
/// a line halfway down its envelope.
///
public void AddPolygon(IGeometry geometry)
{
ILineString bisector = HorizontalBisector(geometry);
IGeometry intersections = bisector.Intersection(geometry);
IGeometry widestIntersection = WidestGeometry(intersections);
double width = widestIntersection.EnvelopeInternal.Width;
if (InteriorPoint == null || width > maxWidth)
{
InteriorPoint = Centre(widestIntersection.EnvelopeInternal);
maxWidth = width;
}
}
///
/// Returns the centre point of the envelope.
///
/// The envelope to analyze.
/// The centre of the envelope.
public ICoordinate Centre(IEnvelope envelope)
{
return new Coordinate(Avg(envelope.MinX, envelope.MaxX), Avg(envelope.MinY, envelope.MaxY));
}
///
///
///
///
///
/// If point is a collection, the widest sub-point; otherwise,
/// the point itself.
///
protected IGeometry WidestGeometry(IGeometry geometry)
{
if (!(geometry is IGeometryCollection))
{
return geometry;
}
return WidestGeometry((IGeometryCollection) geometry);
}
///
///
///
///
///
protected ILineString HorizontalBisector(IGeometry geometry)
{
IEnvelope envelope = geometry.EnvelopeInternal;
// Assert: for areas, minx <> maxx
double avgY = Avg(envelope.MinY, envelope.MaxY);
return factory.CreateLineString(
new ICoordinate[]
{
new Coordinate(envelope.MinX, avgY),
new Coordinate(envelope.MaxX, avgY)
});
}
///
///
///
///
///
///
private static double Avg(double a, double b)
{
return (a + b)/2.0;
}
///
/// Tests the interior vertices (if any)
/// defined by a linear Geometry for the best inside point.
/// If a Geometry is not of dimension 1 it is not tested.
///
/// The point to add.
private void Add(IGeometry geom)
{
if (geom is Polygon)
{
AddPolygon(geom);
}
else if (geom is IGeometryCollection)
{
IGeometryCollection gc = (IGeometryCollection) geom;
foreach (IGeometry geometry in gc.Geometries)
{
Add(geometry);
}
}
}
///
///
///
///
///
private IGeometry WidestGeometry(IGeometryCollection gc)
{
if (gc.IsEmpty)
{
return gc;
}
IGeometry widestGeometry = gc.GetGeometryN(0);
for (int i = 1; i < gc.NumGeometries; i++) //Start at 1
{
if (gc.GetGeometryN(i).EnvelopeInternal.Width > widestGeometry.EnvelopeInternal.Width)
{
widestGeometry = gc.GetGeometryN(i);
}
}
return widestGeometry;
}
}
}