// Copyright (C) Stichting Deltares 2018. All rights reserved. // // This file is part of the Dam Engine. // // The Dam Engine is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero 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.Linq; using Deltares.DamEngine.Data.Geometry; using Deltares.DamEngine.Data.Standard; using Deltares.DamEngine.Data.Standard.Language; using Deltares.DamEngine.Data.Standard.Validation; namespace Deltares.DamEngine.Data.Geotechnics { /// /// Representation of a surface line with characteristic point annotations. /// /// Extension methods can be found in . public class SurfaceLine2 : GeometryObject { private CharacteristicPointSet characteristicPoints; private GeometryPointString geometry; /// /// Create a new empty surface line. /// public SurfaceLine2() { geometry = new GeometryPointString(); characteristicPoints = new CharacteristicPointSet { GeometryMustContainPoint = true }; LandwardDirection = LandwardDirection.PositiveX; Name = LocalizationManager.GetTranslatedText(this, "DefaultNameSurfaceLine2"); } /// /// Deep clone the object. /// /// public SurfaceLine2 FullDeepClone() { SurfaceLine2 surfaceLine2 = new SurfaceLine2(); surfaceLine2.SetValuesFromOtherSurfaceLine(this, true); return surfaceLine2; } /// /// The geometrical description of the surface line. /// /// Aggregation relationship. public GeometryPointString Geometry { get { return geometry; } set { geometry = value; CharacteristicPoints.Geometry = geometry; } } /// /// The characteristic point annotations for . /// /// Cannot be null. public CharacteristicPointSet CharacteristicPoints { get { return characteristicPoints; } set { characteristicPoints = value; characteristicPoints.Geometry = geometry; } } /// /// Determines toward which direction land is expected. Value is used as reference. /// public LandwardDirection LandwardDirection { get; set; } /// /// Define a new characteristic point on the surface line with the given characteristic /// point annotation. /// /// The point to be added. Cannot be null. /// The annotations for . public void AddCharacteristicPoint(GeometryPoint geometryPoint, params CharacteristicPointType[] annotations) { if (annotations == null || annotations.Length == 0) { CharacteristicPoints.Add(new CharacteristicPoint { GeometryPoint = geometryPoint, CharacteristicPointType = CharacteristicPointType.None }); } else { foreach (var type in annotations) { CharacteristicPoints.Add(new CharacteristicPoint { GeometryPoint = geometryPoint, CharacteristicPointType = type }); } } } /// /// Define a new characteristic point on the surface line with the given characteristic /// point annotation. /// /// The x. /// The y. /// The annotations for geometryPoint with x, z/>. public void AddCharacteristicPoint(double x, double z, params CharacteristicPointType[] annotations) { GeometryPoint geometryPoint = new GeometryPoint(x, z); AddCharacteristicPoint(geometryPoint, annotations); } /// /// Find all characteristic point annotations corresponding with the given location. /// /// /// public IEnumerable GetCharacteristicPoints(GeometryPoint geometryPoint) { return CharacteristicPoints.Where(cp => ReferenceEquals(cp.GeometryPoint, geometryPoint)) .Select(cp => cp.CharacteristicPointType); } /// /// Sort all points on X in a ascending manner. /// public void SortPoints() { CharacteristicPoints.Sort(); } /// /// Gets the points orderd by x. /// /// /// The points orderd by x. /// private IEnumerable PointsOrderdByX { get { return Geometry.Points.OrderBy(p => p.X); } } /// /// Tests if the given point is between the given start X and end X. /// /// The point. /// The start x. /// The end x. /// true if the given point is between the given start X and end X, otherwise false private bool TestIncluding(GeometryPoint point, double startX, double endX) { if (point == null) { return false; } double x = point.X; return (x >= startX || x.AlmostEquals(startX, GeometryPoint.Precision)) && (x < endX || x.AlmostEquals(endX, GeometryPoint.Precision)); } /// /// Gets the point segment including given start x and end x. /// /// The start x. /// The end x. /// collection of points between start X and end X (inlcuding those) /// End value is smaller then the start value public virtual IEnumerable GetPointSegmentIncluding(double startX, double endX) { if (endX < startX) { throw new ArgumentException("End value is smaller then the start value"); } return from point in PointsOrderdByX where TestIncluding(point, startX, endX) orderby point.X ascending select point; } /// /// Validates this surfaceline. /// /// All validation messages. [Validate] public ValidationResult[] Validate() { return new SurfaceLine2Validator().Validate(this).ToArray(); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return Name; } /// /// Gets the geometry bounds (i.e. the extents for drawing) /// /// public override GeometryBounds GetGeometryBounds() { if (CharacteristicPoints.Geometry != null) { return CharacteristicPoints.Geometry.GetGeometryBounds(); } return null; } /// /// Copy all characteristic point related data from another . /// /// Data source public void Assign(SurfaceLine2 source) { SetValuesFromOtherSurfaceLine(source); } /// /// Sets the values from another surface line. /// /// The source. /// If set to true, the of /// will be deep cloned, otherwise the reference is shared. private void SetValuesFromOtherSurfaceLine(SurfaceLine2 source, bool cloneGeometry = false) { CharacteristicPoints.Clear(); // clears the whole characteristic points state. Name = source.Name; Geometry.Name = Name; LandwardDirection = source.LandwardDirection; CharacteristicPoints.GeometryMustContainPoint = source.CharacteristicPoints.GeometryMustContainPoint; Geometry = cloneGeometry ? source.Geometry.Clone() : source.Geometry; var geometryAnnotations = GetCharacteristicAnnotationsInSource(source, cloneGeometry); // Reconstruct annotation state from dictionary: if (CharacteristicPoints.GeometryMustContainPoint) { foreach (var annotation in geometryAnnotations) { for (int i = 0; i < annotation.Value.Length; i++) { var index = -1; for (int j = 0; j < CharacteristicPoints.Count; j++) { if (ReferenceEquals(CharacteristicPoints[j].GeometryPoint, annotation.Key)) { index = j; break; } } if (i == 0) { // Reassign annotation of already created CharacteristicPoint: CharacteristicPoints.Annotate(index, annotation.Value[i]); } else { // Add new CharacteristicPoint instance for all subsequent annotations and ensuring to keep the defined order: CharacteristicPoints.Insert(index + i, new CharacteristicPoint(CharacteristicPoints, annotation.Key) { CharacteristicPointType = annotation.Value[i] }); } } } } else { foreach (var annotation in geometryAnnotations) { AddCharacteristicPoint((GeometryPoint)annotation.Key.Clone(), annotation.Value); } } } /// /// Collapses all characteristic point annotations in the given surfaceline into /// a dictionary keyed on instances in that surfaceline /// and their annotations. /// /// The referenced surfaceline. /// True if should be a clone from /// .; false if it should /// take the same instance instead. /// Dictionary keyed on instances in the surfaceline /// that have annotations. private Dictionary GetCharacteristicAnnotationsInSource(SurfaceLine2 source, bool cloneGeometry) { return CharacteristicPoints.GeometryMustContainPoint ? GetCharacteristicAnnotationsInSource_GeometryMustContainPoints(source, cloneGeometry) : GetCharacteristicAnnotationsInSource_GeometryMustNotContainPoints(source); } /// /// Handlers return value in case /// is true. /// /// The referenced surfaceline. /// True if should be a clone from /// .; false if it should /// take the same instance instead. /// Dictionary keyed on instances in the surfaceline /// that have annotations. private Dictionary GetCharacteristicAnnotationsInSource_GeometryMustContainPoints(SurfaceLine2 source, bool cloneGeometry) { var geometryAnnotations = new Dictionary(); for (int i = 0; i < source.Geometry.Count; i++) { var annotationsForPoint = source.GetCharacteristicPoints(source.Geometry.Points[i]).Where(cpt => cpt != CharacteristicPointType.None).ToArray(); if (annotationsForPoint.Length > 0) { geometryAnnotations[Geometry.Points[i]] = annotationsForPoint; } } return geometryAnnotations; } /// /// Handles return value in case /// is false. /// /// The referenced surfaceline. /// Dictionary keyed on instances in the surfaceline /// that have annotations. private static Dictionary GetCharacteristicAnnotationsInSource_GeometryMustNotContainPoints(SurfaceLine2 source) { var geometryAnnotations = new Dictionary(); foreach (var characteristicPoint in source.CharacteristicPoints) { if (!geometryAnnotations.ContainsKey(characteristicPoint.GeometryPoint)) { geometryAnnotations[characteristicPoint.GeometryPoint] = source.GetCharacteristicPoints(characteristicPoint.GeometryPoint).ToArray(); } } return geometryAnnotations; } } }