Index: Ringtoets/Common/src/Ringtoets.Common.IO/Properties/AssemblyInfo.cs =================================================================== diff -u -rb3b6c13cf736c134476b3db34281332d01ca86b1 -r039c5e7dabfcfae7fa9968c3336e542ebe879395 --- Ringtoets/Common/src/Ringtoets.Common.IO/Properties/AssemblyInfo.cs (.../AssemblyInfo.cs) (revision b3b6c13cf736c134476b3db34281332d01ca86b1) +++ Ringtoets/Common/src/Ringtoets.Common.IO/Properties/AssemblyInfo.cs (.../AssemblyInfo.cs) (revision 039c5e7dabfcfae7fa9968c3336e542ebe879395) @@ -26,4 +26,5 @@ [assembly: AssemblyTitle("Ringtoets.Common.IO")] [assembly: AssemblyProduct("Ringtoets.Common.IO")] [assembly: Guid("a5cefa3a-677c-4bd0-85a7-08657cfe6c70")] -[assembly: InternalsVisibleTo("Ringtoets.Common.IO.Test")] \ No newline at end of file +[assembly: InternalsVisibleTo("Ringtoets.Common.IO.Test")] +[assembly: InternalsVisibleTo("Ringtoets.Common.IO.TestUtil")] \ No newline at end of file Index: Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil.Test/Ringtoets.Common.IO.TestUtil.Test.csproj =================================================================== diff -u -r56ac4eb28f5fcc5b20117474e9e4030399d6806a -r039c5e7dabfcfae7fa9968c3336e542ebe879395 --- Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil.Test/Ringtoets.Common.IO.TestUtil.Test.csproj (.../Ringtoets.Common.IO.TestUtil.Test.csproj) (revision 56ac4eb28f5fcc5b20117474e9e4030399d6806a) +++ Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil.Test/Ringtoets.Common.IO.TestUtil.Test.csproj (.../Ringtoets.Common.IO.TestUtil.Test.csproj) (revision 039c5e7dabfcfae7fa9968c3336e542ebe879395) @@ -46,6 +46,7 @@ Properties\GlobalAssembly.cs + Index: Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil.Test/SoilLayer2DTestFactoryTest.cs =================================================================== diff -u --- Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil.Test/SoilLayer2DTestFactoryTest.cs (revision 0) +++ Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil.Test/SoilLayer2DTestFactoryTest.cs (revision 039c5e7dabfcfae7fa9968c3336e542ebe879395) @@ -0,0 +1,140 @@ +// 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.Linq; +using Core.Common.Base.Geometry; +using NUnit.Framework; +using Ringtoets.Common.IO.SoilProfile; + +namespace Ringtoets.Common.IO.TestUtil.Test +{ + [TestFixture] + public class SoilLayer2DTestFactoryTest + { + [Test] + public void CreateSoilLayer2D_InnerLoopNull_ThrowsArgumentNullException() + { + // Call + TestDelegate call = () => SoilLayer2DTestFactory.CreateSoilLayer2D( + null, + Enumerable.Empty()); + + // Assert + string parameter = Assert.Throws(call).ParamName; + Assert.AreEqual("innerLoops", parameter); + } + + [Test] + public void CreateSoilLayer2D_OuterLoopNull_ThrowsArgumentNullException() + { + // Call + TestDelegate call = () => SoilLayer2DTestFactory.CreateSoilLayer2D( + Enumerable.Empty(), + null); + + // Assert + string parameter = Assert.Throws(call).ParamName; + Assert.AreEqual("outerLoop", parameter); + } + + [Test] + public void CreateSoilLayer2D_InvalidInnerLoop_ThrowsArgumentException() + { + // Setup + var pointA = new Point2D(0.0, 0.0); + var pointB = new Point2D(1.0, 0.0); + var pointC = new Point2D(1.0, 1.0); + + // Call + TestDelegate test = () => SoilLayer2DTestFactory.CreateSoilLayer2D( + new[] + { + new[] + { + new Segment2D(pointA, pointB), + new Segment2D(pointB, pointC) + } + }, + Enumerable.Empty()); + + // Assert + Assert.Throws(test); + } + + [Test] + public void CreateSoilLayer2D_InvalidOuterLoop_ThrowsArgumentException() + { + // Setup + var pointA = new Point2D(0.0, 0.0); + var pointB = new Point2D(1.0, 0.0); + var pointC = new Point2D(1.0, 1.0); + + // Call + TestDelegate test = () => SoilLayer2DTestFactory.CreateSoilLayer2D( + Enumerable.Empty(), + new[] + { + new Segment2D(pointA, pointB), + new Segment2D(pointB, pointC) + } + ); + + // Assert + Assert.Throws(test); + } + + [Test] + public void CreateSoilLayer2D_ValidArguments_ExpectedProperties() + { + // Setup + var pointA = new Point2D(0.0, 0.0); + var pointB = new Point2D(1.0, 0.0); + var pointC = new Point2D(1.0, 1.0); + var pointD = new Point2D(2.0, 1.0); + + // Call + SoilLayer2D soilLayer = SoilLayer2DTestFactory.CreateSoilLayer2D( + new[] + { + new[] + { + new Segment2D(pointC, pointD), + new Segment2D(pointD, pointC) + } + }, + new List + { + new Segment2D(pointA, pointB), + new Segment2D(pointB, pointA) + }); + + // Assert + Assert.AreEqual(new Segment2D(pointA, pointB), soilLayer.OuterLoop.ElementAt(0)); + Assert.AreEqual(new Segment2D(pointB, pointA), soilLayer.OuterLoop.ElementAt(1)); + + Assert.AreEqual(1, soilLayer.InnerLoops.Count()); + Assert.AreEqual(new Segment2D(pointC, pointD), soilLayer.InnerLoops.ElementAt(0)[0]); + Assert.AreEqual(new Segment2D(pointD, pointC), soilLayer.InnerLoops.ElementAt(0)[1]); + } + } +} \ No newline at end of file Index: Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil/Ringtoets.Common.IO.TestUtil.csproj =================================================================== diff -u -re507c88f2863ff7bc93505caf71c5f8025fb48c0 -r039c5e7dabfcfae7fa9968c3336e542ebe879395 --- Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil/Ringtoets.Common.IO.TestUtil.csproj (.../Ringtoets.Common.IO.TestUtil.csproj) (revision e507c88f2863ff7bc93505caf71c5f8025fb48c0) +++ Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil/Ringtoets.Common.IO.TestUtil.csproj (.../Ringtoets.Common.IO.TestUtil.csproj) (revision 039c5e7dabfcfae7fa9968c3336e542ebe879395) @@ -53,6 +53,7 @@ + Index: Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil/SoilLayer2DTestFactory.cs =================================================================== diff -u --- Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil/SoilLayer2DTestFactory.cs (revision 0) +++ Ringtoets/Common/test/Ringtoets.Common.IO.TestUtil/SoilLayer2DTestFactory.cs (revision 039c5e7dabfcfae7fa9968c3336e542ebe879395) @@ -0,0 +1,69 @@ +// 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 Core.Common.Base.Geometry; +using Ringtoets.Common.IO.SoilProfile; + +namespace Ringtoets.Common.IO.TestUtil +{ + /// + /// Factory to create simple instances that can be used for testing. + /// + public static class SoilLayer2DTestFactory + { + /// + /// Creates a new instance of that can be used for testing. + /// + /// The inner loops of the , + /// for which each of the segments are connected to the next + /// The outer loop of the , + /// for which each of the segments are connected to the next. + /// The created . + /// Thrown when any input parameter is null. + /// Thrown when the in + /// or do not form a loop. + public static SoilLayer2D CreateSoilLayer2D(IEnumerable innerLoops, IEnumerable outerLoop) + { + if (innerLoops == null) + { + throw new ArgumentNullException(nameof(innerLoops)); + } + if (outerLoop == null) + { + throw new ArgumentNullException(nameof(outerLoop)); + } + + var soilLayer2D = new SoilLayer2D + { + OuterLoop = outerLoop + }; + + foreach (Segment2D[] innerLoop in innerLoops) + { + soilLayer2D.AddInnerLoop(innerLoop); + } + + return soilLayer2D; + } + } +} \ No newline at end of file Index: Ringtoets/Piping/src/Ringtoets.Piping.IO/Importers/PipingSoilLayerTransformer.cs =================================================================== diff -u -rc08a823b305b8f2572526d840e0f645ee637225f -r039c5e7dabfcfae7fa9968c3336e542ebe879395 --- Ringtoets/Piping/src/Ringtoets.Piping.IO/Importers/PipingSoilLayerTransformer.cs (.../PipingSoilLayerTransformer.cs) (revision c08a823b305b8f2572526d840e0f645ee637225f) +++ Ringtoets/Piping/src/Ringtoets.Piping.IO/Importers/PipingSoilLayerTransformer.cs (.../PipingSoilLayerTransformer.cs) (revision 039c5e7dabfcfae7fa9968c3336e542ebe879395) @@ -20,12 +20,17 @@ // All rights reserved. using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Core.Common.Base.Geometry; using Ringtoets.Common.IO.Exceptions; using Ringtoets.Common.IO.SoilProfile; using Ringtoets.Piping.IO.Builders; using Ringtoets.Piping.IO.Properties; using Ringtoets.Piping.Primitives; using SoilLayer1D = Ringtoets.Common.IO.SoilProfile.SoilLayer1D; +using SoilLayer2D = Ringtoets.Common.IO.SoilProfile.SoilLayer2D; namespace Ringtoets.Piping.IO.Importers { @@ -64,6 +69,56 @@ return pipingSoilLayer; } + public static IEnumerable Transform(SoilLayer2D soilLayer, double atX, out double bottom) + { + if (soilLayer == null) + { + throw new ArgumentNullException(nameof(soilLayer)); + } + + ValidateStochasticParametersForPiping(soilLayer); + + bottom = double.MaxValue; + + if (soilLayer.OuterLoop == null) + { + return Enumerable.Empty(); + } + + double[] outerLoopIntersectionHeights = GetLoopIntersectionHeights(soilLayer.OuterLoop, atX).ToArray(); + + if (!outerLoopIntersectionHeights.Any()) + { + return Enumerable.Empty(); + } + + var soilLayers = new Collection(); + IEnumerable> innerLoopsIntersectionHeights = soilLayer.InnerLoops.Select(loop => GetLoopIntersectionHeights(loop, atX)); + IEnumerable> innerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(innerLoopsIntersectionHeights).ToList(); + IEnumerable> outerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(outerLoopIntersectionHeights).ToList(); + + double 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 (double height in heights.Where(height => !innerLoopIntersectionHeightPairs.Any(tuple => HeightInInnerLoop(tuple, height)))) + { + var pipingSoilLayer = new PipingSoilLayer(height) + { + IsAquifer = soilLayer.IsAquifer, + MaterialName = soilLayer.MaterialName, + Color = soilLayer.Color + }; + + SetOptionalStochasticParameters(pipingSoilLayer, soilLayer); + + soilLayers.Add(pipingSoilLayer); + } + bottom = EnsureBottomOutsideInnerLoop(innerLoopIntersectionHeightPairs, currentBottom); + return soilLayers; + } + /// /// Validates whether the values of the distribution and shift for the stochastic parameters /// are correct for creating a . @@ -122,5 +177,81 @@ incorrectDistibutionParameter)); } } + + private static bool HeightInInnerLoop(Tuple tuple, double height) + { + return height <= tuple.Item2 && height > tuple.Item1; + } + + private static bool BottomInInnerLoop(Tuple tuple, double height) + { + return height < tuple.Item2 && height >= tuple.Item1; + } + + private static double EnsureBottomOutsideInnerLoop(IEnumerable> innerLoopIntersectionHeightPairs, double bottom) + { + double newBottom = bottom; + List> heightPairArray = innerLoopIntersectionHeightPairs.ToList(); + Tuple overlappingInnerLoop = heightPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom)); + + while (overlappingInnerLoop != null) + { + newBottom = overlappingInnerLoop.Item2; + overlappingInnerLoop = heightPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom)); + } + return newBottom; + } + + private static IEnumerable> GetOrderedStartAndEndPairsIn1D(IEnumerable> innerLoopsIntersectionPoints) + { + var result = new Collection>(); + foreach (IEnumerable innerLoopIntersectionPoints in innerLoopsIntersectionPoints) + { + foreach (Tuple tuple in GetOrderedStartAndEndPairsIn1D(innerLoopIntersectionPoints)) + { + result.Add(tuple); + } + } + return result; + } + + private static Collection> GetOrderedStartAndEndPairsIn1D(IEnumerable innerLoopIntersectionPoints) + { + var result = new Collection>(); + List orderedHeights = innerLoopIntersectionPoints.OrderBy(v => v).ToList(); + for (var i = 0; i < orderedHeights.Count; i = i + 2) + { + double first = orderedHeights[i]; + double 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 static IEnumerable GetLoopIntersectionHeights(IEnumerable loop, double atX) + { + Segment2D[] segment2Ds = loop.ToArray(); + if (segment2Ds.Any(segment => IsVerticalAtX(segment, atX))) + { + string message = string.Format(Resources.Error_Can_not_determine_1D_profile_with_vertical_segments_at_X_0_, atX); + throw new ImportedDataTransformException(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(); + } } } \ No newline at end of file Index: Ringtoets/Piping/test/Ringtoets.Piping.IO.Test/Importers/PipingSoilLayerTransformerTest.cs =================================================================== diff -u -rc08a823b305b8f2572526d840e0f645ee637225f -r039c5e7dabfcfae7fa9968c3336e542ebe879395 --- Ringtoets/Piping/test/Ringtoets.Piping.IO.Test/Importers/PipingSoilLayerTransformerTest.cs (.../PipingSoilLayerTransformerTest.cs) (revision c08a823b305b8f2572526d840e0f645ee637225f) +++ Ringtoets/Piping/test/Ringtoets.Piping.IO.Test/Importers/PipingSoilLayerTransformerTest.cs (.../PipingSoilLayerTransformerTest.cs) (revision 039c5e7dabfcfae7fa9968c3336e542ebe879395) @@ -22,21 +22,25 @@ using System; using System.Collections.Generic; using System.Drawing; +using System.Linq; +using Core.Common.Base.Geometry; using Core.Common.TestUtil; using NUnit.Framework; using Ringtoets.Common.IO.Exceptions; +using Ringtoets.Common.IO.TestUtil; using Ringtoets.Piping.IO.Builders; using Ringtoets.Piping.IO.Importers; using Ringtoets.Piping.Primitives; using SoilLayer1D = Ringtoets.Common.IO.SoilProfile.SoilLayer1D; +using SoilLayer2D = Ringtoets.Common.IO.SoilProfile.SoilLayer2D; namespace Ringtoets.Piping.IO.Test.Importers { [TestFixture] public class PipingSoilLayerTransformerTest { [Test] - public void Transform_SoilLayer1DNull_ThrowsArgumentNullException() + public void SoilLayer1DTransform_SoilLayer1DNull_ThrowsArgumentNullException() { // Call TestDelegate test = () => PipingSoilLayerTransformer.Transform(null); @@ -47,7 +51,7 @@ } [Test] - public void Transform_PropertiesSetWithCorrectDistributionsAndDifferentLayerParameters_ExpectedProperties() + public void SoilLayer1DTransform_PropertiesSetWithCorrectDistributionsAndDifferentLayerParameters_ExpectedProperties() { // Setup var random = new Random(22); @@ -109,7 +113,7 @@ } [Test] - public void Transform_IncorrectShiftedLogNormalDistribution_ThrowsImportedDataTransformException() + public void SoilLayer1DTransform_IncorrectShiftedLogNormalDistribution_ThrowsImportedDataTransformException() { // Setup var layer = new SoilLayer1D(0.0) @@ -127,16 +131,127 @@ [Test] [TestCaseSource(nameof(IncorrectLogNormalDistributions))] - public void Transform_IncorrectLogNormalDistribution_ThrowsImportedDataTransformException(SoilLayer1D layer, string parameter) + public void SoilLayer1DTransform_IncorrectLogNormalDistribution_ThrowsImportedDataTransformException(SoilLayer1D layer, string parameter) { + // Setup + double bottom; + // Call - TestDelegate test = () => PipingSoilLayerTransformer.Transform(layer); + TestDelegate test = () => PipingSoilLayerTransformer.Transform(null, 0, out bottom); // Assert - Exception exception = Assert.Throws(test); - Assert.AreEqual($"Parameter '{parameter}' is niet lognormaal verdeeld.", exception.Message); + var exception = Assert.Throws(test); + Assert.AreEqual("soilLayer", exception.ParamName); } + [Test] + public void SoilLayer2DTransform_SoilLayer2DNull_ThrowsArgumentNullException() + { + // Setup + var layer = new SoilLayer2D(); + double bottom; + + // Call + IEnumerable result = PipingSoilLayerTransformer.Transform(layer, 0.0, out bottom); + + // Assert + CollectionAssert.IsEmpty(result); + Assert.AreEqual(double.MaxValue, bottom); + } + + [Test] + public void SoilLayer2DTransform_EmptySoilLayer2D_ReturnsEmptyCollectionWithMaxValueBottom() + { + // Setup + var layer = new SoilLayer2D(); + double bottom; + + // Call + IEnumerable pipingSoilLayers = PipingSoilLayerTransformer.Transform(layer, 0.0, out bottom); + + // Assert + CollectionAssert.IsEmpty(pipingSoilLayers); + Assert.AreEqual(double.MaxValue, bottom); + } + + [Test] + public void SoilLayer2DTransform_PropertiesSetWithDifferentLayerParameters_ExpectedProperties() + { + // Setup + var random = new Random(22); + double y1 = random.NextDouble(); + double y2 = y1 + random.NextDouble(); + const double x1 = 1.0; + const double x2 = 1.1; + const double x3 = 1.2; + const string materialName = "materialX"; + Color color = Color.DarkSeaGreen; + double bottom; + + bool isAquifer = random.NextBoolean(); + const int logNormalDistribution = 3; + const int logNormalShift = 0; + + double belowPhreaticLevelMean = random.NextDouble(); + double belowPhreaticLevelDeviation = random.NextDouble(); + + double diameterD70Mean = random.NextDouble(); + double diameterD70CoefficientOfVariation = random.NextDouble(); + + double permeabilityMean = random.NextDouble(); + double permeabilityCoefficientOfVariation = random.NextDouble(); + + var outerLoop = new List + { + new Segment2D(new Point2D(x1, y1), + new Point2D(x3, y1)), + new Segment2D(new Point2D(x3, y1), + new Point2D(x3, y2)), + new Segment2D(new Point2D(x3, y2), + new Point2D(x1, y2)), + new Segment2D(new Point2D(x1, y1), + new Point2D(x1, y2)) + }; + + SoilLayer2D layer = SoilLayer2DTestFactory.CreateSoilLayer2D(Enumerable.Empty(), + outerLoop); + layer.MaterialName = materialName; + layer.IsAquifer = isAquifer; + layer.Color = color; + layer.BelowPhreaticLevelDistribution = logNormalDistribution; + layer.BelowPhreaticLevelShift = logNormalShift; + layer.BelowPhreaticLevelMean = belowPhreaticLevelMean; + layer.BelowPhreaticLevelDeviation = belowPhreaticLevelDeviation; + layer.DiameterD70Distribution = logNormalDistribution; + layer.DiameterD70Shift = logNormalShift; + layer.DiameterD70Mean = diameterD70Mean; + layer.DiameterD70CoefficientOfVariation = diameterD70CoefficientOfVariation; + layer.PermeabilityDistribution = logNormalDistribution; + layer.PermeabilityShift = logNormalShift; + layer.PermeabilityMean = permeabilityMean; + layer.PermeabilityCoefficientOfVariation = permeabilityCoefficientOfVariation; + + // Call + PipingSoilLayer[] pipingSoilLayers = PipingSoilLayerTransformer.Transform( + layer, x2, out bottom).ToArray(); + + // Assert + Assert.AreEqual(1, pipingSoilLayers.Length); + Assert.AreEqual(y1, bottom, 1e-6); + PipingSoilLayer resultLayer = pipingSoilLayers.First(); + Assert.AreEqual(y2, resultLayer.Top, 1e-6); + Assert.AreEqual(isAquifer, resultLayer.IsAquifer); + Assert.AreEqual(materialName, resultLayer.MaterialName); + Assert.AreEqual(color, resultLayer.Color); + + Assert.AreEqual(belowPhreaticLevelMean, resultLayer.BelowPhreaticLevelMean); + Assert.AreEqual(belowPhreaticLevelDeviation, resultLayer.BelowPhreaticLevelDeviation); + Assert.AreEqual(diameterD70Mean, resultLayer.DiameterD70Mean); + Assert.AreEqual(diameterD70CoefficientOfVariation, resultLayer.DiameterD70CoefficientOfVariation); + Assert.AreEqual(permeabilityMean, resultLayer.PermeabilityMean); + Assert.AreEqual(permeabilityCoefficientOfVariation, resultLayer.PermeabilityCoefficientOfVariation); + } + private static IEnumerable IncorrectLogNormalDistributions() { const string testNameFormat = "Transform_Incorrect{0}{{1}}_ThrowsImportedDataTransformException";