using System; using System.ComponentModel; using System.Xml.Serialization; using Deltares.Geographic; using Deltares.Standard; using Deltares.Standard.Attributes; using Deltares.Standard.EventPublisher; using Deltares.Standard.Units; namespace Deltares.DeltaModel { /// /// This class describes a point constrained to be on a dike line. /// public sealed class DikeLinePoint : GeographicPoint, IComparable { private DikeLine dikeLine; private string name = "Dike line point"; private double offset; /// /// Initializes a DikeLinePoint with no offset or dike line associated to it. /// [Obsolete("Only to be used in serialization", true)] public DikeLinePoint() { } /// /// Initializes a new instance of the class using given x and y and . /// /// The point. /// The dike line. public DikeLinePoint(IGeographicPoint point, DikeLine dikeLine) : this(NotNull(point).X, NotNull(point).Y, dikeLine) {} /// /// Initializes a new instance of the class using given point and . /// /// The point. public DikeLinePoint(double x, double y, DikeLine dikeLine) { DikeLine = dikeLine; SetCoordinate(x, y); } /// /// Initializes a new instance of the class using given offset and . /// /// The offset. /// The dike line. public DikeLinePoint(double offset, DikeLine dikeLine) { DikeLine = dikeLine; Offset = offset; } /// /// Initializes a new instance of the from another . /// private DikeLinePoint(DikeLinePoint point) : this(point.offset, point.DikeLine) {} /// /// Gets or sets the name of this /// [Label("Name")] [PropertyOrder(0, 1)] public string Name { get { return name; } set { this.SetAndNotify2(out name, value, dlp => dlp.Name); } } /// /// Gets the that this point lays on. /// [PropertyOrder(1, 4)] [XmlIgnore] // TODO only used in dikeCrossSection and DikeSection .. This should never change I guess, only set at construction time (check this) public DikeLine DikeLine { get { return dikeLine; } set { dikeLine = NotNullOrEmpty(value); } } /// /// Gets or sets the offset of this on the . /// [PropertyOrder(1, 5)] [Format("F3")] [Unit(UnitType.Length)] [XmlIgnore] public double Offset { get { return offset; } set { if (value > DikeLine.Length && value <= DikeLine.Length + 1e-6) { value = DikeLine.Length; } else if (value < 0.0 || value > DikeLine.Length) { throw new ArgumentException(String.Format("Offset should be in range [0, {0}].", DikeLine.Length)); } var point = DikeLine.GetPointAtOffset(value); base.X = point.X; base.Y = point.Y; this.SetAndNotify2(out offset, value, dlp => dlp.Offset); } } /// /// Gets or sets the X coordinate of this . /// /// Thrown when the resulting , coordinate could not be found on the . /// Only use the getter. Setting this property will most likely result in a , so use for setting the coordinate instead. [PropertyOrder(1, 2)] [Format("F2")] [Unit(UnitType.Length)] [ReadOnly(true)] [XmlIgnore] public override double X { get { return base.X; } set { SetCoordinate(value, Y); } } /// /// Gets or sets the Y coordinate of this . /// /// Thrown when the resulting , coordinate could not be found on the . /// Only use the getter. Setting this property will most likely result in a , so use for setting the coordinate instead. [PropertyOrder(1, 3)] [Format("F2")] [Unit(UnitType.Length)] [ReadOnly(true)] [XmlIgnore] public override double Y { get { return base.Y; } set { SetCoordinate(X, value); } } /// /// Sets the offset of this point by looking at the offset of the coordinate of the closest /// to the given (,) coordinate. /// /// The new x coordinate of this . /// The new y coordinate of this . /// Thrown when no point with given , coordinate could not be found on the . public override void SetCoordinate(double x, double y) { var newOffset = DikeLine.GetOffset(new GeographicPoint(x, y)); if (newOffset < 0) { throw new ArgumentException(String.Format("Point ({0},{1}) is not on the dike line; could not determine offset.", x, y)); } Offset = newOffset; } /// /// Creates a partial clone of this , while keeping the reference to the intact. /// /// A partial clone. public override IGeographicPoint Clone() { return new DikeLinePoint(this); } public override string ToString() { return String.Format("{0} {1}", Name, Offset); } public override bool Equals(object obj) { var dikeLinePoint = obj as DikeLinePoint; return dikeLinePoint != null && dikeLinePoint.DikeLine == DikeLine && CompareTo(dikeLinePoint) == 0; } public int CompareTo(DikeLinePoint other) { var comparedDikeLines = DikeLine.CompareTo(other.DikeLine); return comparedDikeLines == 0 ? Offset.CompareTo(other.Offset) : comparedDikeLines; } public override int GetHashCode() { return (int) Offset / 10; } [Obsolete("Only used for serialization", true)] private SerializedDikeLinePoint State { get { return new SerializedDikeLinePoint(this); } set { DikeLine = value.DikeLine; Offset = value.Offset; Name = value.Name; } } private class SerializedDikeLinePoint { public SerializedDikeLinePoint(DikeLinePoint point) { DikeLine = point.dikeLine; Offset = point.offset; Name = point.name; } private SerializedDikeLinePoint() {} public DikeLine DikeLine { get; private set; } public double Offset { get; private set; } public String Name { get; private set; } } #region parameter validation private static IGeographicPoint NotNull(IGeographicPoint point) { if (point == null) { throw new ArgumentNullException("point", @"Cannot initialize DikeLinePoint from a null point."); } return point; } private static DikeLine NotNullOrEmpty(DikeLine dikeLine) { if (dikeLine == null) { throw new ArgumentNullException("dikeLine", @"Cannot set a null dike line for a DikeLinePoint."); } if (dikeLine.Points.Count == 0) { throw new ArgumentException(@"Cannot set an empty dike line for a DikeLinePoint.", "dikeLine"); } return dikeLine; } #endregion } }