using SharpMap;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Reflection;
using NetTopologySuite.Geometries;
using SharpMap.Rendering;
using SharpMap.Utilities;
namespace GeoAPI.Geometries
{
///
/// Set of extension methods for use of GeoAPI within SharpMap
///
public static class GeoAPIEx
{
///
/// Gets the minimum coordinate of the
///
/// The envelope
/// The minimum coordinate
public static Coordinate Min(this Envelope self)
{
return new Coordinate(self.MinX, self.MinY);
}
///
/// Ensures that a CoordinateSequence forms a valid ring,
/// returning a new closed sequence of the correct length if required.
/// If the input sequence is already a valid ring, it is returned
/// without modification.
/// If the input sequence is too short or is not closed,
/// it is extended with one or more copies of the start point.
///
/// The CoordinateSequenceFactory to use to create the new sequence
/// The sequence to test
/// The original sequence, if it was a valid ring, or a new sequence which is valid.
public static ICoordinateSequence EnsureValidRing(ICoordinateSequenceFactory fact, ICoordinateSequence seq)
{
var n = seq.Count;
// empty sequence is valid
if (n == 0) return seq;
// too short - make a new one
if (n <= 3)
return CreateClosedRing(fact, seq, 4);
var isClosed = Math.Abs(seq.GetOrdinate(0, Ordinate.X) - seq.GetOrdinate(n - 1, Ordinate.X)) < double.Epsilon &&
Math.Abs(seq.GetOrdinate(0, Ordinate.Y) - seq.GetOrdinate(n - 1, Ordinate.Y)) < double.Epsilon;
if (isClosed) return seq;
// make a new closed ring
return CreateClosedRing(fact, seq, n + 1);
}
private static ICoordinateSequence CreateClosedRing(ICoordinateSequenceFactory fact, ICoordinateSequence seq, int size)
{
var newseq = fact.Create(size, seq.Dimension);
int n = seq.Count;
Copy(seq, 0, newseq, 0, n);
// fill remaining coordinates with start point
for (int i = n; i < size; i++)
Copy(seq, 0, newseq, i, 1);
return newseq;
}
///
/// Copies a section of a to another .
/// The sequences may have different dimensions;
/// in this case only the common dimensions are copied.
///
/// The sequence to copy coordinates from
/// The starting index of the coordinates to copy
/// The sequence to which the coordinates should be copied to
/// The starting index of the coordinates in
/// The number of coordinates to copy
public static void Copy(ICoordinateSequence src, int srcPos, ICoordinateSequence dest, int destPos, int length)
{
for (int i = 0; i < length; i++)
CopyCoord(src, srcPos + i, dest, destPos + i);
}
///
/// Copies a coordinate of a to another .
/// The sequences may have different dimensions;
/// in this case only the common dimensions are copied.
///
/// The sequence to copy coordinate from
/// The index of the coordinate to copy
/// The sequence to which the coordinate should be copied to
/// The index of the coordinate in
public static void CopyCoord(ICoordinateSequence src, int srcPos, ICoordinateSequence dest, int destPos)
{
int minDim = Math.Min(src.Dimension, dest.Dimension);
for (int dim = 0; dim < minDim; dim++)
{
var ordinate = (Ordinate)dim;
double value = src.GetOrdinate(srcPos, ordinate);
dest.SetOrdinate(destPos, ordinate, value);
}
}
///
/// Ensures that a CoordinateSequence forms a valid ring,
/// returning a new closed sequence of the correct length if required.
/// If the input sequence is already a valid ring, it is returned
/// without modification.
/// If the input sequence is too short or is not closed,
/// it is extended with one or more copies of the start point.
///
/// List of coordinates
/// The original sequence, if it was a valid ring, or a new sequence which is valid.
public static void EnsureValidRing(this List coordinates)
{
var seq = GeometryServiceProvider.Instance.DefaultCoordinateSequenceFactory.Create(coordinates.ToArray());
seq = EnsureValidRing(GeometryServiceProvider.Instance.DefaultCoordinateSequenceFactory, seq);
if (seq.Count != coordinates.Count)
{
for (int i = coordinates.Count; i < seq.Count; i++)
{
coordinates.Add(seq.GetCoordinate(i));
}
}
}
///
/// Gets the maximum coordinate of the
///
/// The envelope
/// The maximum coordinate
public static Coordinate Max(this Envelope self)
{
return new Coordinate(self.MaxX, self.MaxY);
}
///
/// Adds to coordinate's
///
/// the first coordinate
/// The second coordinate
public static Coordinate Add(this Coordinate self, Coordinate summand)
{
if (self == null && summand == null)
return null;
if (summand == null)
return self;
if (self == null)
return summand;
// for now we only care for 2D
return new Coordinate(self.X + summand.X, self.Y + summand.Y);
}
///
/// Subtracts two coordinates from one another
///
/// The first coordinate
/// The second coordinate
public static Coordinate Subtract(this Coordinate self, Coordinate summand)
{
if (self == null && summand == null)
return null;
if (summand == null)
return self;
if (self == null)
return new Coordinate(-summand.X, -summand.Y);
// for now we only care for 2D
return new Coordinate(self.X - summand.X, self.Y - summand.Y);
}
///
/// Gets the axis of the longest axis
///
///
///
public static Ordinate LongestAxis(this Envelope self)
{
if (self.MaxExtent == self.Width)
return Ordinate.X;
return Ordinate.Y;
}
///
/// Gets the bottom-left coordinate of the
///
/// The envelope
/// The bottom-left coordinate
public static Coordinate BottomLeft(this Envelope self)
{
return self.Min();
}
///
/// Gets the bottom-right coordinate of the
///
/// The envelope
/// The bottom-right coordinate
public static Coordinate BottomRight(this Envelope self)
{
return new Coordinate(self.MaxX, self.MinY);
}
///
/// Gets the top-left coordinate of the
///
/// The envelope
/// The top-left coordinate
public static Coordinate TopLeft(this Envelope self)
{
return new Coordinate(self.MinX, self.MaxY);
}
///
/// Gets the top-right coordinate of the
///
/// The envelope
/// The top-right coordinate
public static Coordinate TopRight(this Envelope self)
{
return self.Max();
}
///
/// Gets the minimum y-value of the
///
/// The envelope
/// The minimum y-value
public static double Bottom(this Envelope self)
{
return self.MinY;
}
///
/// Gets the maximum y-value of the
///
/// The envelope
/// The maximum y-value
public static double Top(this Envelope self)
{
return self.MaxY;
}
///
/// Gets the minimum x-value of the
///
/// The envelope
/// The minimum x-value
public static double Left(this Envelope self)
{
return self.MinX;
}
///
/// Gets the maximum x-value of the
///
/// The envelope
/// The maximum x-value
public static double Right(this Envelope self)
{
return self.MaxX;
}
///
/// Transforms a to an array of s.
///
/// The linestring
/// The mapviewport defining transformation parameters
/// The array of s
public static PointF[] TransformToImage(this ILineString self, MapViewport map)
{
if (map.MapTransformRotation.Equals(0f))
return Transform.WorldToMap(self.Coordinates, map.Left, map.Top, map.PixelWidth, map.PixelHeight);
return Transform.WorldToMap(self.Coordinates,map.WorldToMapTransform(false));
}
///
/// Converts a to an array of s.
///
/// The coordinate
/// An array of doubles
public static double [] ToDoubleArray(this Coordinate self)
{
return new[] { self.X, self.Y /*, self.Z */};
}
///
/// Abbreviation to counter clockwise function
///
/// The ring
/// true if the ring is oriented counter clockwise
public static bool IsCCW(this ILinearRing self)
{
return NetTopologySuite.Algorithm.Orientation.IsCCW(self.Coordinates);
}
///
/// Increases the size of the boundingbox by the given amount in all directions
///
/// The envelope to grow
/// Amount to grow in all directions
public static Envelope Grow(this Envelope self, double amount)
{
return new Envelope(self.MinX - amount, self.MaxX + amount,
self.MinY - amount, self.MaxY + amount);
}
///
/// Increases the size of the boundingbox by the given amount in horizontal and vertical directions
///
/// The envelope
/// Amount to grow in horizontal direction
/// Amount to grow in vertical direction
public static Envelope Grow(this Envelope self, double amountInX, double amountInY)
{
return new Envelope(self.MinX - amountInX, self.MaxX + amountInX,
self.MinY - amountInY, self.MaxY + amountInY);
}
///
/// Transforms a to an array of s
///
/// The polygon
/// The map that defines the affine coordinate transformation.
/// Use clipping for the polygon
/// An array of PointFs
public static GraphicsPath TransformToImage(this IPolygon self, MapViewport map, bool useClipping = false)
{
var res = new GraphicsPath(FillMode.Alternate);
if (useClipping)
{
res.AddPolygon(VectorRenderer.ClipPolygon(
VectorRenderer.LimitValues(self.ExteriorRing.TransformToImage(map), VectorRenderer.ExtremeValueLimit),
map.Size.Width, map.Size.Height));
for (var i = 0; i < self.NumInteriorRings; i++)
res.AddPolygon(VectorRenderer.ClipPolygon(
VectorRenderer.LimitValues(self.GetInteriorRingN(i).TransformToImage(map),VectorRenderer.ExtremeValueLimit),
map.Size.Width, map.Size.Height));
}
else
{
res.AddPolygon(self.ExteriorRing.TransformToImage(map));
for (var i = 0; i < self.NumInteriorRings; i++)
res.AddPolygon(self.GetInteriorRingN(i).TransformToImage(map));
}
return res;
}
///
/// Tests if a coordinate is empty
///
/// The coordinate
public static bool IsEmpty(this Coordinate c)
{
if (c == null) return true;
if (!double.IsNaN(c.X)) return false;
if (!double.IsNaN(c.Y)) return false;
return true;
}
///
///
///
///
///
///
public static IGeometry GeomFromText(this IGeometry self, string wkt)
{
var factory = self == null ? new NetTopologySuite.Geometries.GeometryFactory() : self.Factory;
var reader = new NetTopologySuite.IO.WKTReader(factory);
return reader.Read(wkt);
}
private static readonly FieldInfo _envFi;
static GeoAPIEx()
{
try
{
_envFi = typeof(Geometry).GetField("_envelope", BindingFlags.Instance | BindingFlags.NonPublic);
}
catch{}
}
///
/// Utility function to set the when it is known.
///
/// The geometry
/// The envelope of .
public static void SetExtent(Geometry geom, Envelope envelope)
{
if (geom == null)
return;
if (_envFi != null)
_envFi.SetValue(geom, envelope);
}
}
}