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