using System; using Core.Gis.GeoApi.Geometries; namespace Core.GIS.NetTopologySuite.Geometries { /// /// Specifies the precision model of the Coordinates in a Geometry. /// In other words, specifies the grid of allowable /// points for all Geometrys. /// The makePrecise method allows rounding a coordinate to /// a "precise" value; that is, one whose /// precision is known exactly. /// Coordinates are assumed to be precise in geometries. /// That is, the coordinates are assumed to be rounded to the /// precision model given for the point. /// NTS input routines automatically round coordinates to the precision model /// before creating Geometries. /// All internal operations /// assume that coordinates are rounded to the precision model. /// Constructive methods (such as bool operations) always round computed /// coordinates to the appropriate precision model. /// Currently three types of precision model are supported: /// /// Floating: represents full double precision floating point. /// This is the default precision model used in NTS /// FloatingSingle: represents single precision floating point. /// Fixed: represents a model with a fixed number of decimal places. /// /// A Fixed Precision Model is specified by a scale factor. /// The scale factor specifies the grid which numbers are rounded to. /// Input coordinates are mapped to fixed coordinates according to the following /// equations: /// jtsPt.x = round( (inputPt.x * scale ) / scale /// jtsPt.y = round( (inputPt.y * scale ) / scale /// Coordinates are represented internally as double-precision values. /// Since .NET uses the IEEE-394 floating point standard, this /// provides 53 bits of precision. (Thus the maximum precisely representable /// integer is 9,007,199,254,740,992). /// NTS methods currently do not handle inputs with different precision models. /// [Serializable] public class PrecisionModel : IPrecisionModel { /// /// The maximum precise value representable in a double. Since IEE754 /// double-precision numbers allow 53 bits of mantissa, the value is equal to /// 2^53 - 1. This provides almost 16 decimal digits of precision. /// public const double MaximumPreciseValue = 9007199254740992.0; private const int FloatingPrecisionDigits = 16; private const int FloatingSinglePrecisionDigits = 6; private const int FixedPrecisionDigits = 1; /// /// The type of PrecisionModel this represents. /// private readonly PrecisionModels modelType; /// /// The scale factor which determines the number of decimal places in fixed precision. /// private double scale; /// /// Creates a PrecisionModel with a default precision /// of Floating. /// public PrecisionModel() { // default is floating precision modelType = PrecisionModels.Floating; } /// /// Creates a PrecisionModel that specifies /// an explicit precision model type. /// If the model type is Fixed the scale factor will default to 1. /// /// /// The type of the precision model. /// public PrecisionModel(PrecisionModels modelType) { this.modelType = modelType; if (modelType == PrecisionModels.Fixed) { Scale = 1.0; } } /// /// Creates a PrecisionModel that specifies Fixed precision. /// Fixed-precision coordinates are represented as precise internal coordinates, /// which are rounded to the grid defined by the scale factor. /// /// /// Amount by which to multiply a coordinate after subtracting /// the offset, to obtain a precise coordinate /// /// Not used. /// Not used. [Obsolete("Offsets are no longer supported, since internal representation is rounded floating point")] public PrecisionModel(double scale, double offsetX, double offsetY) { modelType = PrecisionModels.Fixed; Scale = scale; } /// /// Creates a PrecisionModel that specifies Fixed precision. /// Fixed-precision coordinates are represented as precise internal coordinates, /// which are rounded to the grid defined by the scale factor. /// /// /// Amount by which to multiply a coordinate after subtracting /// the offset, to obtain a precise coordinate. /// public PrecisionModel(double scale) { modelType = PrecisionModels.Fixed; Scale = scale; } /// /// Copy constructor to create a new PrecisionModel /// from an existing one. /// /// public PrecisionModel(PrecisionModel pm) { modelType = pm.modelType; scale = pm.scale; } /// /// Returns the x-offset used to obtain a precise coordinate. /// /// /// The amount by which to subtract the x-coordinate before /// multiplying by the scale. /// [Obsolete("Offsets are no longer used")] public double OffsetX { get { return 0; } } /// /// Returns the y-offset used to obtain a precise coordinate. /// /// /// The amount by which to subtract the y-coordinate before /// multiplying by the scale /// [Obsolete("Offsets are no longer used")] public double OffsetY { get { return 0; } } /// /// Tests whether the precision model supports floating point. /// /// true if the precision model supports floating point. public bool IsFloating { get { return modelType == PrecisionModels.Floating || modelType == PrecisionModels.FloatingSingle; } } /// /// Returns the maximum number of significant digits provided by this /// precision model. /// Intended for use by routines which need to print out precise values. /// /// /// The maximum number of decimal places provided by this precision model. /// public int MaximumSignificantDigits { get { switch (modelType) { case PrecisionModels.Floating: return FloatingPrecisionDigits; case PrecisionModels.FloatingSingle: return FloatingSinglePrecisionDigits; case PrecisionModels.Fixed: return FixedPrecisionDigits + (int) Math.Ceiling(Math.Log(Scale)/Math.Log(10)); default: throw new ArgumentOutOfRangeException(modelType.ToString()); } } } /// /// Returns the multiplying factor used to obtain a precise coordinate. /// This method is private because PrecisionModel is intended to /// be an immutable (value) type. /// /// /// the amount by which to multiply a coordinate after subtracting /// the offset. /// public double Scale { get { return scale; } set { scale = Math.Abs(value); } } /// /// Gets the type of this PrecisionModel. /// /// public PrecisionModels PrecisionModelType { get { return modelType; } } /// /// Gets the type of this PrecisionModel. /// /// public PrecisionModels GetPrecisionModelType() { return modelType; } /// /// Sets internal to the precise representation of external. /// /// The original coordinate. /// /// The coordinate whose values will be changed to the /// precise representation of external. /// [Obsolete("Use MakePrecise instead")] public void ToInternal(ICoordinate cexternal, ICoordinate cinternal) { if (IsFloating) { cinternal.X = cexternal.X; cinternal.Y = cexternal.Y; } else { cinternal.X = MakePrecise(cexternal.X); cinternal.Y = MakePrecise(cexternal.Y); } cinternal.Z = cexternal.Z; } /// /// Returns the precise representation of external. /// /// The original coordinate. /// /// The coordinate whose values will be changed to the precise /// representation of external /// [Obsolete("Use MakePrecise instead")] public ICoordinate ToInternal(ICoordinate cexternal) { ICoordinate cinternal = new Coordinate(cexternal); MakePrecise(cinternal); return cinternal; } /// /// Returns the external representation of internal. /// /// The original coordinate. /// /// The coordinate whose values will be changed to the /// external representation of internal. /// [Obsolete("No longer needed, since internal representation is same as external representation")] public ICoordinate ToExternal(ICoordinate cinternal) { ICoordinate cexternal = new Coordinate(cinternal); return cexternal; } /// /// Sets external to the external representation of internal. /// /// The original coordinate. /// /// The coordinate whose values will be changed to the /// external representation of internal. /// [Obsolete("No longer needed, since internal representation is same as external representation")] public void ToExternal(ICoordinate cinternal, ICoordinate cexternal) { cexternal.X = cinternal.X; cexternal.Y = cinternal.Y; } /// /// /// /// /// /// public static bool operator ==(PrecisionModel obj1, PrecisionModel obj2) { return Equals(obj1, obj2); } /// /// /// /// /// /// public static bool operator !=(PrecisionModel obj1, PrecisionModel obj2) { return !(obj1 == obj2); } /// /// Return HashCode. /// public override int GetHashCode() { return base.GetHashCode(); } /// /// /// /// public override string ToString() { string description = "UNKNOWN"; if (modelType == PrecisionModels.Floating) { description = "Floating"; } else if (modelType == PrecisionModels.FloatingSingle) { description = "Floating-Single"; } else if (modelType == PrecisionModels.Fixed) { description = "Fixed (Scale=" + Scale + ")"; } return description; } /// /// /// /// /// public override bool Equals(object other) { if (other == null) { return false; } if (!(other is IPrecisionModel)) { return false; } return Equals((IPrecisionModel) other); } /// /// Rounds a numeric value to the PrecisionModel grid. /// Symmetric Arithmetic Rounding is used, to provide /// uniform rounding behaviour no matter where the number is /// on the number line. /// /// public double MakePrecise(double val) { if (modelType == PrecisionModels.FloatingSingle) { float floatSingleVal = (float) val; return (double) floatSingleVal; } if (modelType == PrecisionModels.Fixed) { // return Math.Round(val * scale) / scale; // Diego Guidi say's: i use the Java Round algorithm (used in JTS 1.6) // Java Rint method, used in JTS 1.5, was consistend with .NET Round algorithm return Math.Floor(((val*scale) + 0.5d))/scale; } return val; // modelType == FLOATING - no rounding necessary } /// /// Rounds a Coordinate to the PrecisionModel grid. /// /// public void MakePrecise(ICoordinate coord) { // optimization for full precision if (modelType == PrecisionModels.Floating) { return; } coord.X = MakePrecise(coord.X); coord.Y = MakePrecise(coord.Y); //MD says it's OK that we're not makePrecise'ing the z [Jon Aquino] } /// /// /// /// /// public bool Equals(IPrecisionModel otherPrecisionModel) { return modelType == otherPrecisionModel.PrecisionModelType && scale == otherPrecisionModel.Scale; } /// /// Compares this PrecisionModel object with the specified object for order. /// A PrecisionModel is greater than another if it provides greater precision. /// The comparison is based on the value returned by the /// {getMaximumSignificantDigits) method. /// This comparison is not strictly accurate when comparing floating precision models /// to fixed models; however, it is correct when both models are either floating or fixed. /// /// /// The PrecisionModel with which this PrecisionModel /// is being compared. /// /// /// A negative integer, zero, or a positive integer as this PrecisionModel /// is less than, equal to, or greater than the specified PrecisionModel. /// public int CompareTo(object o) { return CompareTo((IPrecisionModel) o); } /// /// /// /// /// public int CompareTo(IPrecisionModel other) { int sigDigits = MaximumSignificantDigits; int otherSigDigits = other.MaximumSignificantDigits; return (sigDigits).CompareTo(otherSigDigits); } } }