// 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.Geometry;
using Ringtoets.Piping.IO.Properties;
using Ringtoets.Piping.Primitives;
namespace Ringtoets.Piping.IO.Builders
{
///
/// This class represents objects which were imported from a DSoilModel database.
/// Instances of this class are transient and are not to be used once the DSoilModel
/// database has been imported.
///
internal class SoilLayer2D
{
private readonly Collection innerLoops;
private Segment2D[] outerLoop;
///
/// Creates a new instance of .
///
public SoilLayer2D()
{
innerLoops = new Collection();
}
///
/// Gets or sets a value representing
/// whether the is an aquifer.
///
public double? IsAquifer { get; set; }
///
/// Gets or sets the above phreatic level for the .
///
public double? AbovePhreaticLevel { get; set; }
///
/// Gets or sets the below phreatic level for the .
///
public double? BelowPhreaticLevel { get; set; }
///
/// Gets or sets the dry unit weight for the .
///
public double? DryUnitWeight { get; set; }
///
/// Gets the outer loop of the as a of ,
/// for which each of the segments are connected to the next.
///
/// Thrown when the in
/// do not form a loop.
public IEnumerable OuterLoop
{
get
{
return outerLoop;
}
internal set
{
var loop = value.ToArray();
CheckValidLoop(loop);
outerLoop = loop;
}
}
///
/// Gets the of inner loops (as of ,
/// for which each of the segments are connected to the next) of the .
///
public IEnumerable InnerLoops
{
get
{
return innerLoops;
}
}
///
/// Adds an inner loop to the geometry.
///
/// The innerloop to add.
/// Thrown when the in
/// do not form a loop.
internal void AddInnerLoop(IEnumerable innerLoop)
{
var loop = innerLoop.ToArray();
CheckValidLoop(loop);
innerLoops.Add(loop);
}
///
/// Constructs a (1D) based on the and set for the .
///
/// The point from which to take a 1D profile.
/// The bottom level of the .
/// A of .
/// Thrown when any of the or
/// contain a vertical line at .
internal IEnumerable AsPipingSoilLayers(double atX, out double bottom)
{
bottom = Double.MaxValue;
var result = new Collection();
if (OuterLoop != null)
{
IEnumerable outerLoopIntersectionHeights = GetLoopIntersectionHeights(outerLoop, atX);
if (outerLoopIntersectionHeights.Any())
{
IEnumerable> innerLoopsIntersectionHeights = InnerLoops.Select(loop => GetLoopIntersectionHeights(loop, atX));
IEnumerable> innerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(innerLoopsIntersectionHeights).ToList();
IEnumerable> outerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(outerLoopIntersectionHeights).ToList();
var currentBottom = outerLoopIntersectionHeightPairs.First().Item1;
var heights = new List();
heights.AddRange(innerLoopIntersectionHeightPairs.Where(p => p.Item1 >= currentBottom).Select(p => p.Item1));
heights.AddRange(outerLoopIntersectionHeightPairs.Select(p => p.Item2));
foreach (var height in heights.Where(height => !innerLoopIntersectionHeightPairs.Any(tuple => HeightInInnerLoop(tuple, height))))
{
result.Add(new PipingSoilLayer(height)
{
IsAquifer = IsAquifer.HasValue && IsAquifer.Value.Equals(1.0),
BelowPhreaticLevel = BelowPhreaticLevel,
AbovePhreaticLevel = AbovePhreaticLevel,
DryUnitWeight = DryUnitWeight
});
}
bottom = EnsureBottomOutsideInnerLoop(innerLoopIntersectionHeightPairs, currentBottom);
}
}
return result;
}
private static void CheckValidLoop(Segment2D[] innerLoop)
{
if (innerLoop.Length == 1 || !IsLoopConnected(innerLoop))
{
throw new ArgumentException(Resources.SoilLayer2D_Error_Loop_contains_disconnected_segments);
}
}
private static bool IsLoopConnected(Segment2D[] segments)
{
int segmentCount = segments.Length;
if (segmentCount == 2)
{
return segments[0].Equals(segments[1]);
}
for (int i = 0; i < segmentCount; i++)
{
var segmentA = segments[i];
var segmentB = segments[(i + 1)%segmentCount];
if (!segmentA.IsConnected(segmentB))
{
return false;
}
}
return true;
}
private double EnsureBottomOutsideInnerLoop(IEnumerable> innerLoopIntersectionHeightPairs, double bottom)
{
var newBottom = bottom;
var heigthPairArray = innerLoopIntersectionHeightPairs.ToList();
var overlappingInnerLoop = heigthPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom));
while (overlappingInnerLoop != null)
{
newBottom = overlappingInnerLoop.Item2;
overlappingInnerLoop = heigthPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom));
}
return newBottom;
}
private bool HeightInInnerLoop(Tuple tuple, double height)
{
return height <= tuple.Item2 && height > tuple.Item1;
}
private bool BottomInInnerLoop(Tuple tuple, double height)
{
return height < tuple.Item2 && height >= tuple.Item1;
}
private IEnumerable> GetOrderedStartAndEndPairsIn1D(IEnumerable> innerLoopsIntersectionPoints)
{
Collection> result = new Collection>();
foreach (var innerLoopIntersectionPoints in innerLoopsIntersectionPoints)
{
foreach (var tuple in GetOrderedStartAndEndPairsIn1D(innerLoopIntersectionPoints))
{
result.Add(tuple);
}
}
return result;
}
private static Collection> GetOrderedStartAndEndPairsIn1D(IEnumerable innerLoopIntersectionPoints)
{
var result = new Collection>();
var orderedHeights = innerLoopIntersectionPoints.OrderBy(v => v).ToList();
for (int i = 0; i < orderedHeights.Count; i = i + 2)
{
var first = orderedHeights[i];
var second = orderedHeights[i + 1];
result.Add(Tuple.Create(first, second));
}
return result;
}
///
/// Gets a of heights where the intersects the
/// vertical line at .
///
/// The sequence of which together create a loop.
/// The point on the x-axis where the vertical line is constructed do determine intersections with.
/// A of , representing the height at which the
/// intersects the vertical line at .
/// Thrown when a segment is vertical at and thus
/// no deterministic intersection points can be determined.
private IEnumerable GetLoopIntersectionHeights(IEnumerable loop, double atX)
{
var segment2Ds = loop.ToArray();
if (segment2Ds.Any(segment => IsVerticalAtX(segment, atX)))
{
var message = string.Format(Resources.Error_Can_not_determine_1D_profile_with_vertical_segments_at_X_0_, atX);
throw new SoilLayer2DConversionException(message);
}
return Math2D.SegmentsIntersectionWithVerticalLine(segment2Ds, atX).Select(p => p.Y);
}
private static bool IsVerticalAtX(Segment2D segment, double atX)
{
return segment.FirstPoint.X.Equals(atX) && segment.IsVertical();
}
}
}