// Copyright (C) Stichting Deltares 2016. 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.Linq; using Core.Common.Base.Data; using Core.Common.Base.Geometry; using Core.Common.Base.Storage; using Ringtoets.Piping.Primitives.Exceptions; using Ringtoets.Piping.Primitives.Properties; namespace Ringtoets.Piping.Primitives { /// /// Definition of a surfaceline for piping. /// public class RingtoetsPipingSurfaceLine : IStorable { private Point3D[] geometryPoints; /// /// Initializes a new instance of the class. /// public RingtoetsPipingSurfaceLine() { Name = string.Empty; geometryPoints = new Point3D[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 { return geometryPoints; } } /// /// 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 the point which characterizes the ditch at polder side. /// public Point3D DitchPolderSide { get; private set; } /// /// Gets the point which characterizes the bottom of the ditch at polder side. /// public Point3D BottomDitchPolderSide { get; private set; } /// /// Gets the point which characterizes the bottom of the ditch at dike side. /// public Point3D BottomDitchDikeSide { get; private set; } /// /// Gets the point which characterizes the ditch at dike side. /// public Point3D DitchDikeSide { get; private set; } /// /// Gets the point which characterizes the dike toe at river side. /// public Point3D DikeToeAtRiver { get; private set; } /// /// Gets the point which characterizes the dike toe at polder side. /// public Point3D DikeToeAtPolder { get; private set; } /// /// Gets or sets the reference line intersection point in world coordinates. /// public Point2D ReferenceLineIntersectionWorldPoint { get; set; } public long StorageId { get; set; } /// /// 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("points", Resources.RingtoetsPipingSurfaceLine_Collection_of_points_for_geometry_is_null); } if (points.Any(p => p == null)) { throw new ArgumentException(Resources.RingtoetsPipingSurfaceLine_A_point_in_the_collection_was_null); } geometryPoints = points.ToArray(); if (geometryPoints.Length > 0) { StartingWorldPoint = geometryPoints[0]; EndingWorldPoint = geometryPoints[geometryPoints.Length - 1]; } } /// /// Sets the at the given point. /// /// The location as a which to set as the . /// Thrown when doesn't contain a at /// . /// is null. public void SetDitchPolderSideAt(Point3D point) { var geometryPoint = GetPointFromGeometry(point); if (geometryPoint == null) { throw CreatePointNotInGeometryException(point, Resources.CharacteristicPoint_DitchPolderSide); } DitchPolderSide = geometryPoint; } /// /// Sets the at the given point. /// /// The location as a which to set as the . /// Thrown when doesn't contain a at /// . /// is null. public void SetBottomDitchPolderSideAt(Point3D point) { var geometryPoint = GetPointFromGeometry(point); if (geometryPoint == null) { throw CreatePointNotInGeometryException(point, Resources.CharacteristicPoint_BottomDitchPolderSide); } BottomDitchPolderSide = geometryPoint; } /// /// Sets the at the given point. /// /// The location as a which to set as the . /// Thrown when doesn't contain a at /// . /// is null. public void SetBottomDitchDikeSideAt(Point3D point) { var geometryPoint = GetPointFromGeometry(point); if (geometryPoint == null) { throw CreatePointNotInGeometryException(point, Resources.CharacteristicPoint_BottomDitchDikeSide); } BottomDitchDikeSide = geometryPoint; } /// /// Sets the at the given point. /// /// The location as a which to set as the . /// Thrown when doesn't contain a at /// . /// is null. public void SetDitchDikeSideAt(Point3D point) { var geometryPoint = GetPointFromGeometry(point); if (geometryPoint == null) { throw CreatePointNotInGeometryException(point, Resources.CharacteristicPoint_DitchDikeSide); } DitchDikeSide = geometryPoint; } /// /// Sets the at the given point. /// /// The location as a which to set as the . /// Thrown when doesn't contain a at /// . /// is null. public void SetDikeToeAtRiverAt(Point3D point) { var geometryPoint = GetPointFromGeometry(point); if (geometryPoint == null) { throw CreatePointNotInGeometryException(point, Resources.CharacteristicPoint_DikeToeAtRiver); } DikeToeAtRiver = geometryPoint; } /// /// Sets the at the given point. /// /// The location as a which to set as the . /// Thrown when doesn't contain a at /// . /// is null. public void SetDikeToeAtPolderAt(Point3D point) { var geometryPoint = GetPointFromGeometry(point); if (geometryPoint == null) { throw CreatePointNotInGeometryException(point, Resources.CharacteristicPoint_DikeToeAtPolder); } DikeToeAtPolder = geometryPoint; } /// /// 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(double l) { ValidateHasPoints(); Point2D[] pointsInLocalCoordinates = ProjectGeometryToLZ().ToArray(); ValidateInRange(l, pointsInLocalCoordinates); var segments = new Collection(); for (int i = 1; i < pointsInLocalCoordinates.Length; i++) { segments.Add(new Segment2D(pointsInLocalCoordinates[i - 1], pointsInLocalCoordinates[i])); } IEnumerable intersectionPoints = Math2D.SegmentsIntersectionWithVerticalLine(segments, l).OrderBy(p => p.Y).ToArray(); const double intersectionTolerance = 1e-6; bool equalIntersections = Math.Abs(intersectionPoints.First().Y - intersectionPoints.Last().Y) < intersectionTolerance; if (equalIntersections) { return intersectionPoints.First().Y; } var message = string.Format(Resources.RingtoetsPipingSurfaceLine_Cannot_determine_reliable_z_when_surface_line_is_vertical_in_l, l); throw new RingtoetsPipingSurfaceLineException(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() { var numberOfDecimalPlaces = 2; var count = geometryPoints.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(); Point2D firstPoint = new Point2D(first.X, first.Y); Point2D lastPoint = new Point2D(last.X, last.Y); return new RoundedPoint2DCollection(numberOfDecimalPlaces, Points.Select(p => p.ProjectIntoLocalCoordinates(firstPoint, lastPoint))); } public override string ToString() { return Name; } /// /// Finds a point from which is at the same position as . /// /// The location of a point from . /// The from at the same location as . /// is null. private Point3D GetPointFromGeometry(Point3D point) { if (point == null) { throw new ArgumentNullException("point", "Cannot find a point in geometry using a null point."); } return Points.FirstOrDefault(p => p.Equals(point)); } private static ArgumentException CreatePointNotInGeometryException(Point3D point, string characteristicPointDescription) { var message = string.Format(Resources.RingtoetsPipingSurfaceLine_SetCharacteristicPointAt_Geometry_does_not_contain_point_at_0_to_assign_as_characteristic_point_1_, point, characteristicPointDescription); return new ArgumentException(message); } /// /// Checks whether the current collection is not empty. /// /// is empty. private void ValidateHasPoints() { if (!Points.Any()) { throw new InvalidOperationException(Resources.RingtoetsPipingSurfaceLine_SurfaceLine_has_no_Geometry); } } /// /// Checks whether is in range of the . /// /// The value to check for. /// Geometry projected in local coordinate system where the points are ordered on the /// L-coordinate being monotonically non-decreasing /// falls outside the L-coordiante span /// defined by . public void ValidateInRange(double localCoordinateL, Point2D[] geometryInLocalCoordinates) { Point2D firstLocalPoint = geometryInLocalCoordinates.First(); Point2D lastLocalPoint = geometryInLocalCoordinates.Last(); if (firstLocalPoint.X > localCoordinateL || lastLocalPoint.X < localCoordinateL) { var outOfRangeMessage = string.Format(Resources.RingtoetsPipingSurfaceLine_0_L_needs_to_be_in_1_2_range, Resources.RingtoetsPipingSurfaceLine_GetZAtL_Cannot_determine_height, firstLocalPoint.X, lastLocalPoint.X); throw new ArgumentOutOfRangeException(null, outOfRangeMessage); } } } }