// Copyright (C) Stichting Deltares 2017. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using Core.Common.Base;
using Core.Common.Base.Data;
using Core.Common.Base.Geometry;
using Ringtoets.MacroStabilityInwards.Primitives.Exceptions;
using Ringtoets.MacroStabilityInwards.Primitives.Properties;
namespace Ringtoets.MacroStabilityInwards.Primitives
{
///
/// Definition of a surfaceline for macro stability inwards.
///
public class RingtoetsMacroStabilityInwardsSurfaceLine : Observable
{
private const int numberOfDecimalPlaces = 2;
private Point2D[] localGeometry;
///
/// Initializes a new instance of the class.
///
public RingtoetsMacroStabilityInwardsSurfaceLine()
{
Name = string.Empty;
Points = new Point3D[0];
localGeometry = new Point2D[0];
}
///
/// Gets or sets the name of the surfaceline.
///
public string Name { get; set; }
///
/// Gets the 3D points describing its geometry.
///
public Point3D[] Points { get; private set; }
///
/// Gets or sets the first 3D geometry point defining the surfaceline in world coordinates.
///
public Point3D StartingWorldPoint { get; private set; }
///
/// Gets or sets the last 3D geometry point defining the surfaceline in world coordinates.
///
public Point3D EndingWorldPoint { get; private set; }
///
/// Gets or sets the reference line intersection point in world coordinates.
///
public Point2D ReferenceLineIntersectionWorldPoint { get; set; }
///
/// Gets the 2D points describing the local geometry of the surface line.
///
public IEnumerable LocalGeometry
{
get
{
return localGeometry;
}
}
///
/// Sets the geometry of the surfaceline.
///
/// The collection of points defining the surfaceline geometry.
/// Thrown when is null.
/// Thrown when any element of is null.
public void SetGeometry(IEnumerable points)
{
if (points == null)
{
throw new ArgumentNullException(nameof(points), Resources.RingtoetsMacroStabilityInwardsSurfaceLine_Collection_of_points_for_geometry_is_null);
}
if (points.Any(p => p == null))
{
throw new ArgumentException(Resources.RingtoetsMacroStabilityInwardsSurfaceLine_A_point_in_the_collection_was_null);
}
Points = points.Select(p => new Point3D(p)).ToArray();
if (Points.Length > 0)
{
StartingWorldPoint = Points[0];
EndingWorldPoint = Points[Points.Length - 1];
}
localGeometry = ProjectGeometryToLZ().ToArray();
}
///
/// Gets the height of the projected at a L=.
///
/// The L coordinate from where to take the height of the .
/// The height of the at L=.
/// Thrown when the
/// intersection point at have a significant difference in their y coordinate.
/// is not in range of the LZ-projected .
/// is empty.
public double GetZAtL(RoundedDouble l)
{
ValidateHasPoints();
if (!ValidateInRange(l))
{
var localRangeL = new Range(LocalGeometry.First().X, LocalGeometry.Last().X);
string outOfRangeMessage = string.Format(Resources.RingtoetsMacroStabilityInwardsSurfaceLine_0_L_needs_to_be_in_Range_1_,
Resources.RingtoetsMacroStabilityInwardsSurfaceLine_GetZAtL_Cannot_determine_height,
localRangeL.ToString(FormattableConstants.ShowAtLeastOneDecimal, CultureInfo.CurrentCulture));
throw new ArgumentOutOfRangeException(null, outOfRangeMessage);
}
var segments = new Collection();
for (var i = 1; i < localGeometry.Length; i++)
{
segments.Add(new Segment2D(localGeometry[i - 1], localGeometry[i]));
}
IEnumerable intersectionPoints = Math2D.SegmentsIntersectionWithVerticalLine(segments, l).OrderBy(p => p.Y).ToArray();
const double intersectionTolerance = 1e-2;
bool equalIntersections = Math.Abs(intersectionPoints.First().Y - intersectionPoints.Last().Y) < intersectionTolerance;
if (equalIntersections)
{
return intersectionPoints.First().Y;
}
string message = string.Format(Resources.RingtoetsMacroStabilityInwardsSurfaceLine_Cannot_determine_reliable_z_when_surface_line_is_vertical_in_l, l);
throw new RingtoetsMacroStabilityInwardsSurfaceLineException(message);
}
///
/// Projects the points in to localized coordinate (LZ-plane) system.
/// Z-values are retained, and the first point is put a L=0.
///
/// Collection of 2D points in the LZ-plane.
public RoundedPoint2DCollection ProjectGeometryToLZ()
{
int count = Points.Length;
if (count == 0)
{
return new RoundedPoint2DCollection(numberOfDecimalPlaces, Enumerable.Empty());
}
Point3D first = Points.First();
if (count == 1)
{
return new RoundedPoint2DCollection(numberOfDecimalPlaces, new[]
{
new Point2D(0.0, first.Z)
});
}
Point3D last = Points.Last();
var firstPoint = new Point2D(first.X, first.Y);
var lastPoint = new Point2D(last.X, last.Y);
return new RoundedPoint2DCollection(numberOfDecimalPlaces, Points.Select(p => p.ProjectIntoLocalCoordinates(firstPoint, lastPoint)));
}
///
/// Checks whether is in range of the geometry projected in local coordinate system
/// where the points are ordered on the L-coordinate being monotonically non-decreasing.
///
/// The local L-coordinate value to check for.
/// true when local L-coordinate is in range of the local geometry. false otherwise.
public bool ValidateInRange(double localCoordinateL)
{
Point2D firstLocalPoint = LocalGeometry.First();
Point2D lastLocalPoint = LocalGeometry.Last();
var roundedLocalCoordinateL = new RoundedDouble(numberOfDecimalPlaces, localCoordinateL);
return !(firstLocalPoint.X > roundedLocalCoordinateL) && !(lastLocalPoint.X < roundedLocalCoordinateL);
}
///
/// Gets the local coordinate with rounded values based on the geometry of the surface line and the given world coordinate.
///
/// The world coordinate to get the local coordinate for.
/// The local coordinate.
public Point2D GetLocalPointFromGeometry(Point3D worldCoordinate)
{
int count = Points.Length;
if (count <= 1)
{
return new Point2D(double.NaN, double.NaN);
}
Point3D first = Points.First();
Point3D last = Points.Last();
var firstPoint = new Point2D(first.X, first.Y);
var lastPoint = new Point2D(last.X, last.Y);
Point2D localCoordinate = worldCoordinate.ProjectIntoLocalCoordinates(firstPoint, lastPoint);
return new Point2D(new RoundedDouble(numberOfDecimalPlaces, localCoordinate.X),
new RoundedDouble(numberOfDecimalPlaces, localCoordinate.Y));
}
///
/// Copies the property values of the to
/// the .
///
/// The
/// to get the property values from.
/// Thrown when
/// is null.
public void CopyProperties(RingtoetsMacroStabilityInwardsSurfaceLine fromSurfaceLine)
{
if (fromSurfaceLine == null)
{
throw new ArgumentNullException(nameof(fromSurfaceLine));
}
Name = fromSurfaceLine.Name;
ReferenceLineIntersectionWorldPoint = fromSurfaceLine.ReferenceLineIntersectionWorldPoint != null
? new Point2D(fromSurfaceLine.ReferenceLineIntersectionWorldPoint)
: null;
SetGeometry(fromSurfaceLine.Points);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((RingtoetsMacroStabilityInwardsSurfaceLine) obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = Name.GetHashCode();
foreach (Point3D point in Points)
{
hashCode = (hashCode * 397) ^ point.GetHashCode();
}
return hashCode;
}
}
public override string ToString()
{
return Name;
}
private bool Equals(RingtoetsMacroStabilityInwardsSurfaceLine other)
{
return string.Equals(Name, other.Name)
&& Equals(ReferenceLineIntersectionWorldPoint, other.ReferenceLineIntersectionWorldPoint)
&& EqualGeometricPoints(other.Points);
}
private bool EqualGeometricPoints(Point3D[] otherPoints)
{
int nrOfOtherPoints = otherPoints.Length;
if (Points.Length != nrOfOtherPoints)
{
return false;
}
for (var index = 0; index < Points.Length; index++)
{
if (!Points[index].Equals(otherPoints[index]))
{
return false;
}
}
return true;
}
///
/// Checks whether the current collection is not empty.
///
/// is empty.
private void ValidateHasPoints()
{
if (!Points.Any())
{
throw new InvalidOperationException(Resources.RingtoetsMacroStabilityInwardsSurfaceLine_SurfaceLine_has_no_Geometry);
}
}
}
}