using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml.Serialization;
using Deltares.Geographic;
using Deltares.Geotechnics;
using Deltares.Geotechnics.GeotechnicalGeometry;
using Deltares.Standard;
using Deltares.Standard.Attributes;
using Deltares.Standard.Data;
using Deltares.Standard.EventPublisher;
using Deltares.Standard.Extensions;
using Deltares.Standard.Units;
using Deltares.Standard.Validation;
namespace Deltares.DeltaModel
{
///
/// Block revetment Cross Section Object
///
public class BlockRevetmentCrossSection : IGeographicString, IComparable, IHasSelectedSubObject, IDisposable
{
private readonly List points = new List();
private bool applyAboveToetsPeilCriteria;
private bool applyLowerPeriodCriteria;
private double averageHighWaterLevel = double.NaN;
private double averageLowWaterLevel = double.NaN;
private DikeLinePoint beginPoint;
private double betaDijk;
private YesNoQuestionMark breedteKering;
private DikeLinePoint endPoint;
private string name = "";
private object selectedSubObject;
private LocalizedGeometryPointString surfaceLine;
private double tanaBottom;
private double zbodem;
///
/// Initializes a new instance of the class.
/// When a is supplied, the of this provider is used to project
/// the segments upon.
///
public BlockRevetmentCrossSection(IHasSurfaceLine surfaceLineProvider)
{
Segments = new DelegatedList
{
AddMethod = AddSegment,
DeleteMethod = RemoveSegment
};
surfaceLine = null != surfaceLineProvider ? surfaceLineProvider.SurfaceLine : new LocalizedGeometryPointString();
}
///
/// Initializes a new instance of the class based
/// on an already existing instance. When a is supplied, the of this provider is used to project
/// the segments upon.
///
public BlockRevetmentCrossSection(BlockRevetmentCrossSection blockRevetmentCrossSection, IHasSurfaceLine surfaceLineProvider)
: this(surfaceLineProvider)
{
blockRevetmentCrossSection.CopyTo(this);
}
[Obsolete("Only use this for deserialization")]
public BlockRevetmentCrossSection() : this(null) {}
///
/// List of all the segments that make up this cross section.
///
public DelegatedList Segments { get; private set; }
///
/// Gets or sets the name.
///
///
/// The name.
///
[Data] // Used for visualization in map
[PropertyOrder(0, 1)]
public string Name
{
get
{
return name;
}
set
{
this.SetAndNotify2(out name, value, x => x.Name);
}
}
///
/// Gets or sets the dike line.
///
///
/// The dike line.
///
[ReadOnly(true)]
[PropertyOrder(1, 1)]
public DikeLine DikeLine { get; set; }
///
/// Gets or sets the begin point.
///
///
/// The begin point.
///
[Browsable(false)]
public DikeLinePoint BeginPoint
{
get
{
return beginPoint;
}
set
{
if (value != beginPoint)
{
this.SetAndNotify2(out beginPoint, value, x => x.BeginPoint);
}
}
}
///
/// Gets or sets the end point.
///
///
/// The end point.
///
[Browsable(false)]
public DikeLinePoint EndPoint
{
get
{
return endPoint;
}
set
{
if (value != endPoint)
{
this.SetAndNotify2(out endPoint, value, x => x.EndPoint);
}
}
}
///
/// Gets the begin offset.
///
///
/// The begin offset.
///
[XmlIgnore]
[Unit(UnitType.Length)]
[PropertyOrder(1, 2)]
[Format("F2")]
public double BeginOffset
{
get
{
return BeginPoint != null ? BeginPoint.Offset : double.NaN;
}
}
///
/// Gets the end offset.
///
///
/// The end offset.
///
[XmlIgnore]
[Unit(UnitType.Length)]
[PropertyOrder(1, 3)]
[Format("F2")]
public double EndOffset
{
get
{
return EndPoint != null ? EndPoint.Offset : double.NaN;
}
}
///
/// Value indicating whether to apply lower period criteria.
///
[Category("Properties")]
[PropertyOrder(2, 0)]
public bool ApplyLowerPeriodCriteria
{
get
{
return applyLowerPeriodCriteria;
}
set
{
this.SetAndNotify2(out applyLowerPeriodCriteria, value, x => x.ApplyLowerPeriodCriteria);
}
}
///
/// Value indicating whether to apply above toetspeil criteria.
///
[Category("Properties")]
[PropertyOrder(2, 1)]
public bool ApplyAboveToetsPeilCriteria
{
get
{
return applyAboveToetsPeilCriteria;
}
set
{
this.SetAndNotify2(out applyAboveToetsPeilCriteria, value, x => x.ApplyAboveToetsPeilCriteria);
}
}
///
/// Direction of the normal on the dike (Richting normaal op dijk (gr tov N))
///
[Unit(UnitType.Angle, GeometryAngleUnit.deg)] // gr tov N
[Format("F3")]
[Clearable]
[Category("Properties")]
[PropertyOrder(2, 2)]
public double BetaDijk
{
get
{
return betaDijk;
}
set
{
this.SetAndNotify2(out betaDijk, value, x => x.BetaDijk);
}
}
///
/// Average slope of the foreland (vert. : hor.)
/// for a few tens of meters to the toe of the dike or jetty
/// TODO: Comment
///
[Unit(UnitType.None, GeometryAngleUnit.tan)]
[Format("F3")]
[Clearable]
[Category("Properties")]
[PropertyOrder(2, 3)]
[ReadOnly(true)]
public double TanaBottom
{
get
{
return tanaBottom;
}
set
{
this.SetAndNotify2(out tanaBottom, value, x => x.TanaBottom);
}
}
///
/// If the dike on 2.5m above the average outer water level is wider than 150 m
/// (including the area lying behind it), then there is a risico of slide
/// in case there is a layer of clay present.
/// Not filling in any value means the same as 'n'.
/// TODO: Comment
//
// Dutch original text:
// "Als de dijk op 2,5m boven de gemiddelde
// buitenwaterstand breder is dan 150 m (incl. erachter gelegen terrein),
// is er een risico op afschuiving indien er een kleilaag aanwezig is.
// Niets invullen staat gelijk aan 'n'. "
///
[Category("Properties")]
[PropertyOrder(2, 4)]
public YesNoQuestionMark BreedteKering
{
get
{
return breedteKering;
}
set
{
this.SetAndNotify2(out breedteKering, value, x => x.BreedteKering);
}
}
///
/// Height of the foreland at the toe of the dike or outside
/// of the mole, relative to NAP.
/// TODO: Comment
///
[Format("F3")]
[Clearable]
[Category("Properties")]
[Unit(UnitType.Depth)]
[PropertyOrder(2, 5)]
[ReadOnly(true)]
public double Zbodem
{
get
{
return zbodem;
}
set
{
this.SetAndNotify2(out zbodem, value, x => x.Zbodem);
}
}
///
/// Gets or sets the average low water level.
///
[Unit(UnitType.Depth)]
[NotClear]
[Clearable]
[Format("F2")]
public double AverageLowWaterLevel
{
get
{
return averageLowWaterLevel;
}
set
{
this.SetAndNotify2(out averageLowWaterLevel, value, x => x.AverageLowWaterLevel);
}
}
///
/// Gets or sets the average high water level.
///
[Unit(UnitType.Depth)]
[NotClear]
[Clearable]
[Format("F2")]
public double AverageHighWaterLevel
{
get
{
return averageHighWaterLevel;
}
set
{
this.SetAndNotify2(out averageHighWaterLevel, value, x => x.AverageHighWaterLevel);
}
}
///
/// The surface line along all segments. This collection is kept synchronized with the segment definition
///
[Browsable(false)]
public LocalizedGeometryPointString SurfaceLine
{
get
{
return surfaceLine;
}
private set
{
this.SetAndNotify2(out surfaceLine, value, s => s.SurfaceLine);
}
}
#region IComparable Members
///
/// Compares the current object with another object of the same type.
///
/// An object to compare with this object.
///
/// Result of comparing the BeginOffset values
///
public int CompareTo(BlockRevetmentCrossSection other)
{
return BeginOffset.CompareTo(other.BeginOffset);
}
#endregion
///
/// Creates a list of points starting with BeginPoint and ending with EndPoints and returns that.
///
///
/// The points.
///
[Browsable(false)]
[XmlIgnore]
public IList Points
{
get
{
if (points.Count == 0 && BeginPoint != null && EndPoint != null && DikeLine != null)
{
points.Add(BeginPoint);
for (var i = 0; i < DikeLine.Points.Count; i++)
{
IGeographicPoint point = DikeLine.Points[i];
double offset = GeographicHelper.Instance.GetOffsetOnGeographicLineString(point, DikeLine);
if (offset >= EndPoint.Offset)
{
break;
}
if (offset > BeginPoint.Offset)
{
points.Add(point);
}
}
points.Add(EndPoint);
}
return points;
}
}
///
/// Selected segment is just a property for the GUI
/// TODO: Comment
///
[Browsable(false)]
[XmlIgnore]
public object SelectedSubObject
{
get
{
return selectedSubObject;
}
set
{
this.SetAndNotify2(out selectedSubObject, value, x => x.SelectedSubObject);
}
}
///
/// Shifts the surface line points according to the provided .
///
/// The shift to apply.
public void ShiftSurfaceLinePoints(double shift)
{
var orderedSurfaceLinePoints = shift > 0
? surfaceLine.Points.OrderByDescending(slp => slp.X) // Order by descending when shifting to the right, otherwise the shifting action might be "cut off"
: surfaceLine.Points.OrderBy(slp => slp.X); // Order by ascending when shifting to the left, otherwise the shifting action might be "cut off"
foreach (var point in orderedSurfaceLinePoints)
{
point.X += shift;
}
}
public override string ToString()
{
return Name;
}
public void Dispose()
{
Segments.AddMethod = null;
Segments.DeleteMethod = null;
}
private void CopyTo(BlockRevetmentCrossSection target)
{
target.name = Name;
target.DikeLine = DikeLine;
target.applyLowerPeriodCriteria = applyLowerPeriodCriteria;
target.applyAboveToetsPeilCriteria = applyAboveToetsPeilCriteria;
target.betaDijk = betaDijk;
target.tanaBottom = tanaBottom;
target.breedteKering = breedteKering;
target.zbodem = zbodem;
target.averageLowWaterLevel = averageLowWaterLevel;
target.averageHighWaterLevel = averageHighWaterLevel;
if (null != BeginPoint)
{
target.BeginPoint = BeginPoint.Clone() as DikeLinePoint;
}
if (null != EndPoint)
{
target.EndPoint = EndPoint.Clone() as DikeLinePoint;
}
}
private void AddSegment(BlockRevetmentSegment newSegment)
{
newSegment.Owner = this;
// Take action when a segment is added without start and end point
if (Double.IsNaN(newSegment.StartX) && Double.IsNaN(newSegment.EndX))
{
var index = Segments.IndexOf(newSegment);
var previousSegment = GetSegmentAt(index - 1);
var nextSegment = GetSegmentAt(index + 1);
var isPreviousSet = previousSegment != null;
var isNextSet = nextSegment != null;
if (isPreviousSet && isNextSet) // Handle segments that were added somewhere in the middle
{
previousSegment.DisconnectFrom(nextSegment);
previousSegment.EndX = (previousSegment.StartX + previousSegment.EndX)/2.0;
previousSegment.EndZ = (previousSegment.StartZ + previousSegment.EndZ)/2.0;
}
if (isPreviousSet)
{
previousSegment.CopyTo(newSegment);
newSegment.StartX = previousSegment.EndX;
newSegment.StartZ = previousSegment.EndZ;
previousSegment.ConnectTo(newSegment);
}
else
{
newSegment.StartX = 0;
newSegment.StartZ = 0;
}
if (isNextSet)
{
if (null == newSegment.BlockRevetmentProfile)
{
nextSegment.CopyTo(newSegment);
}
newSegment.EndX = nextSegment.StartX;
newSegment.EndZ = nextSegment.StartZ;
newSegment.ConnectTo(nextSegment);
}
else
{
newSegment.EndX = newSegment.StartX + 5;
newSegment.EndZ = 0;
}
}
else
{
var existingStartPoint = surfaceLine.GetPointAt(newSegment.StartX, newSegment.StartZ);
var existingEndPoint = surfaceLine.GetPointAt(newSegment.EndX, newSegment.EndZ);
var previousSegment = null == existingStartPoint ? null : Segments.FirstOrDefault(s => s.EndX.AlmostEquals(existingStartPoint.X));
var nextSegment = null == existingEndPoint ? null : Segments.FirstOrDefault(s => s.StartX.AlmostEquals(existingEndPoint.X));
if (null != previousSegment)
{
previousSegment.ConnectTo(newSegment);
}
if (null != nextSegment)
{
newSegment.ConnectTo(nextSegment);
}
}
UpdateSurfaceLine();
}
private BlockRevetmentSegment GetSegmentAt(int index)
{
if (index >= 0 && index < Segments.Count)
{
return Segments[index];
}
return null;
}
///
/// This method offers functionality that is only used in the interface. If possible, try to remove it, because there
/// is no such thing a a surfaceline for block revetment cross sections, just a set of segments which bind points which
/// can be used to draw a surfaceline.
///
private void UpdateSurfaceLine()
{
SurfaceLine.Points.Clear();
foreach (var segment in Segments)
{
segment.AddTo(SurfaceLine);
}
DataEventPublisher.DataListModified(SurfaceLine.Points);
}
private void RemoveSegment(BlockRevetmentSegment removedSegment)
{
var previousSegment = Segments.FirstOrDefault(s => s.IsConnectedTo(removedSegment));
var nextSegment = Segments.FirstOrDefault(s => removedSegment.IsConnectedTo(s));
if (previousSegment != null && nextSegment != null)
{
previousSegment.ConnectTo(nextSegment);
}
SelectedSubObject = previousSegment ?? nextSegment;
UpdateSurfaceLine();
}
}
}