Index: dam engine/branches/Initial Source/Deltares.DamEngine.Data/Geotechnics/SurfaceLine2Extensions.cs =================================================================== diff -u -r316 -r330 --- dam engine/branches/Initial Source/Deltares.DamEngine.Data/Geotechnics/SurfaceLine2Extensions.cs (.../SurfaceLine2Extensions.cs) (revision 316) +++ dam engine/branches/Initial Source/Deltares.DamEngine.Data/Geotechnics/SurfaceLine2Extensions.cs (.../SurfaceLine2Extensions.cs) (revision 330) @@ -23,6 +23,7 @@ using System.Collections.Generic; using System.Linq; using Deltares.DamEngine.Data.Geometry; +using Deltares.DamEngine.Data.Standard; namespace Deltares.DamEngine.Data.Geotechnics { @@ -134,5 +135,412 @@ IsDefined(line, CharacteristicPointType.DikeTopAtPolder) && IsDefined(line, CharacteristicPointType.DikeToeAtPolder); } + + /// + /// Add 2D characteristic point without a type + /// + /// The surfaceline to be modified. + /// The x. + /// The z. + public static void EnsurePoint(this SurfaceLine2 line, double x, double z) + { + line.EnsurePointOfType(x, z, null); + } + + /// + /// Add characteristic point in X-Z plane + /// + /// The surfaceline to be modified. + /// The x. + /// The z. + /// The type. + public static void EnsurePointOfType(this SurfaceLine2 line, double x, double z, CharacteristicPointType? type) + { + if (!line.CharacteristicPoints.GeometryMustContainPoint) + { + throw new NotImplementedException("SurfaceLine specific method, not implemented for 'Ringtoets'-mode."); + } + + bool movedPointToMatch = false; + bool addPoint = false; + Point2D point = null; + var newPoint = new Point2D() + { + X = x, + Z = z + }; + GeometryPoint gp = null; + if (type.HasValue) + { + // Get point of this type.. + gp = line.CharacteristicPoints.GetGeometryPoint(type.Value); + if (gp != null) + { + point = new Point2D(gp.X, gp.Z); + // Characteristic point annotation set, check location... + if (point.LocationEquals(newPoint)) + { + // Annotated point is at given location; We're done here! :) + return; + } + else + { + bool isAssignedToOtherCharacteristicPoints = + line.GetCharacteristicPoints(gp).Count(cpt => cpt != CharacteristicPointType.None) > 1; + if (isAssignedToOtherCharacteristicPoints) + { + // Other characteristic points exist with the same coordinates so add as new point + point = line.Geometry.GetPointAt(newPoint.X, newPoint.Z); // Get point at specified coords + if (point == null) + { + point = newPoint; + addPoint = true; + } + } + else + { + // Point is unique as characteristic point so set its coords to specified coords + point.X = x; + point.Z = z; + movedPointToMatch = true; + } + gp.X = point.X; + gp.Z = point.Z; + } + } + } + if (point == null) + { + point = line.Geometry.GetPointAt(x, z); // Get point at specified coords + } + if (point == null) + { + point = new Point2D() + { + X = x, + Z = z + }; + addPoint = true; + } + if (addPoint) + { + var newgp = new GeometryPoint(point.X, point.Z); + line.Geometry.Points.Add(newgp); + line.AddCharacteristicPoint(newgp, type ?? CharacteristicPointType.None); + } + else if (type.HasValue && !movedPointToMatch) + { + if (line.CharacteristicPoints.Any(cp => ReferenceEquals(cp.GeometryPoint, gp) && + cp.CharacteristicPointType != CharacteristicPointType.None)) + { + line.AddCharacteristicPoint(gp, type.Value); + } + else + { + int index = -1; + for (int i = 0; i < line.CharacteristicPoints.Count; i++) + { + if (ReferenceEquals(line.CharacteristicPoints[i].GeometryPoint, gp)) + { + index = i; + break; + } + } + line.CharacteristicPoints.Annotate(index, type.Value); + } + } + } + + /// + /// Removes the points between the two x values. The start and end points will + /// not be removed. + /// + /// Surfaceline being modified. + /// The non-inclusive starting X-coordinate. + /// The non-inclusive ending X-coordinate. + /// + public static void RemoveSegmentBetween(this SurfaceLine2 line, double startX, double endX) + { + RemoveGeometryPointsInRange(line, startX, endX, false); + } + + /// + /// Removes the points between the two x values. + /// + /// Surfaceline being modified. + /// The inclusive starting X-Coordinate. + /// The inclusive ending X-Coordinate. + /// + public static void RemoveSegmentIncluding(this SurfaceLine2 line, double startX, double endX) + { + RemoveGeometryPointsInRange(line, startX, endX, true); + } + + /// + /// Removes the points between the two x values. + /// + /// Surfaceline being modified. + /// The starting X-Coordinate. + /// The ending X-Coordinate. + /// Indicates if and + /// are inclusive bounds or not. + private static void RemoveGeometryPointsInRange(SurfaceLine2 line, double startX, double endX, bool isInclusiveRange) + { + foreach (var geometryPoint in GetGeometryPointsWithinRange(line, startX, endX, isInclusiveRange).ToArray()) + { + var characteristicPoints = line.CharacteristicPoints.Where(cp => ReferenceEquals(cp.GeometryPoint, geometryPoint)).ToArray(); + if (characteristicPoints.Length > 0) + { + // CharacteristicPointSet will manage both collections of CharacteristicPoint instances and Geometry + foreach (var characteristicPoint in characteristicPoints) + { + line.CharacteristicPoints.Remove(characteristicPoint); + } + } + else + { + // Notify change such that CharacteristicPointSet instances observing this + // geometry can update if required. + line.Geometry.Points.Remove(geometryPoint); + } + } + } + + /// + /// Retrieve all instances of a surfaceline that + /// fall with the range. + /// + /// Surfaceline being evaluated. + /// Starting X-coordinate. + /// Ending X-coordinate. + /// Indicates if and + /// are inclusive bounds or not. + /// Collection of characteristic points within the given range. + private static IEnumerable GetGeometryPointsWithinRange(SurfaceLine2 line, + double startX, double endX, bool isInclusiveRange) + { + if (isInclusiveRange) + { + return line.Geometry.Points.Where(cp => (cp.X >= startX || cp.X.AlmostEquals(startX, GeometryPoint.Precision)) && + (cp.X <= endX || cp.X.AlmostEquals(endX, GeometryPoint.Precision))); + } + return line.Geometry.Points.Where(cp => cp.X > startX && cp.X < endX); + } + + /// + /// Checks if a surfaceline has all characteristic point types required to describe + /// an inside shoulder. + /// + /// Surfaceline to be checked. + /// True if there are characteristic points defined that described the + /// inside shoulder; False otherwise. + public static bool HasShoulderInside(this SurfaceLine2 line) + { + return IsDefined(line, CharacteristicPointType.ShoulderTopInside) && + IsDefined(line, CharacteristicPointType.ShoulderBaseInside); + } + + /// + /// Checks if a surfaceline has all characteristic point types required to describe + /// a ditch. + /// + /// Surfaceline to be checked. + /// True if there are characteristic points defined that described the + /// ditch; False otherwise. + public static bool HasDitch(this SurfaceLine2 line) + { + return IsDefined(line, CharacteristicPointType.DitchDikeSide) && + IsDefined(line, CharacteristicPointType.DitchPolderSide); + } + + /// + /// Determines whether ditch is correct. + /// + /// true if ditch is correct, otherwise false + /// This methods checks if the following points have their X cooridnates + /// properly defined: + /// + /// + /// + /// + /// + /// + /// + public static bool IsDitchCorrect(this SurfaceLine2 line) + { + // No ditch then always ok + bool res = !line.HasDitch(); + if (!res) + { + // check the unchecked points + var bottomDitchDikeSide = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.BottomDitchDikeSide); + var bottomDitchPolderSide = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.BottomDitchPolderSide); + res = (bottomDitchDikeSide != null && bottomDitchPolderSide != null); + + // check the ditch points describe following shape: + // 0 0 + // \ / + // 0---0 + var ditchPolderSide = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchPolderSide); + var ditchDikeSide = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchDikeSide); + if (res) + { + res = ditchPolderSide.X >= bottomDitchPolderSide.X && + bottomDitchPolderSide.X >= bottomDitchDikeSide.X && + bottomDitchDikeSide.X >= ditchDikeSide.X && + bottomDitchDikeSide.Z <= ditchDikeSide.Z && + bottomDitchPolderSide.Z <= ditchPolderSide.Z; + } + } + return res; + } + + /// + /// Gets the starting point of the surface line. + /// + /// Starting point, or null if none of the considered points can be found. + /// + /// Method looks for the following characteristic points (in this order) and returns + /// the corresponding geometry point if present: + /// + /// + /// + /// + /// + /// + public static GeometryPoint GetStartingPoint(this SurfaceLine2 line) + { + return line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.ShoulderBaseOutside) ?? + line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.ShoulderTopOutside) ?? + line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver); + } + + /// + /// Gets the default height of the dike table. + /// + /// null or height of table + public static double? GetDefaultDikeTableHeight(this SurfaceLine2 line) + { + // Consulted Erik Vastenburg about this: Use buitenkruinlijn as default DTH + GeometryPoint point = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtRiver); + return point == null ? null : (double?)point.Z; + } + + /// + /// Make sure river level is above the bottom of the river. + /// + /// The evaluated surfaceline. + /// The current river level. + /// Bottom of the river if bottomlevel is above riverLevel, else + /// is required to have the characteristic point + /// when + /// is not null. + /// + public static double? EnsureWaterLevelIsAboveRiverBottom(this SurfaceLine2 line, double? riverLevel) + { + // Waterlevel is supposed to be at level of SurfaceLevelOutside when waterlevel + // is below SurfaceLevelOutside (is the bottom of the river) + if (riverLevel.HasValue) + { + double z = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelOutside).Z; + riverLevel = Math.Max(z, riverLevel.Value); + } + return riverLevel; + } + + /// + /// Determines the intersection of a horizontal line with the surfaceline, starting + /// from to . + /// + /// The surfaceline being evaluated. + /// The height of the horizontal line. + /// The GeometryPoint at the intersection, or null if no intersection can + /// be found. + /// + /// Requires that the following characteristic points are defined: + /// + /// + /// + /// + /// + /// This method draws the horizontal line starting from the smallest X available + /// in the geometry to . + /// + /// When greater than + /// the height of the characteristic point . + /// + public static GeometryPoint DetermineIntersectionWithLevel(this SurfaceLine2 line, double level) + { + double startXCoordinate = line.Geometry.GetMinX(); + var waterlevelLine = GetWaterlevelLineStartingFrom(line, level, startXCoordinate); + + return DetermineIntersectionWithHorizontalLevel(line, waterlevelLine); + } + + /// + /// Create a horizontal line from a given starting X coordinate and ending at the + /// X coordinate of defined + /// in the surfaceline. + /// + /// The referenced surfaceline. + /// The height level of the horizontal line. + /// The starting coordinate. + /// The line segment. + private static Line GetWaterlevelLineStartingFrom(SurfaceLine2 line, double level, double startXCoordinate) + { + GeometryPoint pointEndOfprofile = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelInside); + var waterlevelLine = new Line(); + waterlevelLine.CreateHorizontalZLine(startXCoordinate, pointEndOfprofile.X, level); + + return waterlevelLine; + } + + /// + /// Finds the intersection point of a given horizontal line and the river side talud. + /// + /// The evaluated surfaceline. + /// The horizontal line. + /// The intersection point, or null in case no intersection was found. + /// When height of the horizontal line is + /// greater than the height of the characteristic point . + private static GeometryPoint DetermineIntersectionWithHorizontalLevel(SurfaceLine2 line, Line waterlevelLine) + { + ThrowWhenLevelAboveDike(line, waterlevelLine.BeginPoint.Z); + + var list = line.Geometry.Points.Where(point => + point.X >= line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelOutside).X && + point.X <= line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtRiver).X).ToList(); + + for (int i = 1; i < list.Count; i++) + { + var surfaceLineSegment = new Line(); + surfaceLineSegment.SetBeginAndEndPoints( + new Point2D(list[i - 1].X, list[i - 1].Z), + new Point2D(list[i].X, list[i].Z)); + + var intersectPoint = surfaceLineSegment.GetIntersectPointXz(waterlevelLine); + if (intersectPoint != null) + { + return new GeometryPoint(intersectPoint.X, intersectPoint.Z); + } + } + + return null; + } + + /// + /// Throw an in case the given height level is + /// higher than the characteristic point . + /// + private static void ThrowWhenLevelAboveDike(SurfaceLine2 line, double Level) + { + var dikeTopAtRiver = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtRiver); + if (Level > dikeTopAtRiver.Z) + { + throw new SurfaceLineException(String.Format( + "Level ({0:F2} m) should NOT be higher than surface line ({1:F2}))", + Level, dikeTopAtRiver.Z)); + } + } } } \ No newline at end of file