using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Deltares.Geographic;
using Deltares.Geometry;
using Deltares.Geotechnics;
using Deltares.Geotechnics.GeotechnicalGeometry;
using Deltares.Geotechnics.SurfaceLines;
using Deltares.Mathematics;
using Deltares.Standard;
using Deltares.Standard.Attributes;
using Deltares.Standard.Data;
using Deltares.Standard.EventPublisher;
using Deltares.Standard.Validation;
namespace Deltares.DeltaModel
{
///
/// Holder for revetment schematizations.
///
/// The type of surface line revetment segments.
public abstract class RevetmentCrossSection : IDisposable where T : SurfaceLineRevetmentSegment
{
private CreatingDelegatedList segments;
private IHasSurfaceLine surfaceLineProvider;
protected RevetmentCrossSection()
{
segments = new CreatingDelegatedList();
Segments.AddMethod = AddSegment;
Segments.DeleteMethod = RemoveSegment;
}
protected RevetmentCrossSection(IHasSurfaceLine surfaceLineProvider)
: this()
{
SurfaceLineProvider = surfaceLineProvider;
}
///
/// List of all segments of the cross section.
///
[PropertyOrder(3, 0)]
[Category("Segments")]
[Browsable(false)]
[Validate]
[XmlOldName("RevetmentSegments")]
public CreatingDelegatedList Segments
{
get
{
return segments;
}
private set
{
segments = value;
}
}
///
/// The object which contains a which the segments use to project themselves upon.
///
[Browsable(false)]
public IHasSurfaceLine SurfaceLineProvider
{
get
{
return surfaceLineProvider;
}
set
{
surfaceLineProvider = value;
foreach (var segment in segments.ToList())
{
segment.SurfaceLineProvider = value;
}
}
}
///
/// Creates segments using zones by geographically finding intersections the the surafce line of the dike location
///
/// List of revetment zones
public void ConnectRevetmentZones(List revetmentZones) where U : RevetmentZone
{
const double tolerance = 100.0;
LocalizedGeometryPointString surfaceLine = surfaceLineProvider.SurfaceLine;
GeographicString geographicString = surfaceLine.GetGeograpicString();
segments.Clear();
foreach (var revetmentZone in revetmentZones)
{
IGeographic intersection = GeographicHelper.Instance.GetIntersection(geographicString.Points, revetmentZone.Points);
var nearest = GeographicHelper.Instance.GetNearestCoordinatesOnGeographicString(revetmentZone, surfaceLine.Origin.X, surfaceLine.Origin.Y);
var distance = Routines2D.Compute2DDistance(nearest.X, nearest.Y, surfaceLine.Origin.X, surfaceLine.Origin.Y);
// todo along surfaceline
if (distance < tolerance && intersection != null)
{
// If casting to T does not work, I want to fail hard
var segment = (T)Activator.CreateInstance(typeof(T), surfaceLineProvider);
segment.SegmentDescription = revetmentZone.Data;
var otherPoint = geographicString.Points[0];
if (intersection is IGeographicPoint)
{
var intersectionPoint = (IGeographicPoint) intersection;
double distanceFromPointsStart = Routines2D.Compute2DDistance(intersectionPoint.X, intersectionPoint.Y, otherPoint.X, otherPoint.Y);
segment.EndX = distanceFromPointsStart;
}
else if (intersection is IGeographicString)
{
var intersectionGeographicString = (GeographicString)intersection;
var firstPoint = intersectionGeographicString.Points.First();
var lastPoint = intersectionGeographicString.Points.Last();
var distanceFirstPoint = Routines2D.Compute2DDistance(firstPoint.X, firstPoint.Y, otherPoint.X, otherPoint.Y);
var localizedFirstPoint = new GeometryPoint(distanceFirstPoint, 0, surfaceLine.GetZAtX(distanceFirstPoint));
double distanceLastPoint = Routines2D.Compute2DDistance(lastPoint.X, lastPoint.Y, otherPoint.X, otherPoint.Y);
var localizedLastPoint = new GeometryPoint(distanceLastPoint, 0, surfaceLine.GetZAtX(distanceLastPoint));
segment.StartX = (distanceFirstPoint < distanceLastPoint) ? localizedFirstPoint.X : localizedLastPoint.X;
segment.EndX = (distanceLastPoint >= distanceFirstPoint) ? localizedLastPoint.X : localizedFirstPoint.X;
}
Segments.Add(segment);
}
}
}
public List GetSegments(double start, double end)
{
const double delta = 0.001;
var parts = new List();
var beginPoint = new GeometryPoint(start, 0, SurfaceLineProvider.SurfaceLine.GetZAtX(start));
while (beginPoint.X < end - delta && beginPoint.X < SurfaceLineProvider.SurfaceLine.Points.Last().X)
{
T segment = null;
foreach (T existingSegment in Segments)
{
if (existingSegment.StartX <= beginPoint.X + delta && existingSegment.EndX >= beginPoint.X - delta)
{
if (segment == null || existingSegment.EndX > segment.EndX)
{
segment = existingSegment;
}
}
}
// detect next point in the surface line
GeometryPoint endPoint = null;
for (int i = 0; i < SurfaceLineProvider.SurfaceLine.Points.Count; i++)
{
if (SurfaceLineProvider.SurfaceLine.Points[i].X > beginPoint.X + delta)
{
endPoint = SurfaceLineProvider.SurfaceLine.Points[i];
break;
}
}
if (endPoint == null)
{
break;
}
if (endPoint.X > end + delta)
{
endPoint = new GeometryPoint(end, 0, SurfaceLineProvider.SurfaceLine.GetZAtX(end));
}
// detect whether there is a segment
if (segment != null && segment.EndX < endPoint.X)
{
endPoint = new GeometryPoint(segment.EndX, 0, SurfaceLineProvider.SurfaceLine.GetZAtX(segment.EndX));
}
var segmentLine = new SegmentLine
{
StartPoint = beginPoint,
EndPoint = endPoint,
Segment = segment
};
parts.Add(segmentLine);
beginPoint = endPoint;
}
return parts;
}
///
/// Indicates whether the segments cover a section completely
///
/// Begin of the section
/// End of the section
/// Covered completely indication
public bool IsCoveredCompletely(double start, double end)
{
const double delta = 0.001;
bool startPointCovered = Segments.Any(p => p.StartX <= start + delta && p.EndX >= start - delta);
if (!startPointCovered)
{
return false;
}
double reachedX = start;
bool gotFurther = true;
while (gotFurther)
{
gotFurther = false;
foreach (var segment in Segments.Where(p => p.StartX <= reachedX + delta && p.EndX >= reachedX - delta))
{
if (segment.EndX > reachedX)
{
reachedX = segment.EndX;
gotFurther = true;
break;
}
}
}
return reachedX >= end - delta;
}
public void Dispose()
{
Segments.AddMethod = null;
Segments.DeleteMethod = null;
Segments.CreateMethod = null;
}
private void RemoveSegment(T segment)
{
var previousSegment = segments.FirstOrDefault(s => s.IsConnectedTo(segment));
var nextSegment = segments.FirstOrDefault(s => segment.IsConnectedTo(s));
if (null != nextSegment && null != previousSegment)
{
previousSegment.ConnectTo(nextSegment);
}
DataEventPublisher.DataListModified(segments);
}
private void AddSegment(T segment)
{
if (Double.IsNaN(segment.StartX))
{
var index = segments.IndexOf(segment);
var previousIndex = index - 1;
var nextIndex = index + 1;
var previousSegment = previousIndex >= 0 ? segments[previousIndex] : null;
var nextSegment = nextIndex < segments.Count ? segments[nextIndex] : null;
if (previousSegment != null && nextSegment != null)
{
previousSegment.DisconnectFrom(nextSegment);
nextSegment.StartX = (nextSegment.StartX + nextSegment.EndX) / 2;
}
segment.StartX = previousSegment == null ? 0 : previousSegment.EndX;
segment.EndX = nextSegment == null ? segment.StartX + 5 : nextSegment.StartX;
if (previousSegment != null)
{
previousSegment.ConnectTo(segment);
}
if (nextSegment != null)
{
segment.ConnectTo(nextSegment);
}
}
segment.SurfaceLineProvider = surfaceLineProvider;
DataEventPublisher.DataListModified(segments);
}
public class SegmentLine
{
public GeometryPoint EndPoint;
public T Segment;
public GeometryPoint StartPoint;
public override string ToString()
{
return string.Format("{0:F3} - {1:F3} : {2}", StartPoint.X, EndPoint.X, Segment != null ? Segment.Description : "null");
}
}
}
}