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); } } }