Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/OrSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/OrSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/OrSpecification.cs (revision 1581) @@ -0,0 +1,55 @@ +// 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. + +namespace Deltares.DamEngine.Data.General.Specifications +{ + public class OrSpecification : CompositeSpecification + { + private readonly ISpecification one; + private readonly ISpecification other; + + public OrSpecification(ISpecification one, ISpecification other) + { + this.one = one; + this.other = other; + } + + /// + /// Determines whether the candidate satisfies the specification. + /// + /// The candidate to test. + /// + /// true if the candidate satisfies the specification otherwise, false. + /// + public override bool IsSatisfiedBy(TCandidate candidate) + { + bool otherIsSatisfiedBy = other.IsSatisfiedBy(candidate); + bool thisIsSatisfiedBy = one.IsSatisfiedBy(candidate); + + bool isSatisfied = (thisIsSatisfiedBy || otherIsSatisfiedBy); + if (!isSatisfied) + { + Description = "This " + one.Description + " AND " + other.Description + " are not satisfied"; + } + return isSatisfied; + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Standard/UniqueNameProvider.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/Standard/UniqueNameProvider.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Standard/UniqueNameProvider.cs (revision 1581) @@ -0,0 +1,128 @@ +// 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; +using System.Collections.Generic; + +namespace Deltares.DamEngine.Data.Standard +{ + public class UniqueNameProvider + { + private static List collections = new List(); + + static UniqueNameProvider() + { + } + + public static void Clear() + { + UniqueNameProvider.collections.Clear(); + } + + public static void Register(params IList[] lists) + { + foreach (IList list in lists) + { + if (!UniqueNameProvider.collections.Contains(list)) + UniqueNameProvider.collections.Add(list); + } + } + + public static void ProvideUniqueName(IList collection, IName item, Func translator = null) + { + if (string.IsNullOrEmpty(item.Name)) + { + Type type = collection.GetType().GetGenericArgumentsFromFirstGenericSuperClass()[0]; + item.Name = translator == null ? type.Name : translator(type.Name); + } + int indexer; + if (UniqueNameProvider.NameContainsIndexer(item.Name, out indexer)) + { + string newName = item.Name; + while (UniqueNameProvider.NameExists(collection, item, newName)) + newName = newName.Replace(string.Format("({0})", (object)indexer), string.Format("({0})", (object)++indexer)); + item.Name = newName; + } + else + { + if (!string.IsNullOrWhiteSpace(item.Name) && !UniqueNameProvider.NameExists(collection, item, item.Name)) + return; + string pattern = string.IsNullOrWhiteSpace(item.Name) ? "{0}" : item.Name + " ({0})"; + int index = 1; + while (UniqueNameProvider.NameExists(collection, item, string.Format(pattern, (object)index))) + ++index; + item.Name = string.Format(pattern, (object)index); + } + } + + private static bool NameContainsIndexer(string originalName, out int indexer) + { + indexer = -1; + if (string.IsNullOrEmpty(originalName)) + return false; + string str = originalName.TrimEnd(); + int num1 = str.LastIndexOf("(", StringComparison.Ordinal); + int num2 = str.LastIndexOf(")", StringComparison.Ordinal); + if (num1 < 0 || num2 < 0 || num1 > num2) + return false; + int startIndex = num1 + 1; + return int.TryParse(str.Substring(startIndex, num2 - startIndex), out indexer); + } + + public static string ProvideNewUniqueName(IList collection, string item, Func translator = null) + { + if (string.IsNullOrEmpty(item)) + { + Type type = collection.GetType().GetGenericArgumentsFromFirstGenericSuperClass()[0]; + item = translator == null ? type.Name : translator(type.Name); + } + if (string.IsNullOrWhiteSpace(item) || UniqueNameProvider.NameExists(collection, item)) + { + string format = string.IsNullOrWhiteSpace(item) ? "{0}" : item + " ({0})"; + int num = 0; + for (item = string.Format(format, (object)num); UniqueNameProvider.NameExists(collection, item); item = string.Format(format, (object)num)) + ++num; + } + return item; + } + + private static bool NameExists(IList list, IName excludedItem, string newName) + { + foreach (IName name in (IEnumerable)list) + { + if (name != excludedItem && name.Name == newName) + return true; + } + return false; + } + + private static bool NameExists(IList list, string excludedItem) + { + foreach (IName name in (IEnumerable)list) + { + if (name.Name == excludedItem) + return true; + } + return false; + } + } +} Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLineCreator.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLineCreator.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLineCreator.cs (revision 1581) @@ -0,0 +1,147 @@ +// 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.General; +using Deltares.DamEngine.Data.General.PlLines; +using Deltares.DamEngine.Data.General.Sensors; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + internal class SensorPlLineCreator + { + /// + /// Creates an instance of a SensorPlLineCreator for Pl1. Pl3, Pl4. + /// + /// The sensor location. + /// The sensor values. + /// + public static SensorPlLineCreator CreateInstance(SensorLocation sensorLocation, IDictionary sensorValues) + { + return CreateInstance(sensorLocation, sensorValues, new[] {PlLineType.Pl1, PlLineType.Pl3, PlLineType.Pl4,}); + } + + /// + /// Creates an instance of a SensorPlLineCreator. + /// + /// The sensor location. + /// The sensor values. + /// + /// + public static SensorPlLineCreator CreateInstance(SensorLocation sensorLocation, IDictionary sensorValues, IEnumerable lineTypes) + { + IPlLineCreator c1 = new SensorPlLine1Creator(sensorLocation, sensorValues); + IPlLineCreator c3 = new SensorPlLine3Creator(sensorLocation, sensorValues); + IPlLineCreator c4 = new SensorPlLine4Creator(sensorLocation, sensorValues); + + var tuples = new List>(); + foreach (var lineType in lineTypes) + { + switch(lineType) + { + case PlLineType.Pl1: + tuples.Add(new Tuple(lineType, c1)); + break; + case PlLineType.Pl2: + // ignore + break; + case PlLineType.Pl3: + tuples.Add(new Tuple(lineType, c3)); + break; + case PlLineType.Pl4: + tuples.Add(new Tuple(lineType, c4)); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + if (!lineTypes.Contains(PlLineType.Pl3) && sensorLocation.Pl3 == DataSourceTypeSensors.LocationData) + { + tuples.Add(new Tuple(PlLineType.Pl3, c3)); + } + + if (!lineTypes.Contains(PlLineType.Pl4) && sensorLocation.Pl4 == DataSourceTypeSensors.LocationData) + { + tuples.Add(new Tuple(PlLineType.Pl4, c4)); + } + + return new SensorPlLineCreator(tuples); + } + + private readonly IDictionary creators = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// The 'concrete' creator. + /// The type. + public SensorPlLineCreator(IPlLineCreator creator, PlLineType type) + { + this.creators.Add(type, creator); + } + + /// + /// Initializes a new instance of the class. + /// + /// The 'concrete' creators. + public SensorPlLineCreator(IEnumerable> creators) + { + foreach (var creator in creators) + { + this.creators.Add(creator.Item1, creator.Item2); + } + } + + /// + /// Creates all PL lines. + /// + /// A PlLines set + public PlLines CreateAllPlLines() + { + var lines = new PlLines(); + foreach (var item in creators) + { + IPlLineCreator creator = item.Value; + PlLine PlLine = creator.CreatePlLine(); + lines.Lines[PlLine.PlLineType] = PlLine; + } + + return lines; + } + + /// + /// Creates a PL line of the given type. + /// + /// The type of the PL line. + /// A PlLine instance + public PlLine CreatePlLine(PlLineType type) + { + if (!creators.ContainsKey(type) || creators[type] == null) + throw new NotSupportedException("Creation of type " + type + " currently not supported"); + + return creators[type].CreatePlLine(); + } + } + +} Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/DitchWaterLevelSensorSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/DitchWaterLevelSensorSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/DitchWaterLevelSensorSpecification.cs (revision 1581) @@ -0,0 +1,46 @@ +// 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 Deltares.DamEngine.Data.General.Specifications; + +namespace Deltares.DamEngine.Data.General.Sensors.Specifications +{ + public class DitchWaterLevelSensorSpecification : PredicateSpecification + { + private readonly double xDitchDikeSide; + private readonly double xDitchPolderSide; + + public DitchWaterLevelSensorSpecification(double xDitchDikeSide, double xDitchPolderSide) : + base(s => s.Type == SensorType.PolderLevel) + { + this.xDitchDikeSide = xDitchDikeSide; + this.xDitchPolderSide = xDitchPolderSide; + } + + public override bool IsSatisfiedBy(Sensor candidate) + { + if (base.IsSatisfiedBy(candidate)) + return true; + + return candidate.RelativeLocation > xDitchDikeSide && candidate.RelativeLocation < xDitchPolderSide; + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Deltares.DamEngine.Calculators.Tests.csproj =================================================================== diff -u -r1497 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Deltares.DamEngine.Calculators.Tests.csproj (.../Deltares.DamEngine.Calculators.Tests.csproj) (revision 1497) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Deltares.DamEngine.Calculators.Tests.csproj (.../Deltares.DamEngine.Calculators.Tests.csproj) (revision 1581) @@ -70,6 +70,9 @@ + + + Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/CompositeSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/CompositeSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/CompositeSpecification.cs (revision 1581) @@ -0,0 +1,117 @@ +// 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. + + +namespace Deltares.DamEngine.Data.General.Specifications +{ + public abstract class CompositeSpecification : SpecificationBase + { + #region Operator overloads + + /// + /// Implements the operator true. + /// + /// The specification. + /// + /// The result of the operator. + /// + public static bool operator true(CompositeSpecification specification) + { + return false; + } + + /// + /// Implements the operator false. + /// + /// The specification. + /// + /// The result of the operator. + /// + public static bool operator false(CompositeSpecification specification) + { + return false; + } + + /// + /// Implements the operator &. + /// + /// The left. + /// The right. + /// + /// The result of the operator. + /// + public static CompositeSpecification operator &(CompositeSpecification left, CompositeSpecification right) + { + return new AndSpecification(left, right); + } + + /// + /// Ands the specified other. + /// + /// The other. + /// + public CompositeSpecification And(ISpecification other) + { + return new AndSpecification(this, other); + } + + /// + /// Implements the operator |. + /// + /// The left. + /// The right. + /// + /// The result of the operator. + /// + public static CompositeSpecification operator |(CompositeSpecification left, CompositeSpecification right) + { + return new AndSpecification(left, right); + } + + public OrSpecification Or(ISpecification other) + { + return new OrSpecification(this, other); + } + + /// + /// Implements the operator !. + /// + /// The specification. + /// + /// The result of the operator. + /// + public static CompositeSpecification operator !(CompositeSpecification specification) + { + return new NotSpecification(specification); + } + + /// + /// Nots this instance. + /// + /// + public NotSpecification Not() + { + return new NotSpecification(this); + } + + #endregion + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine1Creator.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine1Creator.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine1Creator.cs (revision 1581) @@ -0,0 +1,375 @@ +// 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.General; +using Deltares.DamEngine.Data.General.PlLines; +using Deltares.DamEngine.Data.General.Sensors; +using Deltares.DamEngine.Data.General.Sensors.Specifications; +using Deltares.DamEngine.Data.General.Specifications.Extensions; +using Deltares.DamEngine.Data.Geometry; +using Deltares.DamEngine.Data.Geotechnics; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + internal class SensorPlLine1Creator : SensorPlLineCreatorBase + { + public SensorPlLine1Creator(SensorLocation sensorLocation, IDictionary sensorValues) + : base(sensorLocation, PlLineType.Pl1, sensorValues) + { + } + + /// + /// Creates the PL line. + /// + /// + public override PlLine CreatePlLine() + { + var lineConstructor = new PlLineConstructor(); + + double waterLevel = 0; + try + { + waterLevel = WaterLevelAtRiver; + } + catch (Exception e) + { + throw new PlLinesCreatorException("There was an error trying to read the water level", e); + } + + // Make sure there is an intersection with the surfaceline + waterLevel = this.SensorLocation.SurfaceLine.EnsureWaterLevelIsAboveRiverBottom(waterLevel).Value; + + // Add begin boundary + lineConstructor.Insert(new PlLinePoint(XBeginBoundary, waterLevel)); + + + // add all sensors to the pl line + var sortedSensors = SensorsSortedAlongProfile.ToList(); + double lastZ = waterLevel; + double lastX = XBeginBoundary; + foreach (Sensor sensor in sortedSensors) + { + var x = lastX = GetSensorXValue(sensor); + var z = lastZ = GetSensorZValue(sensor); + + var point = new PlLinePoint(x, z) { Name = sensor.Name }; + lineConstructor.Insert(point); + } + + // insert intersection point (at dike river side) + var xIntersection = IntersectionXAtRiverWaterLevel(waterLevel); + lineConstructor.Insert(new PlLinePoint(xIntersection, waterLevel) { Name = "Intersection point dike river side" }); + + // insert offset below dike top at river point? + var useLocationAsDataSource = new UseLocationAsDataSource(); + if (useLocationAsDataSource.IsSatisfiedBy(SensorLocation.Pl1PlLineOffsetBelowDikeTopAtRiver)) + { + double offset = waterLevel - SensorLocation.PlLineOffsetBelowDikeTopAtRiver; + lineConstructor.Insert(new PlLinePoint(XDikeTopAtRiver, offset)); + } + + // insert offset below dike top at polder point? + if (useLocationAsDataSource.IsSatisfiedBy(SensorLocation.Pl1PlLineOffsetBelowDikeTopAtPolder)) + { + double offset = waterLevel - SensorLocation.PlLineOffsetBelowDikeTopAtPolder; + var x = XDikeTopAtPolder; + lineConstructor.Insert(new PlLinePoint(x, offset)); + } + + // insert offset below shoulder base inside? + if (useLocationAsDataSource.IsSatisfiedBy(SensorLocation.Pl1PlLineOffsetBelowShoulderBaseInside)) + { + double offset = lastZ = ZShouldeBaseInside - SensorLocation.PlLineOffsetDryBelowShoulderBaseInside; + lineConstructor.Insert(new PlLinePoint(XShoulderBaseInside, offset)); + } + + // insert offset below dike toe at river? + if (useLocationAsDataSource.IsSatisfiedBy(SensorLocation.Pl1PlLineOffsetBelowDikeToeAtPolder)) + { + double offset = lastZ = ZDikeToeAtPolder - SensorLocation.PlLineOffsetBelowDikeToeAtPolder; + var x = XDikeToeAtPolder; + lineConstructor.Insert(new PlLinePoint(x, offset)); + + if (lastX < x) lastX = x; + } + + if (DikeHasDitch) + { + var ditchWaterLevelSensor = SensorLocation.Sensors + .GetBySpecification(new DitchWaterLevelSensorSpecification(SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchDikeSide).X, SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchPolderSide).X)) + .FirstOrDefault(); + + var polderLevel = ditchWaterLevelSensor != null ? SensorValues[ditchWaterLevelSensor] : PolderLevel; + + double? intersection = GetDitchWaterLevelIntersectionAtXDikeSide(polderLevel); + if (intersection != null) + { + lineConstructor.Insert(new PlLinePoint(intersection.Value, polderLevel)); + double? intersectionPolderSide = GetDitchWaterLevelIntersectionAtXPolderSide(polderLevel); + if (intersectionPolderSide != null) + lineConstructor.Insert(new PlLinePoint(intersectionPolderSide.Value, polderLevel)); + } + + lineConstructor.Insert(new PlLinePoint(XEndBoundary, polderLevel)); + } + else + { + double depth = lastZ; + double xLocation = XDikeToeAtPolder; + if (useLocationAsDataSource.IsSatisfiedBy(SensorLocation.Pl1PlLineOffsetBelowDikeToeAtPolder)) + { + depth = ZDikeToeAtPolder - SensorLocation.PlLineOffsetBelowDikeToeAtPolder; + + } + else + { + var sensor = PolderLevelSensor; + if (sensor != null) + { + xLocation = sensor.RelativeLocation; + depth = PolderLevel; + } + } + lineConstructor.Insert(new PlLinePoint(xLocation, depth)); + + // always continue horiontal to end + lineConstructor.Insert(new PlLinePoint(XEndBoundary, depth)); + + /* + * TODO: verify with Kin Sun + else if (lastZ <= PolderLevel) + { + lineConstructor.Insert(new PlLinePoint(XEndBoundary, lastZ)); + } + else + { + + // add tail of surface line + foreach (GeometryPoint geometryPoint in SensorLocation.SurfaceLine.GetSurfaceLineTailExcluding(lastX)) + { + lineConstructor.Insert(new PlLinePoint(geometryPoint.X, geometryPoint.Z) { Name = geometryPoint.Name }); + } + } + */ + } + + return lineConstructor.CreatePlLine(PlLineType); + } + + /// + /// Gets the Polders the level intersection at X dike side. + /// + /// The polder level. + /// + public double? GetDitchWaterLevelIntersectionAtXDikeSide(double level) + { + var ditchDikeSide = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchDikeSide); + if (ditchDikeSide == null) + throw new PlLinesCreatorException("Ditch at dike side is not defined in surface line, but was expected"); + + var ditchPolderSide = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchPolderSide); + if (ditchPolderSide == null) + throw new PlLinesCreatorException("Ditch at polder side is not defined in surface line, but was expected"); + + var ditchBottomDikeSide = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.BottomDitchDikeSide); + if (ditchBottomDikeSide == null) + throw new PlLinesCreatorException("Ditch at bottom polder side is not defined in surface line, but was expected"); + + var waterlevelLine = new Line(); + + double startX = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelOutside).X; + double endX = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelInside).X; + var waterLevelBeginPoint = new Point2D(startX, level); + var waterLevelEndPoint = new Point2D(endX, level); + waterlevelLine.SetBeginAndEndPoints(waterLevelBeginPoint, waterLevelEndPoint); + + var surfaceLineSegment = new Line(); + var beginPoint = new Point2D(ditchDikeSide.X, ditchDikeSide.Z); + var endPoint = new Point2D(ditchBottomDikeSide.X, ditchBottomDikeSide.Z); + surfaceLineSegment.SetBeginAndEndPoints(beginPoint, endPoint); + + Point2D intersectPoint = surfaceLineSegment.GetIntersectPointXz(waterlevelLine); + if (intersectPoint != null) + { + return intersectPoint.X; + } + + return null; + } + + public double? GetDitchWaterLevelIntersectionAtXPolderSide(double level) + { + var ditchDikeSide = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchDikeSide); + if (ditchDikeSide == null) + throw new PlLinesCreatorException("Ditch at dike side is not defined in surface line, but was expected"); + + var ditchPolderSide = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchPolderSide); + if (ditchPolderSide == null) + throw new PlLinesCreatorException("Ditch at polder side is not defined in surface line, but was expected"); + + var ditchBottomPolderSide = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.BottomDitchPolderSide); + if (ditchBottomPolderSide == null) + throw new PlLinesCreatorException("Ditch at bottom polder side is not defined in surface line, but was expected"); + + var waterlevelLine = new Line(); + + double startX = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelOutside).X; + double endX = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelInside).X; + var waterLevelBeginPoint = new Point2D(startX, level); + var waterLevelEndPoint = new Point2D(endX, level); + waterlevelLine.SetBeginAndEndPoints(waterLevelBeginPoint, waterLevelEndPoint); + + var surfaceLineSegment = new Line(); + var beginPoint = new Point2D(ditchPolderSide.X, ditchPolderSide.Z); + var endPoint = new Point2D(ditchBottomPolderSide.X, ditchBottomPolderSide.Z); + surfaceLineSegment.SetBeginAndEndPoints(beginPoint, endPoint); + + Point2D intersectPoint = surfaceLineSegment.GetIntersectPointXz(waterlevelLine); + if (intersectPoint != null) + { + return intersectPoint.X; + } + + return null; + } + + /// + /// Gets the water level at river. + /// + protected double WaterLevelAtRiver + { + get + { + Sensor sensor = null; + try + { + // use single, because there should be exactly one waterlevel sensor + sensor = WaterLevelSensor; + } + catch (Exception e) + { + throw new PlLinesCreatorException("There are multiple or no water level sensors defined.", e); + } + + var value = SensorLocation.GetValue(x => x.Pl1WaterLevelAtRiver, SensorValues, sensor); + if (value.HasValue) + return value.Value; + + var message = string.Format("Water level at river for the location '{0}' was not set or initialized", SensorLocation.LocationName); + throw new PlLinesCreatorException(message); + } + } + + /// + /// Gets the polder level. + /// + public double PolderLevel + { + get + { + var sensor = PolderLevelSensor; + if (sensor != null) + { + var value = SensorLocation.GetValue(x => x.Pl1WaterLevelAtPolder, SensorValues, sensor); + if (value.HasValue) + return value.Value; + + var message = string.Format("Polder level at river for the location '{0}' was not set or initialized", SensorLocation.LocationName); + throw new PlLinesCreatorException(message); + } + else + { + return SensorLocation.PolderLevel; + } + } + } + + /// + /// Gets the X dike top at river. + /// + public double XDikeTopAtRiver + { + get { return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtRiver).X; } + } + + /// + /// Gets the X dike top at polder. + /// + public double XDikeTopAtPolder + { + get { return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder).X; } + } + + /// + /// Gets the X dike toe at polder. + /// + public double XDikeToeAtPolder + { + get { return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder).X; } + } + + /// + /// Gets the X shoulder base inside. + /// + public double XShoulderBaseInside + { + get { return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.ShoulderBaseInside).X; } + } + + /// + /// Gets the Z shoulde base inside. + /// + public double ZShouldeBaseInside + { + get { return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.ShoulderBaseInside).Z; } + } + + /// + /// Gets the Z dike toe at polder. + /// + public double ZDikeToeAtPolder + { + get { return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder).Z; } + } + + /// + /// Gets the index of the insert position for a given value x in the line of points + /// + /// The line. + /// The x value. + /// + private int GetIndexOf(PlLine line, double xValue) + { + int index = 0; + foreach (var point in line.Points) + { + if (xValue > point.X) + index++; + else + break; + } + return index; + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/PlLinesCreator/PlLinesCreatorTest.cs =================================================================== diff -u -r1122 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/PlLinesCreator/PlLinesCreatorTest.cs (.../PlLinesCreatorTest.cs) (revision 1122) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/PlLinesCreator/PlLinesCreatorTest.cs (.../PlLinesCreatorTest.cs) (revision 1581) @@ -386,7 +386,7 @@ } }; { - Calculators.PlLinesCreator.PlLinesCreator plLineCreator = new Calculators.PlLinesCreator.PlLinesCreator{SurfaceLine = surfaceLine}; + Calculators.PlLinesCreator.PlLinesCreator plLineCreator = new Calculators.PlLinesCreator.PlLinesCreator { SurfaceLine = surfaceLine }; Assert.IsNotNull(plLineCreator); plLineCreator.SoilProfile = FactoryForSoilProfiles.CreateComplexProfile(); plLineCreator.WaterLevelRiverHigh = 4.0; @@ -979,7 +979,7 @@ } }; { - Calculators.PlLinesCreator.PlLinesCreator plLineCreator = new Calculators.PlLinesCreator.PlLinesCreator{SurfaceLine = surfaceLine}; + Calculators.PlLinesCreator.PlLinesCreator plLineCreator = new Calculators.PlLinesCreator.PlLinesCreator { SurfaceLine = surfaceLine }; plLineCreator.SoilProfile = FactoryForSoilProfiles.CreateClaySandProfile(); plLineCreator.SurfaceLine.AddCharacteristicPoint(new GeometryPoint(1.0, 2.0)); plLineCreator.SurfaceLine.AddCharacteristicPoint(new GeometryPoint(10.0, 2.0)); @@ -1022,7 +1022,7 @@ } }; { - Calculators.PlLinesCreator.PlLinesCreator plLineCreator = new Calculators.PlLinesCreator.PlLinesCreator{SurfaceLine = surfaceLine}; + Calculators.PlLinesCreator.PlLinesCreator plLineCreator = new Calculators.PlLinesCreator.PlLinesCreator { SurfaceLine = surfaceLine }; plLineCreator.SoilProfile = FactoryForSoilProfiles.CreateClaySandClaySandProfile(); plLineCreator.SurfaceLine.AddCharacteristicPoint(new GeometryPoint(1.0, 2.0)); plLineCreator.SurfaceLine.AddCharacteristicPoint(new GeometryPoint(10.0, 2.0)); @@ -1055,7 +1055,7 @@ } }; { - Calculators.PlLinesCreator.PlLinesCreator plLineCreator = new Calculators.PlLinesCreator.PlLinesCreator{SurfaceLine = surfaceLine}; + Calculators.PlLinesCreator.PlLinesCreator plLineCreator = new Calculators.PlLinesCreator.PlLinesCreator { SurfaceLine = surfaceLine }; plLineCreator.SoilProfile = FactoryForSoilProfiles.CreateClaySandClaySandProfile(); plLineCreator.SurfaceLine.AddCharacteristicPoint(new GeometryPoint(1.0, 2.0)); plLineCreator.SurfaceLine.AddCharacteristicPoint(new GeometryPoint(10.0, 2.0)); @@ -1663,7 +1663,7 @@ { location }; - var gauges = new List + var gauges = new List { new Gauge { Name = "G1", Location = locations[0], LocalX = 3.2 }, new Gauge { Name = "G2", Location = locations[0], LocalX = 12.4 } @@ -1703,7 +1703,7 @@ { location }; - var gauges = new List + var gauges = new List { new Gauge { Name = "G1", Location = locations[0], LocalX = 15, Value = 4 }, new Gauge { Name = "G2", Location = locations[0], LocalX = 45, Value = 1 } @@ -1764,7 +1764,7 @@ { location }; - var gauges = new List + var gauges = new List { new Gauge { Name = "G1", Location = locations[0], LocalX = 15, Value = 4 }, new Gauge { Name = "G2", Location = locations[0], LocalX = 20, Value = 2 }, @@ -1826,7 +1826,7 @@ { location }; - var gauges = new List + var gauges = new List { new Gauge { Name = "G1", Location = locations[0], LocalX = 15, Value = 6 }, new Gauge { Name = "G2", Location = locations[0], LocalX = 20, Value = 2 }, Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Properties/AssemblyInfo.cs =================================================================== diff -u -r1578 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Data/Properties/AssemblyInfo.cs (.../AssemblyInfo.cs) (revision 1578) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Properties/AssemblyInfo.cs (.../AssemblyInfo.cs) (revision 1581) @@ -30,3 +30,4 @@ [assembly: InternalsVisibleTo("Deltares.DamEngine.Interface")] [assembly: InternalsVisibleTo("Deltares.DamEngine.Interface.Tests")] +[assembly: InternalsVisibleTo("Deltares.DamEngine.Calculators.Tests")] Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/SpecificationBaseAttribute.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/SpecificationBaseAttribute.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/SpecificationBaseAttribute.cs (revision 1581) @@ -0,0 +1,93 @@ +// 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 Deltares.DamEngine.Data.Standard; + +namespace Deltares.DamEngine.Data.General.Specifications +{ + /// + /// Defines the abstract base class for the specification attributes + /// to derive more specific types from + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Property, Inherited = true, AllowMultiple = true)] + public abstract class SpecificationBaseAttribute : Attribute + { + private readonly ISpecification specification; + + protected SpecificationBaseAttribute(Type specification, params object[] args) + { + if (args == null && !specification.HasDefaultConstructor()) + throw new ArgumentException("The specification type should have a default constructor or supply "); + + if (!typeof(ISpecification).IsAssignableFrom(specification)) + throw new ArgumentException("The specification type should implement interface ISpecification"); + + this.specification = Activator.CreateInstance(specification, args) as ISpecification; + } + + protected SpecificationBaseAttribute(Type specification) : this(specification, null) + { + } + + /// + /// Gets or sets the name of the specification. + /// + /// If this property is not set, the name of the specification (when available) will be used + /// + /// The name. + /// + public string Name { get; set; } + + /// + /// Gets or sets the text in case the specification is not satisfied + /// + /// If this property is not set, the description of the specification (when available) will be used + /// + /// The not satisfied text. + /// + public string NotSatisfiedText { get; set; } + + + public string GroupName { get; set; } + + + // This is a positional argument + + public ISpecification Specification + { + get { return this.specification; } + } + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + public sealed class SpecificationAttribute : SpecificationBaseAttribute + { + public SpecificationAttribute(Type specification, params object[] args) + : base(specification, args) + { + } + + public SpecificationAttribute(Type specification) : base(specification) + { + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/WaterLevelSensorSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/WaterLevelSensorSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/WaterLevelSensorSpecification.cs (revision 1581) @@ -0,0 +1,34 @@ +// 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 Deltares.DamEngine.Data.General.Specifications; + +namespace Deltares.DamEngine.Data.General.Sensors.Specifications +{ + public class WaterLevelSensorSpecification : PredicateSpecification + { + public WaterLevelSensorSpecification() : + base(s => s.Type == SensorType.WaterLevel) + { + } + } + +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/PlLinesCreator.cs =================================================================== diff -u -r1289 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/PlLinesCreator.cs (.../PlLinesCreator.cs) (revision 1289) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/PlLinesCreator.cs (.../PlLinesCreator.cs) (revision 1581) @@ -22,7 +22,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Serialization; using Deltares.DamEngine.Calculators.General; using Deltares.DamEngine.Calculators.Uplift; using Deltares.DamEngine.Data.General; @@ -258,7 +257,7 @@ /// private PlLine CreatePlLine2ByExpertKnowledge(double penetrationLength, double? headInPlLine2) { - PlLine plLine = null; + PlLine PlLine = null; if (penetrationLength < 0.0) { @@ -267,7 +266,7 @@ if ((penetrationLength.AlmostEquals(0.0)) || (headInPlLine2 == null)) { // No penetration, or no Head Pl2 defined, so empty pl-line will be returned - plLine = new PlLine(); + PlLine = new PlLine(); } else { @@ -278,16 +277,16 @@ switch (SoilProfileType) { case SoilProfileType.ProfileType1D: - plLine = CreatePlLine2ByExpertKnowledgeFor1DGeometry(penetrationLength, headInPlLine2); + PlLine = CreatePlLine2ByExpertKnowledgeFor1DGeometry(penetrationLength, headInPlLine2); break; case SoilProfileType.ProfileType2D: case SoilProfileType.ProfileTypeStiFile: - plLine = CreatePlLine2ByExpertKnowledgeFor2DGeometry(penetrationLength, headInPlLine2); + PlLine = CreatePlLine2ByExpertKnowledgeFor2DGeometry(penetrationLength, headInPlLine2); break; } } - return plLine; + return PlLine; } /// @@ -303,10 +302,10 @@ { throw new PlLinesCreatorException("Head PL2 not defined"); } - PlLine plLine = new PlLine(); - plLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.First().X, headInPlLine2.Value)); - plLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, headInPlLine2.Value)); - return plLine; + PlLine PlLine = new PlLine(); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.First().X, headInPlLine2.Value)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, headInPlLine2.Value)); + return PlLine; } /// @@ -321,7 +320,7 @@ { throw new PlLinesCreatorException("Head PL2 not defined"); } - PlLine plLine = new PlLine(); + PlLine PlLine = new PlLine(); if (SoilProfile != null) { IList aquiferLayers = this.SoilProfile.GetAquiferLayers(); @@ -343,8 +342,8 @@ if (separationLevel <= infiltrationLayers.First().TopLevel) { - plLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.First().X, headInPlLine2.Value)); - plLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, headInPlLine2.Value)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.First().X, headInPlLine2.Value)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, headInPlLine2.Value)); SoilLayer1D separationLayer = (from SoilLayer1D layer in infiltrationLayers where layer.TopLevel >= separationLevel @@ -365,7 +364,7 @@ this.SoilProfile.DetermineInfiltrationLayer(penetrationLength); } } - return plLine; + return PlLine; } /// @@ -408,12 +407,12 @@ return CreatePlLine3Or4ByExpertKnowledge(headValue, dampingFactor, PlLineType.Pl4, slopeGradient); } - private PlLine CreatePlLine3Or4ByExpertKnowledge(double headValue, double dampingFactor, PlLineType plLineType, double slopeGradient) + private PlLine CreatePlLine3Or4ByExpertKnowledge(double headValue, double dampingFactor, PlLineType PlLineType, double slopeGradient) { // Offset to solve issue MWDAM-557 const double offset = 0.01; - PlLine plLine = new PlLine(); + PlLine PlLine = new PlLine(); ThrowIfInsufficientSoilGeometryData(); ThrowIfNoSurfaceLine(); @@ -432,51 +431,51 @@ throw new PlLinesCreatorException("Damping factor < 0.0"); } - var relevantAquiferLayer = GetRelevantAquiferLayer(plLineType, actualSoilProfile); + var relevantAquiferLayer = GetRelevantAquiferLayer(PlLineType, actualSoilProfile); if (relevantAquiferLayer != null) { double referenceLevel = (this.HeadInPlLine2 != null) ? this.HeadInPlLine2.Value : this.waterLevelPolder; double headAtPolderDikeToe = headValue - Math.Max(0, dampingFactor * (headValue - referenceLevel)); - plLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.First().X, headValue)); - plLine.Points.Add(new PlLinePoint(this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver).X, headValue)); - plLine.Points.Add(new PlLinePoint(this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder).X, headAtPolderDikeToe)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.First().X, headValue)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver).X, headValue)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder).X, headAtPolderDikeToe)); - // Now continue PLline to the end with a slope of slopegradient - AddTailOfPl3OrPl4WithSlopeGradient(slopeGradient, plLine); + // Now continue PlLine to the end with a slope of slopegradient + AddTailOfPl3OrPl4WithSlopeGradient(slopeGradient, PlLine); if (isAdjustPl3AndPl4SoNoUpliftWillOccurEnabled) { - AdjustLineAccordingToTRWUplift(plLine, plLineType, slopeGradient); + AdjustLineAccordingToTRWUplift(PlLine, PlLineType, slopeGradient); } - EnsureDescendingLine(plLine); + EnsureDescendingLine(PlLine); - RemoveRedundantPoints(plLine); + RemoveRedundantPoints(PlLine); } - return plLine; + return PlLine; } /// - /// Continue PLline to the end with a slope of slopegradient + /// Continue PlLine to the end with a slope of slopegradient /// If PlLine is lower then polderlevel, continue with polderlevel /// /// The slope gradient. - /// The pl line. - private void AddTailOfPl3OrPl4WithSlopeGradient(double slopeGradient, PlLine plLine) + /// The pl line. + private void AddTailOfPl3OrPl4WithSlopeGradient(double slopeGradient, PlLine PlLine) { - if (plLine.Points.Last().Z <= this.WaterLevelPolder) + if (PlLine.Points.Last().Z <= this.WaterLevelPolder) { // the PL line is already at WaterLevelPolder, so countinue it horizontally - plLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, this.WaterLevelPolder)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, this.WaterLevelPolder)); } else { - double lengthFromLastPlPointToEnd = Math.Abs(this.surfaceLine.Geometry.Points.Last().X - plLine.Points.Last().X); - double headAtLastPlPoint = plLine.Points.Last().Z; - double headAtEnd = plLine.Points.Last().Z - slopeGradient * lengthFromLastPlPointToEnd; + double lengthFromLastPlPointToEnd = Math.Abs(this.surfaceLine.Geometry.Points.Last().X - PlLine.Points.Last().X); + double headAtLastPlPoint = PlLine.Points.Last().Z; + double headAtEnd = PlLine.Points.Last().Z - slopeGradient * lengthFromLastPlPointToEnd; Line waterLevelPolderLine = new Line(new Point2D(this.surfaceLine.Geometry.Points.First().X, this.WaterLevelPolder), @@ -489,12 +488,12 @@ if (waterLevelPolderLine.IntersectsZ(slopeLine, out intersectionPoint)) { - plLine.Points.Add(new PlLinePoint(intersectionPoint.X, this.WaterLevelPolder)); - plLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, this.WaterLevelPolder)); + PlLine.Points.Add(new PlLinePoint(intersectionPoint.X, this.WaterLevelPolder)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, this.WaterLevelPolder)); } else { - plLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, headAtEnd)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, headAtEnd)); } } @@ -535,30 +534,30 @@ /// Remove redundant points (i.e. lying on a line between its both neighbours) /// /// - /// - private static void RemoveRedundantPoints(PlLine plLine) + /// + private static void RemoveRedundantPoints(PlLine PlLine) { const double Tolerance = 0.001; - for (int pointIndex = plLine.Points.Count - 2; pointIndex > 0; pointIndex--) + for (int pointIndex = PlLine.Points.Count - 2; pointIndex > 0; pointIndex--) { - PlLinePoint plPointPrev = plLine.Points[pointIndex - 1]; - PlLinePoint plPoint = plLine.Points[pointIndex]; - PlLinePoint plPointNext = plLine.Points[pointIndex + 1]; + PlLinePoint plPointPrev = PlLine.Points[pointIndex - 1]; + PlLinePoint plPoint = PlLine.Points[pointIndex]; + PlLinePoint plPointNext = PlLine.Points[pointIndex + 1]; if (Math.Abs((plPoint.Z - plPointPrev.Z) / (plPoint.X - plPointPrev.X) - (plPointNext.Z - plPoint.Z) / (plPointNext.X - plPoint.X)) < Tolerance) { - plLine.Points.Remove(plPoint); + PlLine.Points.Remove(plPoint); } } } /// /// All points should have descending z from dike to polder /// - /// - private static void EnsureDescendingLine(PlLine plLine) + /// + private static void EnsureDescendingLine(PlLine PlLine) { PlLinePoint plPointPrevious = null; - foreach (PlLinePoint plPoint in plLine.Points) + foreach (PlLinePoint plPoint in PlLine.Points) { if (plPointPrevious != null && plPoint.Z > plPointPrevious.Z) plPoint.Z = plPointPrevious.Z; @@ -569,10 +568,10 @@ /// /// Clear outputvalues for PL3 or PL4 /// - /// - private void ClearOutputValuesForPl3_4(PlLineType plLineType) + /// + private void ClearOutputValuesForPl3_4(PlLineType PlLineType) { - switch (plLineType) + switch (PlLineType) { case PlLineType.Pl3: pl3MinUplift = Double.MaxValue; @@ -590,13 +589,13 @@ /// /// Determine outputvalues for PL3 or PL4 for minimal upliftfactor /// - /// Type of the pl line. + /// Type of the pl line. /// The x coordinate. /// The uplift factor. /// The head value. - private void UpdateOutputValuesForPl3_4(PlLineType plLineType, double xCoordinate, double upliftFactor, double headValue) + private void UpdateOutputValuesForPl3_4(PlLineType PlLineType, double xCoordinate, double upliftFactor, double headValue) { - switch (plLineType) + switch (PlLineType) { case PlLineType.Pl3: if (upliftFactor < pl3MinUplift) @@ -621,14 +620,14 @@ /// /// Finds the index of point with X coordinate. /// - /// The pl line. + /// The pl line. /// The x coordinate. /// - private int FindIndexOfPointInPlLineWithXCoordinate(PlLine plLine, double x) + private int FindIndexOfPointInPlLineWithXCoordinate(PlLine PlLine, double x) { - for (int pointIndex = plLine.Points.Count - 1; pointIndex > 0; pointIndex--) + for (int pointIndex = PlLine.Points.Count - 1; pointIndex > 0; pointIndex--) { - PlLinePoint plPoint = plLine.Points[pointIndex]; + PlLinePoint plPoint = PlLine.Points[pointIndex]; if (plPoint.X.AlmostEquals(x, GeometryPoint.Precision)) { return pointIndex; @@ -641,13 +640,13 @@ /// /// Removes the index of all points of pl line after the start index. /// - /// The pl line. + /// The pl line. /// The start index (this point will not be removed). - private void RemoveAllPointsOfPlLineAfterIndex(PlLine plLine, int startIndex) + private void RemoveAllPointsOfPlLineAfterIndex(PlLine PlLine, int startIndex) { - for (int pointIndex = plLine.Points.Count - 1; pointIndex > startIndex; pointIndex--) + for (int pointIndex = PlLine.Points.Count - 1; pointIndex > startIndex; pointIndex--) { - plLine.Points.RemoveAt(pointIndex); + PlLine.Points.RemoveAt(pointIndex); } } @@ -702,7 +701,7 @@ /// Make sure PL3 is always descending from left to right. /// /// A better implementation (not implemented yet) would be: - /// Correct plline 3 or 4 for uplift according to + /// Correct PlLine 3 or 4 for uplift according to /// TRW (Technisch Rapport Waterspanningen bij dijken) par. b1.3.4 "Stijghoogte in het eerste watervoerende pakket" /// - Adjust PL3/4 for all surface points from end of profile to toe of dike, so no uplift will occur in that surface point /// - From the point, closest to the dike, (firstAdjustedPLPoint) where this correction has been made the following has to be done @@ -713,13 +712,13 @@ /// \ /// \__________ /// - /// - /// + /// + /// /// - private void AdjustLineAccordingToTRWUplift(PlLine plLine, PlLineType plLineType, double slopeGradient) + private void AdjustLineAccordingToTRWUplift(PlLine PlLine, PlLineType PlLineType, double slopeGradient) { const double cTolerancePoint = 0.0001; - ClearOutputValuesForPl3_4(plLineType); + ClearOutputValuesForPl3_4(PlLineType); var upliftCalculator = new UpliftCalculator { @@ -728,7 +727,7 @@ }; GeometryPoint startSurfacePoint = this.surfaceLine.GetDikeToeInward(); - int indexOfFixedPlPoint = FindIndexOfPointInPlLineWithXCoordinate(plLine, this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver).X); + int indexOfFixedPlPoint = FindIndexOfPointInPlLineWithXCoordinate(PlLine, this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver).X); if (indexOfFixedPlPoint < 0) { throw new PlLinesCreatorException("Could not find fixed point for PlLine"); @@ -754,14 +753,14 @@ { continue; } - ConfigureUpliftCalculator(plLineType, upliftCalculator, surfacePoint); - SoilLayer1D relevantSandLayer = GetRelevantAquiferLayer(plLineType, upliftCalculator.SoilProfile); - // When plLineType is PL4 it is possible that no relevant aquifer layer is found, then the adjustment is not necessary + ConfigureUpliftCalculator(PlLineType, upliftCalculator, surfacePoint); + SoilLayer1D relevantSandLayer = GetRelevantAquiferLayer(PlLineType, upliftCalculator.SoilProfile); + // When PlLineType is PL4 it is possible that no relevant aquifer layer is found, then the adjustment is not necessary if (relevantSandLayer != null) { double aquiferTopLayer = Math.Min(surfacePoint.Z, relevantSandLayer.TopLevel); upliftCalculator.TopOfLayerToBeEvaluated = aquiferTopLayer; - double headOfPlLine = plLine.ZFromX(surfacePoint.X); + double headOfPlLine = PlLine.ZFromX(surfacePoint.X); double upliftFactor = upliftCalculator.CalculateUpliftFactor(headOfPlLine); if (upliftFactor <= cUpliftFactorEquilibrium) { @@ -780,18 +779,18 @@ { var plPoint = new PlLinePoint(surfacePoint.X, headOfPlLine); // Remove all points of PlLine after fixed point - RemoveAllPointsOfPlLineAfterIndex(plLine, indexOfFixedPlPoint); - plLine.Points.Add(plPoint); + RemoveAllPointsOfPlLineAfterIndex(PlLine, indexOfFixedPlPoint); + PlLine.Points.Add(plPoint); if (!isRemoveAllPlPointsBackToDikeToeAtRiver) { // To make sure that not all points of the PL-line back to the toe of the dike at riverside are to be removed - indexOfFixedPlPoint = plLine.Points.Count - 1; + indexOfFixedPlPoint = PlLine.Points.Count - 1; } - AddTailOfPl3OrPl4WithSlopeGradient(slopeGradient, plLine); + AddTailOfPl3OrPl4WithSlopeGradient(slopeGradient, PlLine); lastAdjustedHeadOfPlLine = headOfPlLine; } } - UpdateOutputValuesForPl3_4(plLineType, surfacePoint.X, upliftFactor, headOfPlLine); + UpdateOutputValuesForPl3_4(PlLineType, surfacePoint.X, upliftFactor, headOfPlLine); } } @@ -807,27 +806,27 @@ foreach (GeometryPoint surfacePoint in recheckSurfacePointsList) { - ConfigureUpliftCalculator(plLineType, upliftCalculator, surfacePoint); - SoilLayer1D relevantSandLayer = GetRelevantAquiferLayer(plLineType, upliftCalculator.SoilProfile); - // When plLineType is PL4 it is possible that no relevant aquifer layer is found, then the adjustment is not necessary + ConfigureUpliftCalculator(PlLineType, upliftCalculator, surfacePoint); + SoilLayer1D relevantSandLayer = GetRelevantAquiferLayer(PlLineType, upliftCalculator.SoilProfile); + // When PlLineType is PL4 it is possible that no relevant aquifer layer is found, then the adjustment is not necessary if (relevantSandLayer != null) { double aquiferTopLayer = Math.Min(surfacePoint.Z, relevantSandLayer.TopLevel); upliftCalculator.TopOfLayerToBeEvaluated = aquiferTopLayer; - double headOfPlLine = plLine.ZFromX(surfacePoint.X); + double headOfPlLine = PlLine.ZFromX(surfacePoint.X); double upliftFactor = upliftCalculator.CalculateUpliftFactor(headOfPlLine); if (upliftFactor <= cUpliftFactorEquilibrium) { // Adjust headOfPlLine so there is equilibrium headOfPlLine = Math.Max(upliftCalculator.CalculateHeadOfPlLine(cUpliftFactorEquilibrium), this.waterLevelPolder); - double currentHead = plLine.ZFromX(surfacePoint.X); + double currentHead = PlLine.ZFromX(surfacePoint.X); if (headOfPlLine < currentHead) { - PlLinePoint plPoint = plLine.EnsurePointAtX(surfacePoint.X, cTolerancePoint); + PlLinePoint plPoint = PlLine.EnsurePointAtX(surfacePoint.X, cTolerancePoint); plPoint.Z = headOfPlLine; } } - UpdateOutputValuesForPl3_4(plLineType, surfacePoint.X, upliftFactor, headOfPlLine); + UpdateOutputValuesForPl3_4(PlLineType, surfacePoint.X, upliftFactor, headOfPlLine); } } } @@ -836,10 +835,10 @@ /// /// Configures the uplift calculator. /// - /// Type of the pl line. + /// Type of the pl line. /// The uplift calculator. /// The surface point. - private void ConfigureUpliftCalculator(PlLineType plLineType, UpliftCalculator upliftCalculator, GeometryPoint surfacePoint) + private void ConfigureUpliftCalculator(PlLineType PlLineType, UpliftCalculator upliftCalculator, GeometryPoint surfacePoint) { // Offset to solve issue MWDAM-764 const double offset = 0.01; @@ -919,14 +918,14 @@ /// /// /// - /// + /// /// - void CopyPointsInPlLine(ref PlLine plLine, GeometryPointString genericLine) + void CopyPointsInPlLine(ref PlLine PlLine, GeometryPointString genericLine) { - plLine.Points.Clear(); + PlLine.Points.Clear(); foreach (GeometryPoint point in genericLine.Points) { - plLine.Points.Add(new PlLinePoint(point.X, point.Z)); + PlLine.Points.Add(new PlLinePoint(point.X, point.Z)); } } @@ -937,41 +936,41 @@ /// private PlLines CreateAllPlLinesWithExpertKnowledge(Location location) { - PlLines plLines = new PlLines(); - foreach (PlLineType plLineType in Enum.GetValues(typeof(PlLineType))) + PlLines PlLines = new PlLines(); + foreach (PlLineType PlLineType in Enum.GetValues(typeof(PlLineType))) { bool isPL1LineDefinedForLocation = (location != null) && (location.LocalXzpl1Line != null) && (location.LocalXzpl1Line.Points.Count > 1); - if ((plLineType == PlLineType.Pl1) && isPL1LineDefinedForLocation) + if ((PlLineType == PlLineType.Pl1) && isPL1LineDefinedForLocation) { - PlLine plLine = plLines.Lines[plLineType]; - CopyPointsInPlLine(ref plLine, location.LocalXzpl1Line); + PlLine PlLine = PlLines.Lines[PlLineType]; + CopyPointsInPlLine(ref PlLine, location.LocalXzpl1Line); } else { - plLines.Lines[plLineType] = CreatePlLineByExpertKnowledge(plLineType, location.DamType, location.SlopeDampingPiezometricHeightPolderSide); + PlLines.Lines[PlLineType] = CreatePlLineByExpertKnowledge(PlLineType, location.DamType, location.SlopeDampingPiezometricHeightPolderSide); } // currentPL1Line is needed when calculating uplift reduction for PL3 and Pl4 - if (plLineType == PlLineType.Pl1) + if (PlLineType == PlLineType.Pl1) { - // var plLine = plLines.Lines[plLineType]; - // AdaptPL1ForNonWaterRetainingObject(ref plLine); - // plLines.Lines[plLineType] = plLine; - currentPL1Line = plLines.Lines[plLineType]; + // var PlLine = PlLines.Lines[PlLineType]; + // AdaptPL1ForNonWaterRetainingObject(ref PlLine); + // PlLines.Lines[PlLineType] = PlLine; + currentPL1Line = PlLines.Lines[PlLineType]; } } - return plLines; + return PlLines; } -// private IEnumerable FindAllPlLinePointsAtNWOLocation(PLLine pl1Line) +// private IEnumerable FindAllPlLinePointsAtNWOLocation(PlLine pl1Line) // { -// IEnumerable results = pl1Line.GetPointSegmentBetween(this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.NonWaterRetainingObjectPoint1).X - cOffsetPhreaticLineBelowSurface, +// IEnumerable results = pl1Line.GetPointSegmentBetween(this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.NonWaterRetainingObjectPoint1).X - cOffsetPhreaticLineBelowSurface, // this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.NonWaterRetainingObjectPoint4).X + cOffsetPhreaticLineBelowSurface); // // return results; // } -// private void AdaptPL1ForNonWaterRetainingObject(ref PLLine pl1Line) +// private void AdaptPL1ForNonWaterRetainingObject(ref PlLine pl1Line) // { // // check if nwo points are available as CharacteristicPoints // var nwo1 = this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.NonWaterRetainingObjectPoint1); @@ -982,52 +981,52 @@ // { // // // Find all points in the Pl line that (might) coincide with the NWO -// IEnumerable plPointsToBeMoved = FindAllPlLinePointsAtNWOLocation(pl1Line); +// IEnumerable plPointsToBeMoved = FindAllPlLinePointsAtNWOLocation(pl1Line); // -// PLLinePoint nwo1Pl = null; -// PLLinePoint nwo2Pl = null; -// PLLinePoint nwo3Pl = null; -// PLLinePoint nwo4Pl = null; +// PlLinePoint nwo1Pl = null; +// PlLinePoint nwo2Pl = null; +// PlLinePoint nwo3Pl = null; +// PlLinePoint nwo4Pl = null; // // // For NWO point, determine whether a pl point has to be added -// if (pl1Line.PositionXzOfPointRelatedToPLLine(nwo1) != PLLinePointPositionXzType.AbovePLLine) +// if (pl1Line.PositionXzOfPointRelatedToPlLine(nwo1) != PlLinePointPositionXzType.AbovePlLine) // { -// nwo1Pl = new PLLinePoint(nwo1.X, nwo1.Z - cOffsetPhreaticLineBelowSurface); +// nwo1Pl = new PlLinePoint(nwo1.X, nwo1.Z - cOffsetPhreaticLineBelowSurface); // } -// if (pl1Line.PositionXzOfPointRelatedToPLLine(nwo2) != PLLinePointPositionXzType.AbovePLLine) +// if (pl1Line.PositionXzOfPointRelatedToPlLine(nwo2) != PlLinePointPositionXzType.AbovePlLine) // { -// nwo2Pl = new PLLinePoint(nwo2.X, nwo2.Z - cOffsetPhreaticLineBelowSurface); +// nwo2Pl = new PlLinePoint(nwo2.X, nwo2.Z - cOffsetPhreaticLineBelowSurface); // } -// if (pl1Line.PositionXzOfPointRelatedToPLLine(nwo3) != PLLinePointPositionXzType.AbovePLLine) +// if (pl1Line.PositionXzOfPointRelatedToPlLine(nwo3) != PlLinePointPositionXzType.AbovePlLine) // { -// nwo3Pl = new PLLinePoint(nwo3.X, nwo3.Z - cOffsetPhreaticLineBelowSurface); +// nwo3Pl = new PlLinePoint(nwo3.X, nwo3.Z - cOffsetPhreaticLineBelowSurface); // } -// if (pl1Line.PositionXzOfPointRelatedToPLLine(nwo4) != PLLinePointPositionXzType.AbovePLLine) +// if (pl1Line.PositionXzOfPointRelatedToPlLine(nwo4) != PlLinePointPositionXzType.AbovePlLine) // { -// nwo4Pl = new PLLinePoint(nwo4.X, nwo4.Z - cOffsetPhreaticLineBelowSurface); +// nwo4Pl = new PlLinePoint(nwo4.X, nwo4.Z - cOffsetPhreaticLineBelowSurface); // } // // // Find the intersections of pl line and NWO and handle these -// // Intersection between nwo point1 and nwo point2 only when nwo point1 is above pl line and nwo point2 is below plLine -// PLLinePoint intersection1 = null; +// // Intersection between nwo point1 and nwo point2 only when nwo point1 is above pl line and nwo point2 is below PlLine +// PlLinePoint intersection1 = null; // if ((nwo1Pl == null) && (nwo2Pl != null)) // { // var lineNWO = new Deltares.Geometry.Line { BeginPoint = new GeometryPoint(nwo1.X, 0, nwo1.Z), EndPoint = new GeometryPoint(nwo2.X, 0, nwo2.Z) }; // var ips = pl1Line.IntersectionPointsXzWithLineXz(lineNWO); // if (ips.Count > 0) // { -// intersection1 = new PLLinePoint(ips.First().X, ips.First().Z); +// intersection1 = new PlLinePoint(ips.First().X, ips.First().Z); // } // } -// // Intersection between nwo point3 and nwo point4 only when nwo point3 is below pl line and nwo point4 is above plLine -// PLLinePoint intersection2 = null; +// // Intersection between nwo point3 and nwo point4 only when nwo point3 is below pl line and nwo point4 is above PlLine +// PlLinePoint intersection2 = null; // if ((nwo3Pl != null) && (nwo4Pl == null)) // { // var lineNWO = new Deltares.Geometry.Line { BeginPoint = new GeometryPoint(nwo3.X, 0, nwo3.Z), EndPoint = new GeometryPoint(nwo4.X, 0, nwo4.Z) }; // var ips = pl1Line.IntersectionPointsXzWithLineXz(lineNWO); // if (ips.Count > 0) // { -// intersection2 = new PLLinePoint(ips.Last().X, ips.Last().Z); +// intersection2 = new PlLinePoint(ips.Last().X, ips.Last().Z); // } // } // @@ -1083,17 +1082,17 @@ // } // // // Move all the points in the pl line itself that need to be moved to the horizontal proper level. -// foreach (var plLinePoint in plPointsToBeMoved) +// foreach (var PlLinePoint in plPointsToBeMoved) // { -// plLinePoint.Z = requiredWaterLevel; +// PlLinePoint.Z = requiredWaterLevel; // } // pl1Line.DeleteCoinsidingPoints(GeometryPoint.Precision); // } // } // } // } -// private void MakeWaterLevelHorizontalInNWOAtRiverSideUsingInterSection2And1(GeometryPoint nwo1, GeometryPoint nwo2, GeometryPoint nwo3, PLLine pl1Line, PLLinePoint nwo2Pl, PLLinePoint intersection1, PLLinePoint intersection2) +// private void MakeWaterLevelHorizontalInNWOAtRiverSideUsingInterSection2And1(GeometryPoint nwo1, GeometryPoint nwo2, GeometryPoint nwo3, PlLine pl1Line, PlLinePoint nwo2Pl, PlLinePoint intersection1, PlLinePoint intersection2) // { // var lineNWO = new Line { BeginPoint = new Point2D(nwo1.X, nwo1.Z), EndPoint = new Point2D(nwo2.X, nwo2.Z) }; // var linePL = new Line { BeginPoint = new Point2D(nwo1.X, intersection2.Z), EndPoint = new Point2D(intersection2.X, intersection2.Z) }; @@ -1127,12 +1126,12 @@ // } // else // { -// throw new PLLinesCreatorException("Could not create the intersectionsection points between NWO and Phreatic line to create horizontal level."); +// throw new PlLinesCreatorException("Could not create the intersectionsection points between NWO and Phreatic line to create horizontal level."); // } // } // } -// private void MakeWaterLevelHorizontalInNWOAtRiverSideUsingInterSection1And2(GeometryPoint nwo2, GeometryPoint nwo3, GeometryPoint nwo4, PLLine pl1Line, PLLinePoint nwo3Pl, PLLinePoint intersection1, PLLinePoint intersection2) +// private void MakeWaterLevelHorizontalInNWOAtRiverSideUsingInterSection1And2(GeometryPoint nwo2, GeometryPoint nwo3, GeometryPoint nwo4, PlLine pl1Line, PlLinePoint nwo3Pl, PlLinePoint intersection1, PlLinePoint intersection2) // { // var lineNWO = new Deltares.Geometry.Line { BeginPoint = new GeometryPoint(nwo3.X, 0, nwo3.Z), EndPoint = new GeometryPoint(nwo4.X, 0, nwo4.Z) }; // var linePL = new Deltares.Geometry.Line { BeginPoint = new GeometryPoint(intersection1.X, 0, intersection1.Z), EndPoint = new GeometryPoint(nwo4.X, 0, intersection1.Z) }; @@ -1165,18 +1164,18 @@ // } // else // { -// throw new PLLinesCreatorException("Could not create the intersectionsection points between NWO and Phreatic line to create horizontal level."); +// throw new PlLinesCreatorException("Could not create the intersectionsection points between NWO and Phreatic line to create horizontal level."); // } // } // } -// private void RemoveAllWaterFromNonWaterRetainingObject(GeometryPoint nwo1, PLLine pl1Line, PLLinePoint nwo1Pl, PLLinePoint nwo2Pl, PLLinePoint nwo3Pl, PLLinePoint nwo4Pl, PLLinePoint intersection1, PLLinePoint intersection2, IEnumerable plPointsToBeMoved) +// private void RemoveAllWaterFromNonWaterRetainingObject(GeometryPoint nwo1, PlLine pl1Line, PlLinePoint nwo1Pl, PlLinePoint nwo2Pl, PlLinePoint nwo3Pl, PlLinePoint nwo4Pl, PlLinePoint intersection1, PlLinePoint intersection2, IEnumerable plPointsToBeMoved) // { // if ((nwo1.X >= this.surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder).X) || // ((intersection1 != null) && (intersection2 != null))) // { // // Move all the points in the pl line itself that need to be moved to below the surfaceline. -// MoveSelectedPLLinePointsBelowSurfaceLine(plPointsToBeMoved); +// MoveSelectedPlLinePointsBelowSurfaceLine(plPointsToBeMoved); // // // now add all extra points to the pl line // if (nwo1Pl != null) @@ -1214,7 +1213,7 @@ // } // } -// private void MakeWaterLevelHorizontalInNWOAtRiverSideUsingInterSection2(GeometryPoint nwo1, GeometryPoint nwo2, GeometryPoint nwo3, PLLine pl1Line, PLLinePoint nwo1Pl, PLLinePoint nwo2Pl, PLLinePoint nwo3Pl, PLLinePoint intersection2) +// private void MakeWaterLevelHorizontalInNWOAtRiverSideUsingInterSection2(GeometryPoint nwo1, GeometryPoint nwo2, GeometryPoint nwo3, PlLine pl1Line, PlLinePoint nwo1Pl, PlLinePoint nwo2Pl, PlLinePoint nwo3Pl, PlLinePoint intersection2) // { // var lineNWO = new Deltares.Geometry.Line { BeginPoint = new GeometryPoint(nwo1.X, 0, nwo1.Z), EndPoint = new GeometryPoint(nwo2.X, 0, nwo2.Z) }; // var linePL = new Deltares.Geometry.Line { BeginPoint = new GeometryPoint(nwo1.X, 0, intersection2.Z), EndPoint = new GeometryPoint(intersection2.X, 0, intersection2.Z) }; @@ -1253,12 +1252,12 @@ // } // else // { -// throw new PLLinesCreatorException("Could not create the intersectionsection points between NWO and Phreatic line to create horizontal level."); +// throw new PlLinesCreatorException("Could not create the intersectionsection points between NWO and Phreatic line to create horizontal level."); // } // } // } -// private void MakeWaterLevelHorizontalInNWOAtRiverSideUsingInterSection1(GeometryPoint nwo2, GeometryPoint nwo3, GeometryPoint nwo4, PLLine pl1Line, PLLinePoint nwo3Pl, PLLinePoint nwo4Pl, PLLinePoint intersection1) +// private void MakeWaterLevelHorizontalInNWOAtRiverSideUsingInterSection1(GeometryPoint nwo2, GeometryPoint nwo3, GeometryPoint nwo4, PlLine pl1Line, PlLinePoint nwo3Pl, PlLinePoint nwo4Pl, PlLinePoint intersection1) // { // var lineNWO = new Deltares.Geometry.Line { BeginPoint = new GeometryPoint(nwo3.X, 0, nwo3.Z), EndPoint = new GeometryPoint(nwo4.X, 0, nwo4.Z) }; // var linePL = new Deltares.Geometry.Line { BeginPoint = new GeometryPoint(intersection1.X, 0, intersection1.Z), EndPoint = new GeometryPoint(nwo4.X, 0, intersection1.Z) }; @@ -1297,20 +1296,20 @@ // } // else // { -// throw new PLLinesCreatorException("Could not create the intersectionsection points between NWO and Phreatic line to create horizontal level."); +// throw new PlLinesCreatorException("Could not create the intersectionsection points between NWO and Phreatic line to create horizontal level."); // } // } // } -// private void MoveSelectedPLLinePointsBelowSurfaceLine(IEnumerable plPointsToBeMoved) +// private void MoveSelectedPlLinePointsBelowSurfaceLine(IEnumerable plPointsToBeMoved) // { -// foreach (var plLinePoint in plPointsToBeMoved) +// foreach (var PlLinePoint in plPointsToBeMoved) // { // // Determine which of these points must be moved and move them -// if (this.surfaceLine.Geometry.PositionXzOfPointRelatedToExtrapolatedLine(plLinePoint) != +// if (this.surfaceLine.Geometry.PositionXzOfPointRelatedToExtrapolatedLine(PlLinePoint) != // RelativeXzPosition.BelowGeometricLine) // { -// plLinePoint.Z = this.surfaceLine.Geometry.GetZatX(plLinePoint.X) - cOffsetPhreaticLineBelowSurface; +// PlLinePoint.Z = this.surfaceLine.Geometry.GetZatX(PlLinePoint.X) - cOffsetPhreaticLineBelowSurface; // } // } // } @@ -1322,26 +1321,26 @@ /// private PlLines CreateAllPlLinesWithGaugesWithFallbackToExpertKnowledgeRrd(Location location) { - var plLines = new PlLines(); + var PlLines = new PlLines(); - foreach (PlLineType plLineType in Enum.GetValues(typeof(PlLineType))) + foreach (PlLineType PlLineType in Enum.GetValues(typeof(PlLineType))) { - GaugePlLine gaugePlLine = (this.GaugePlLines != null) ? (this.GaugePlLines.FirstOrDefault(x => x.PlLineType == plLineType)) : null; + GaugePlLine gaugePlLine = (this.GaugePlLines != null) ? (this.GaugePlLines.FirstOrDefault(x => x.PlLineType == PlLineType)) : null; if (gaugePlLine != null && location != null) { - Boolean isUseWaterLevel = ((plLineType == PlLineType.Pl1) || (plLineType == PlLineType.Pl2)); - plLines.Lines[plLineType] = CreatePlLineFromGauges(gaugePlLine, this.Gauges, location, isUseWaterLevel); + Boolean isUseWaterLevel = ((PlLineType == PlLineType.Pl1) || (PlLineType == PlLineType.Pl2)); + PlLines.Lines[PlLineType] = CreatePlLineFromGauges(gaugePlLine, this.Gauges, location, isUseWaterLevel); } else - plLines.Lines[plLineType] = CreatePlLineByExpertKnowledge(plLineType, location.DamType, location.SlopeDampingPiezometricHeightPolderSide); + PlLines.Lines[PlLineType] = CreatePlLineByExpertKnowledge(PlLineType, location.DamType, location.SlopeDampingPiezometricHeightPolderSide); // currentPL1Line is needed when calculating uplift reduction for PL3 and Pl4 - if (plLineType == PlLineType.Pl1) + if (PlLineType == PlLineType.Pl1) { - currentPL1Line = plLines.Lines[plLineType]; + currentPL1Line = PlLines.Lines[PlLineType]; } } - return plLines; + return PlLines; } /// @@ -1351,70 +1350,63 @@ public PlLines CreateAllPlLines(Location location) { - PlLines plLines = new PlLines(); + PlLines PlLines = new PlLines(); switch (modelParametersForPlLines.PlLineCreationMethod) { case PlLineCreationMethod.ExpertKnowledgeLinearInDike: - case PlLineCreationMethod.ExpertKnowledgeRRD: plLines = CreateAllPlLinesWithExpertKnowledge(location); + case PlLineCreationMethod.ExpertKnowledgeRRD: PlLines = CreateAllPlLinesWithExpertKnowledge(location); break; - case PlLineCreationMethod.GaugesWithFallbackToExpertKnowledgeRRD: plLines = CreateAllPlLinesWithGaugesWithFallbackToExpertKnowledgeRrd(location); + case PlLineCreationMethod.GaugesWithFallbackToExpertKnowledgeRRD: PlLines = CreateAllPlLinesWithGaugesWithFallbackToExpertKnowledgeRrd(location); break; } // If PL Line2 does not exists, make it equal to PL line 4 - if (!plLines.Lines[PlLineType.Pl2].Exists()) + if (!PlLines.Lines[PlLineType.Pl2].Exists()) { - plLines.Lines[PlLineType.Pl2] = plLines.Lines[PlLineType.Pl4].Clone(); + PlLines.Lines[PlLineType.Pl2] = PlLines.Lines[PlLineType.Pl4].Clone(); } - if (!plLines.Lines[PlLineType.Pl1].IsXAscending()) + if (!PlLines.Lines[PlLineType.Pl1].IsXAscending()) { throw new PlLinesCreatorException("PlLine 1 not an X-ascending polyline"); } - return plLines; + return PlLines; } /// /// /// - /// + /// /// - public PlLine CreatePlLineByExpertKnowledge(PlLineType plLineType, DamType damType, double slopeGradient) + public PlLine CreatePlLineByExpertKnowledge(PlLineType PlLineType, DamType damType, double slopeGradient) { - PlLine plLine = null; + PlLine PlLine = null; - switch (plLineType) + switch (PlLineType) { case PlLineType.Pl1: - plLine = this.CreatePlLine1ByExpertKnowledge(); + PlLine = this.CreatePlLine1ByExpertKnowledge(); break; case PlLineType.Pl2: - plLine = this.CreatePlLine2ByExpertKnowledge(this.ModelParametersForPlLines.PenetrationLength, this.HeadInPlLine2); + PlLine = this.CreatePlLine2ByExpertKnowledge(this.ModelParametersForPlLines.PenetrationLength, this.HeadInPlLine2); break; case PlLineType.Pl3: - plLine = this.CreatePlLine3ByExpertKnowledge(HeadPL3TakingInAccountHydraulicShortcut, this.ModelParametersForPlLines.DampingFactorPl3, slopeGradient); + PlLine = this.CreatePlLine3ByExpertKnowledge(HeadPL3TakingInAccountHydraulicShortcut, this.ModelParametersForPlLines.DampingFactorPl3, slopeGradient); break; case PlLineType.Pl4: - plLine = this.CreatePlLine4ByExpertKnowledge(GetHeadPL4TakingInAccountHydraulicShortcut(damType), this.ModelParametersForPlLines.DampingFactorPl4, slopeGradient); + PlLine = this.CreatePlLine4ByExpertKnowledge(GetHeadPL4TakingInAccountHydraulicShortcut(damType), this.ModelParametersForPlLines.DampingFactorPl4, slopeGradient); break; } - if (plLine != null) + if (PlLine != null) { - plLine.DeleteCoinsidingPoints(); + PlLine.DeleteCoinsidingPoints(); } - return plLine; + return PlLine; } - /* - private PLLine CreatePLLineFromSensorGroup(PLLineType pLLineType, SensorLocation sensorLocation) - { - var creator = SensorPLLineCreator.CreateInstance(sensorLocation); - return creator.CreatePLLine(pLLineType); - } - */ /// /// @@ -1425,7 +1417,7 @@ /// private PlLine CreatePlLineFromGauges(GaugePlLine gaugePlLine, IEnumerable gauges, Location location, Boolean useWaterLevel) { - PlLine plLine = new PlLine(); + PlLine PlLine = new PlLine(); double? gaugeWaterLevelRiver = null; double? leftMostXAtRiverLevel = null; @@ -1476,7 +1468,7 @@ { if (!leftMostXAtRiverLevel.HasValue || localX > leftMostXAtRiverLevel) { - plLine.Points.Add(new PlLinePoint(localX.Value, localZ.Value)); + PlLine.Points.Add(new PlLinePoint(localX.Value, localZ.Value)); if (useWaterLevel) { // Have to account for waterlevel @@ -1489,19 +1481,19 @@ { // this is where the waterlevel intersects with the dike leftMostXAtRiverLevel = intersectionsAtX.First(); - plLine.Points.Add(new PlLinePoint(leftMostXAtRiverLevel.Value, gaugeWaterLevelRiver.Value)); + PlLine.Points.Add(new PlLinePoint(leftMostXAtRiverLevel.Value, gaugeWaterLevelRiver.Value)); } else { // No intersection with dike; polder is flooded leftMostXAtRiverLevel = this.surfaceLine.Geometry.Points.Last().X; - plLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, gaugeWaterLevelRiver.Value)); + PlLine.Points.Add(new PlLinePoint(this.surfaceLine.Geometry.Points.Last().X, gaugeWaterLevelRiver.Value)); } } } else { - // In this case no intersection for this Plline with the dike will be considered + // In this case no intersection for this PlLine with the dike will be considered leftMostXAtRiverLevel = this.surfaceLine.Geometry.Points.First().X; } } @@ -1510,13 +1502,13 @@ pointIndex++; } - if (plLine.Points.Count() > 0) + if (PlLine.Points.Count() > 0) { - plLine.Points.First().X = this.surfaceLine.Geometry.Points.First().X; - plLine.Points.Last().X = this.surfaceLine.Geometry.Points.Last().X; + PlLine.Points.First().X = this.surfaceLine.Geometry.Points.First().X; + PlLine.Points.Last().X = this.surfaceLine.Geometry.Points.Last().X; } - return plLine; + return PlLine; } /// Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLineCreatorException.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLineCreatorException.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLineCreatorException.cs (revision 1581) @@ -0,0 +1,48 @@ +// 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.Runtime.Serialization; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + [Serializable] + public class SensorPlLineCreatorException : Exception + { + public SensorPlLineCreatorException() + { + } + + public SensorPlLineCreatorException(string message) : base(message) + { + } + + public SensorPlLineCreatorException(string message, Exception inner) : base(message, inner) + { + } + + protected SensorPlLineCreatorException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Geotechnics/SurfaceLine2Extensions.cs =================================================================== diff -u -r1363 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Data/Geotechnics/SurfaceLine2Extensions.cs (.../SurfaceLine2Extensions.cs) (revision 1363) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Geotechnics/SurfaceLine2Extensions.cs (.../SurfaceLine2Extensions.cs) (revision 1581) @@ -476,6 +476,35 @@ } /// + /// 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 X coordinate of + /// to . + /// + /// When greater than + /// the height of the characteristic point . + /// + public static GeometryPoint DetermineIntersectionBetweenTaludRiverSideAndWaterLevel(this SurfaceLine2 line, double level) + { + double startXCoordinate = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelOutside).X; + var waterlevelLine = GetWaterlevelLineStartingFrom(line, level, startXCoordinate); + + return DetermineIntersectionWithHorizontalLevel(line, waterlevelLine); + } + + /// /// Determines the shoulder length for given shoulder top inside. /// /// The line. @@ -613,14 +642,14 @@ /// Throw an in case the given height level is /// higher than the characteristic point . /// - private static void ThrowWhenLevelAboveDike(SurfaceLine2 line, double Level) + private static void ThrowWhenLevelAboveDike(SurfaceLine2 line, double level) { var dikeTopAtRiver = line.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtRiver); - if (Level > dikeTopAtRiver.Z) + 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)); + level, dikeTopAtRiver.Z)); } } #endregion Private methods Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLineCreatorBase.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLineCreatorBase.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLineCreatorBase.cs (revision 1581) @@ -0,0 +1,236 @@ +// 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.General; +using Deltares.DamEngine.Data.General.PlLines; +using Deltares.DamEngine.Data.General.Sensors; +using Deltares.DamEngine.Data.General.Sensors.Specifications; +using Deltares.DamEngine.Data.General.Specifications.Extensions; +using Deltares.DamEngine.Data.Geometry; +using Deltares.DamEngine.Data.Geotechnics; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + internal abstract class SensorPlLineCreatorBase : IPlLineCreator + { +// internal readonly static LogHelper Logger = LogHelper.Create(); + + protected SensorLocation SensorLocation; + protected readonly IDictionary SensorValues; + + /// + /// Initializes a new instance of the class. + /// + /// The sensor location. + /// The type. + /// The sensor values. + protected SensorPlLineCreatorBase(SensorLocation sensorLocation, PlLineType type, IDictionary sensorValues) + { + SensorLocation = sensorLocation; + SensorValues = sensorValues; + PlLineType = type; + } + + /// + /// Gets the water level sensor. + /// + public Sensor WaterLevelSensor + { + get + { + try + { + return SensorLocation.Sensors + .GetBySpecification(new WaterLevelSensorSpecification()) + .Single(); + } + catch (Exception e) + { + throw new PlLinesCreatorException("There was an error getting the water level sensor. Maybe because there was no water level sensor defined", e); + } + } + } + + /// + /// Gets the water level sensor. + /// + public Sensor PolderLevelSensor + { + get + { + var sensors = SensorLocation.Sensors.GetBySpecification(new PolderLevelSensorSpecification()).ToList(); + if (sensors.Count() == 1) + { + return sensors.First(); + } + if (!sensors.Any()) + { + return null; + } + throw new PlLinesCreatorException("There are multiple polder level sensors defined."); + } + } + + /// + /// Gets the PiezometricHead type sensors sorted along profile. + /// + /// + /// Water level and polder level sensors should not be in this collection + /// + protected IEnumerable SensorsSortedAlongProfile + { + get + { + return SensorLocation.GetSensorsSortedByRelativeLocationAlongProfile(PlLineType).Select(s => s.Value); + } + } + + /// + /// Gets the X begin boundary. + /// + protected double XBeginBoundary + { + get + { + return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelOutside).X; + } + } + + /// + /// Gets the X end boundary. + /// + protected double XEndBoundary + { + get + { + return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelInside).X; + } + } + + protected bool DikeHasDitch + { + get { return SensorLocation.SurfaceLine.HasDitch(); } + } + + protected bool DikeHasShoulder + { + get { return SensorLocation.SurfaceLine.HasShoulderInside(); } + } + + + /// + /// Gets the Y value at the end boundary. + /// + protected double YEndBoundary + { + get + { + return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelInside).Y; + } + } + + protected double XDikeToeAtRiver + { + get { return SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver).X; } + } + + /// + /// Intersection of line at river with a given Z value. + /// + /// + protected double IntersectionXAtRiverWaterLevel(double waterLevel) + { + GeometryPoint point = SensorLocation.SurfaceLine.DetermineIntersectionBetweenTaludRiverSideAndWaterLevel(waterLevel); + if (point == null) + { + throw new PlLinesCreatorException("No intersection point found with water level at dike river side"); + } + return point.X; + } + + /// + /// Intersection of line at polder with a given Z value. + /// + /// + protected double IntersectionXAtPolderZ(double z) + { + var intersectionsXatZ = SensorLocation.SurfaceLine.Geometry.IntersectionsXAtZ(z).ToList(); + var hasIntersections = intersectionsXatZ.Count() == 2; + return hasIntersections ? intersectionsXatZ.Last() : double.NaN; + } + + /// + /// Gets the sensor X value. + /// + /// The sensor. + /// + protected double GetSensorXValue(Sensor sensor) + { + return sensor.RelativeLocation; + } + + /// + /// Gets the sensor Z value. + /// + /// The sensor. + /// + protected double GetSensorZValue(Sensor sensor) + { + return SensorValues[sensor]; + } + + /// + /// Gets the water level at polder. + /// + protected double WaterLevelAtPolder + { + get + { + throw new NotImplementedException(); +// var sensor = SensorLocation.GetSensorsSortedByRelativeLocationAlongProfile(PlLineType).Last().Value; +// var value = SensorLocation.GetValue(x => x.PL1WaterLevelAtPolder, SensorValues, sensor); +// if (value.HasValue) +// return value.Value; +// +// var message = string.Format("Water level at polder for the location '{0}' was not set or initialized", SensorLocation.LocationName); +// throw new InvalidOperationException(message); + + } + } + + /// + /// Gets the type of the PL line. + /// + /// + /// The type of the PL line. + /// + public PlLineType PlLineType { get; private set; } + + /// + /// Creates the PL line. + /// + /// + public abstract PlLine CreatePlLine(); + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Sensors/SensorPLLineCreatorTest.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Sensors/SensorPLLineCreatorTest.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Sensors/SensorPLLineCreatorTest.cs (revision 1581) @@ -0,0 +1,824 @@ +// 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.Calculators.PlLinesCreator; +using Deltares.DamEngine.Data.General; +using Deltares.DamEngine.Data.General.PlLines; +using Deltares.DamEngine.Data.General.Sensors; +using Deltares.DamEngine.Data.Geometry; +using Deltares.DamEngine.Data.Geotechnics; +using NUnit.Framework; + +namespace Deltares.DamEngine.Calculators.Tests.Sensors +{ + [TestFixture] + public class SensorPlLineCreatorTest + { + #region Setup + + [TestFixtureSetUp] + public void FixtureSetup() + { + } + + [TestFixtureTearDown] + public void FixtureTearDown() + { + } + + [SetUp] + public void TestSetup() + { + + } + + [TearDown] + public void TestTearDown() + { + + } + + #endregion + + [Test] + [ExpectedException(typeof(PlLinesCreatorException))] + public void CreatePlLine_Pl1NoSensorData_Throws() + { + // setup + var location = new Location("test"); + SensorLocation sensorLocation = new SensorFactory().CreateSensorLocation(location); + var creator = SensorPlLineCreator.CreateInstance(sensorLocation, new Dictionary()); + + // call + creator.CreatePlLine(PlLineType.Pl1); + } + + [Test] // Pl2 is not yet supported + [ExpectedException(typeof(NotSupportedException))] + public void CreatePlLine_Pl2_ThrowsNotSupported() + { + // setup + var location = new Location("test"); + SensorLocation sensorLocation = new SensorFactory().CreateSensorLocation(location); + var creator = SensorPlLineCreator.CreateInstance(sensorLocation, new Dictionary()); + + // call + creator.CreatePlLine(PlLineType.Pl2); + } + + [Test] + [ExpectedException(typeof(PlLinesCreatorException))] + public void CreatePlLine_Pl3NoSensorData_Throws() + { + // setup + var location = new Location("test"); + SensorLocation sensorLocation = new SensorFactory().CreateSensorLocation(location); + var creator = SensorPlLineCreator.CreateInstance(sensorLocation, new Dictionary()); + + // call + creator.CreatePlLine(PlLineType.Pl3); + } + + [Test] + [ExpectedException(typeof(PlLinesCreatorException))] + public void CreatePlLine_Pl4NoSensorData_Throws() + { + // setup + var location = new Location("test"); + SensorLocation sensorLocation = new SensorFactory().CreateSensorLocation(location); + var creator = SensorPlLineCreator.CreateInstance(sensorLocation, new Dictionary()); + + // call + creator.CreatePlLine(PlLineType.Pl4); + } + + [Test] + public void CreatePlLine_AllPlLinesDummyTest_ResultsAreAsExpected() + { + // setup + var surfaceLine = new SurfaceLine2 + { + CharacteristicPoints = { GeometryMustContainPoint = true }, + Geometry = new GeometryPointString() + }; + var location = new Location("test") + { + RiverLevel = 1, + PolderLevel = 1 + }; + location.AddNewSensorData(); + location.SensorData.Pl1PlLineOffsetBelowDikeToeAtPolder = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtPolder = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.LocationData; + + // create dike with ditch + + + surfaceLine.EnsurePointOfType(0.0, 0.0, CharacteristicPointType.SurfaceLevelOutside); + surfaceLine.EnsurePointOfType(50.0, 0.0, CharacteristicPointType.DikeToeAtRiver); + surfaceLine.EnsurePointOfType(85.0, 8.0, CharacteristicPointType.DikeTopAtRiver); + surfaceLine.EnsurePointOfType(95.0, 8.0, CharacteristicPointType.DikeTopAtPolder); + surfaceLine.EnsurePointOfType(108.0, 4.0, CharacteristicPointType.ShoulderBaseInside); + surfaceLine.EnsurePointOfType(116.0, 3.0, CharacteristicPointType.ShoulderTopInside); + surfaceLine.EnsurePointOfType(121.0, 0.0, CharacteristicPointType.DikeToeAtPolder); + surfaceLine.EnsurePointOfType(190.0, 0.0, CharacteristicPointType.SurfaceLevelInside); + + location.SurfaceLine = surfaceLine; + + var sensor1 = new Sensor { ID = 1, Name = "Test1", Depth = 2, RelativeLocation = 50, PlLineMappings = new[] { PlLineType.Pl1 }, Type = SensorType.WaterLevel }; + var sensor2 = new Sensor { ID = 2, Name = "Test2", Depth = 2, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor3 = new Sensor { ID = 3, Name = "Test3", Depth = 2, RelativeLocation = 104, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor4 = new Sensor { ID = 4, Name = "Test4", Depth = 2, RelativeLocation = 123.3, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor5 = new Sensor { ID = 5, Name = "Test5", Depth = 2, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl3, PlLineType.Pl4 } }; + var sensor6 = new Sensor { ID = 6, Name = "Test6", Depth = 2, RelativeLocation = 123.3, PlLineMappings = new[] { PlLineType.Pl3, PlLineType.Pl4 } }; + var sensor7 = new Sensor { ID = 7, Name = "Test7", Depth = 2, RelativeLocation = 180, PlLineMappings = new[] { PlLineType.Pl3, PlLineType.Pl4 } }; + + var repository = new SensorRepository(location); + + repository.Add(sensor1); + repository.Add(sensor2); + repository.Add(sensor3); + repository.Add(sensor4); + repository.Add(sensor5); + repository.Add(sensor6); + repository.Add(sensor7); + + IDictionary sensorValues = new Dictionary() + { + {sensor1, 1}, + {sensor2, 1}, + {sensor3, 1}, + {sensor4, 1}, + {sensor5, 1}, + {sensor6, 1}, + {sensor7, 1}, + }; + + var sensorLocation = location.SensorData; + + var creator = new SensorPlLine1Creator(sensorLocation, sensorValues); + + // call + PlLine actual = creator.CreatePlLine(); + + // asserts + + // Number of expected points in line + var expectedCount = repository.Sensors.Count(s => s.PlLineMappings.Contains(PlLineType.Pl1)) + 2 + 4; // interection + end + 4 configured characteristic points + Assert.AreEqual(expectedCount, actual.Points.Count); + + // P1 + var p1 = actual.Points.First(); + Assert.IsNotNull(p1); + var expectedZValueP1 = sensorValues[sensor1]; + Assert.AreEqual(expectedZValueP1, p1.Z); + + // P2 + var p2 = actual.Points.FirstOrDefault(p => p.Name == sensor2.Name); + Assert.IsNotNull(p2); + Assert.AreEqual(sensor2.RelativeLocation, p2.X); + var expectedZValueP2 = sensorValues[sensor2]; + Assert.AreEqual(expectedZValueP2, p2.Z); + + // P3 + var p3 = actual.Points.FirstOrDefault(p => p.Name == sensor3.Name); + Assert.IsNotNull(p3); + Assert.AreEqual(sensor3.RelativeLocation, p3.X); + var expectedZValueP3 = sensorValues[sensor3]; + Assert.AreEqual(expectedZValueP3, p3.Z); + + // P4 + var p4 = actual.Points.FirstOrDefault(p => p.Name == sensor4.Name); + Assert.IsNotNull(p4); + Assert.AreEqual(sensor4.RelativeLocation, p4.X); + var expectedZValueP4 = sensorValues[sensor4]; + Assert.AreEqual(expectedZValueP4, p4.Z); + } + + [Test] + public void CreatePlLine_Pl1DikeToeAtPolderToSurfaceLevelInside_ZValueShouldBePolderLevel() + { + // setup + const double polderLevel = -0.5; + const double expectedEndPoint = -0.1; // Is not polderlevel, but level under toe of dike inward + var surfaceLine = new SurfaceLine2 + { + CharacteristicPoints = + { + GeometryMustContainPoint = true + }, + Geometry = new GeometryPointString() + }; + var location = new Location("test") + { + RiverLevel = 5, + PolderLevel = polderLevel + }; + location.AddNewSensorData(); + location.SensorData.Pl1PlLineOffsetBelowDikeToeAtPolder = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtPolder = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.LocationData; + + // create dike with ditch + + surfaceLine.EnsurePointOfType(0.0, 0.0, CharacteristicPointType.SurfaceLevelOutside); + surfaceLine.EnsurePointOfType(50.0, 0.0, CharacteristicPointType.DikeToeAtRiver); + surfaceLine.EnsurePointOfType(85.0, 8.0, CharacteristicPointType.DikeTopAtRiver); + surfaceLine.EnsurePointOfType(95.0, 8.0, CharacteristicPointType.DikeTopAtPolder); + surfaceLine.EnsurePointOfType(108.0, 4.0, CharacteristicPointType.ShoulderBaseInside); + surfaceLine.EnsurePointOfType(116.0, 3.0, CharacteristicPointType.ShoulderTopInside); + surfaceLine.EnsurePointOfType(121.0, 0.0, CharacteristicPointType.DikeToeAtPolder); + surfaceLine.EnsurePointOfType(190.0, 0.0, CharacteristicPointType.SurfaceLevelInside); + + location.SurfaceLine = surfaceLine; + + var sensor1 = new Sensor { ID = 1, Name = "Test1", Depth = 2, RelativeLocation = 50, PlLineMappings = new[] { PlLineType.Pl1 }, Type = SensorType.WaterLevel }; + var sensor2 = new Sensor { ID = 2, Name = "Test2", Depth = 2, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor3 = new Sensor { ID = 3, Name = "Test3", Depth = 2, RelativeLocation = 104, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor4 = new Sensor { ID = 4, Name = "Test4", Depth = 2, RelativeLocation = 123.3, PlLineMappings = new[] { PlLineType.Pl1 } }; + + var repository = new SensorRepository(location); + + repository.Add(sensor1); + repository.Add(sensor2); + repository.Add(sensor3); + repository.Add(sensor4); + + IDictionary sensorValues = new Dictionary() + { + {sensor1, 1}, + {sensor2, 1}, + {sensor3, 1}, + {sensor4, 1}, + }; + + var sensorLocation = location.SensorData; + + var creator = new SensorPlLine1Creator(sensorLocation, sensorValues); + + // call + PlLine actual = creator.CreatePlLine(); + + // asserts + var lastPoint = actual.Points.Last(); + Assert.AreEqual(expectedEndPoint, lastPoint.Z); + } + + [Test, Ignore("Test must be reviewed.")] + public void CreatePlLine_Pl1DikeToeAtPolderToSurfaceLevelInsideAndNoLocationData_ZValueShouldBeSurfaceLevel() + { + // setup + const int expectedValue = 1; + var surfaceLine = new SurfaceLine2 + { + CharacteristicPoints = { GeometryMustContainPoint = true }, + Geometry = new GeometryPointString() + }; + var location = new Location("test") + { + RiverLevel = 5, + PolderLevel = -1 + }; + location.AddNewSensorData(); + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtPolder = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1PlLineOffsetBelowDikeToeAtPolder = DataSourceTypeSensors.Sensor; + + surfaceLine.EnsurePointOfType(0.0, 0.0, CharacteristicPointType.SurfaceLevelOutside); + surfaceLine.EnsurePointOfType(50.0, 0.0, CharacteristicPointType.DikeToeAtRiver); + surfaceLine.EnsurePointOfType(85.0, 8.0, CharacteristicPointType.DikeTopAtRiver); + surfaceLine.EnsurePointOfType(95.0, 8.0, CharacteristicPointType.DikeTopAtPolder); + surfaceLine.EnsurePointOfType(108.0, 4.0, CharacteristicPointType.ShoulderBaseInside); + surfaceLine.EnsurePointOfType(116.0, 3.0, CharacteristicPointType.ShoulderTopInside); + surfaceLine.EnsurePointOfType(121.0, 0.0, CharacteristicPointType.DikeToeAtPolder); + surfaceLine.EnsurePointOfType(190.0, expectedValue, CharacteristicPointType.SurfaceLevelInside); + + location.SurfaceLine = surfaceLine; + + var sensor1 = new Sensor { ID = 1, Name = "Test1", Depth = 2, RelativeLocation = 50, PlLineMappings = new[] { PlLineType.Pl1 }, Type = SensorType.WaterLevel }; + var sensor2 = new Sensor { ID = 2, Name = "Test2", Depth = 2, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor3 = new Sensor { ID = 3, Name = "Test3", Depth = 2, RelativeLocation = 104, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor4 = new Sensor { ID = 4, Name = "Test4", Depth = 2, RelativeLocation = 123.3, PlLineMappings = new[] { PlLineType.Pl1 } }; + + var repository = new SensorRepository(location); + + repository.Add(sensor1); + repository.Add(sensor2); + repository.Add(sensor3); + repository.Add(sensor4); + + IDictionary sensorValues = new Dictionary() + { + {sensor1, 1}, + {sensor2, 1}, + {sensor3, 1}, + {sensor4, 1}, + }; + + var sensorLocation = location.SensorData; + + var creator = new SensorPlLine1Creator(sensorLocation, sensorValues); + + // call + PlLine actual = creator.CreatePlLine(); + + // asserts + var lastPoint = actual.Points.Last(); + Assert.AreEqual(expectedValue, lastPoint.Z); + } + + [Test] + public void GetDitchWaterLevelIntersectionAtXDikeSide_PolderLevelIntersectsInDitch_ShouldReturnIntersectionValue() + { + // setup + var surfaceLine = new SurfaceLine2 + { + CharacteristicPoints = + { + GeometryMustContainPoint = true + }, + Geometry = new GeometryPointString() + }; + var location = new Location(); + surfaceLine.EnsurePointOfType(0.0, 0.0, CharacteristicPointType.SurfaceLevelOutside); + surfaceLine.EnsurePointOfType(10, 0.0, CharacteristicPointType.DitchDikeSide); + surfaceLine.EnsurePointOfType(15, -5, CharacteristicPointType.BottomDitchDikeSide); + surfaceLine.EnsurePointOfType(17, -5, CharacteristicPointType.BottomDitchPolderSide); + surfaceLine.EnsurePointOfType(20, 0.0, CharacteristicPointType.DitchPolderSide); + surfaceLine.EnsurePointOfType(25, 0.0, CharacteristicPointType.SurfaceLevelInside); + + location.AddNewSensorData(); + location.SurfaceLine = surfaceLine; + + var creator = new SensorPlLine1Creator(location.SensorData, new Dictionary()); + var actual = creator.GetDitchWaterLevelIntersectionAtXDikeSide(-2.5); + const double expected = 12.5; + Assert.AreEqual(expected, actual); + } + + [Test] + public void CreatePlLine_Pl1IgnorAllSensors_ResultsAreAsExpected() + { + // setup + var surfaceLine = new SurfaceLine2 + { + CharacteristicPoints = + { + GeometryMustContainPoint = true + }, + Geometry = new GeometryPointString() + }; + var location = new Location("test") + { + RiverLevel = 1, + PolderLevel = 1 + }; + location.AddNewSensorData(); + location.SensorData.Pl1PlLineOffsetBelowDikeToeAtPolder = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtPolder = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1WaterLevelAtRiver = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1WaterLevelAtPolder = DataSourceTypeSensors.LocationData; + + // Assert.IsTrue(location.SensorData.IsValid()); + + surfaceLine.EnsurePointOfType(0.0, 0.0, CharacteristicPointType.SurfaceLevelOutside); + surfaceLine.EnsurePointOfType(50.0, 0.0, CharacteristicPointType.DikeToeAtRiver); + surfaceLine.EnsurePointOfType(80.0, 5.0, CharacteristicPointType.DikeTopAtRiver); + surfaceLine.EnsurePointOfType(125, 5.0, CharacteristicPointType.DikeTopAtPolder); + surfaceLine.EnsurePointOfType(140.0, 0.0, CharacteristicPointType.DikeToeAtPolder); + surfaceLine.EnsurePointOfType(145.0, 0.0, CharacteristicPointType.ShoulderBaseInside); + surfaceLine.EnsurePointOfType(190.0, 0.0, CharacteristicPointType.SurfaceLevelInside); + + location.SurfaceLine = surfaceLine; + + var sensor1 = new Sensor { ID = 1, Name = "Test1", Depth = 2, RelativeLocation = 50, PlLineMappings = new[] { PlLineType.Pl1 }, Type = SensorType.WaterLevel }; + var sensor2 = new Sensor { ID = 2, Name = "Test2", Depth = 2, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor3 = new Sensor { ID = 3, Name = "Test3", Depth = 2, RelativeLocation = 104, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor4 = new Sensor { ID = 4, Name = "Test4", Depth = 2, RelativeLocation = 123.3, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor5 = new Sensor { ID = 5, Name = "Test5", Depth = 2, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl3, PlLineType.Pl4 } }; + var sensor6 = new Sensor { ID = 6, Name = "Test6", Depth = 2, RelativeLocation = 123.3, PlLineMappings = new[] { PlLineType.Pl3, PlLineType.Pl4 } }; + var sensor7 = new Sensor { ID = 7, Name = "Test7", Depth = 2, RelativeLocation = 180, PlLineMappings = new[] { PlLineType.Pl3, PlLineType.Pl4 } }; + + var repository = new SensorRepository(location); + + repository.Add(sensor1); + repository.Add(sensor2); + repository.Add(sensor3); + repository.Add(sensor4); + repository.Add(sensor5); + repository.Add(sensor6); + repository.Add(sensor7); + + IDictionary sensorValues = new Dictionary() + { + {sensor1, 1}, + {sensor2, 1}, + {sensor3, 1}, + {sensor4, 1}, + {sensor5, 1}, + {sensor6, 1}, + {sensor7, 1}, + }; + + var sensorLocation = location.SensorData; + var creator = new SensorPlLine1Creator(sensorLocation, sensorValues); + + // call + PlLine actual = creator.CreatePlLine(); + + // asserts + + // Number of expected points in line + const int expectedNumberOfCharacteristicPoints = 2; + var pl1SensorCount = repository.Sensors.Count(s => s.PlLineMappings.Contains(PlLineType.Pl1)) + + expectedNumberOfCharacteristicPoints + 1; // plus one for the intersection point + one for point below toe of dike + Assert.AreEqual(pl1SensorCount, actual.Points.Count); + + // P2 + var p2 = actual.Points.FirstOrDefault(p => p.Name == sensor2.Name); + Assert.IsNotNull(p2); + Assert.AreEqual(sensor2.RelativeLocation, p2.X); + var expectedZValueP2 = sensorValues[sensor2]; + Assert.AreEqual(expectedZValueP2, p2.Z); + + // P3 + var p3 = actual.Points.FirstOrDefault(p => p.Name == sensor3.Name); + Assert.IsNotNull(p3); + Assert.AreEqual(sensor3.RelativeLocation, p3.X); + var expectedZValueP3 = sensorValues[sensor3]; + Assert.AreEqual(expectedZValueP3, p3.Z); + + // P4 + var p4 = actual.Points.FirstOrDefault(p => p.Name == sensor4.Name); + Assert.IsNotNull(p4); + Assert.AreEqual(sensor4.RelativeLocation, p4.X); + var expectedZValueP4 = sensorValues[sensor4]; + Assert.AreEqual(expectedZValueP4, p4.Z); + } + + [Test] + public void CreatePlLinePl1_DikeHasDitch_ResultsAreAsExpected() + { + var surfaceLine = new SurfaceLine2 + { + CharacteristicPoints = + { + GeometryMustContainPoint = true + }, + Geometry = new GeometryPointString() + }; + var location = new Location("test") + { + RiverLevel = 1, PolderLevel = 1 + }; + #region Setup + + location.AddNewSensorData(); + location.SensorData.Pl1PlLineOffsetBelowDikeToeAtPolder = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtPolder = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1WaterLevelAtRiver = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1WaterLevelAtPolder = DataSourceTypeSensors.LocationData; + + // Assert.IsTrue(location.SensorData.IsValid()); + + surfaceLine.EnsurePointOfType(0.0, 0.0, CharacteristicPointType.SurfaceLevelOutside); + surfaceLine.EnsurePointOfType(50.0, 0.0, CharacteristicPointType.DikeToeAtRiver); + surfaceLine.EnsurePointOfType(80.0, 5.0, CharacteristicPointType.DikeTopAtRiver); + surfaceLine.EnsurePointOfType(125, 5.0, CharacteristicPointType.DikeTopAtPolder); + surfaceLine.EnsurePointOfType(140.0, 0.0, CharacteristicPointType.DikeToeAtPolder); + surfaceLine.EnsurePointOfType(148.0, 0.5, CharacteristicPointType.DitchDikeSide); + surfaceLine.EnsurePointOfType(150.0, -4.0, CharacteristicPointType.BottomDitchDikeSide); + surfaceLine.EnsurePointOfType(154.0, -4.0, CharacteristicPointType.BottomDitchPolderSide); + surfaceLine.EnsurePointOfType(158.0, 0.0, CharacteristicPointType.DitchPolderSide); + surfaceLine.EnsurePointOfType(190.0, 0.0, CharacteristicPointType.SurfaceLevelInside); + + location.SurfaceLine = surfaceLine; + + var sensor1 = new Sensor { ID = 1, Name = "Test1", Depth = 2, RelativeLocation = 50, PlLineMappings = new[] { PlLineType.Pl1 }, Type = SensorType.WaterLevel }; + var sensor2 = new Sensor { ID = 2, Name = "Test2", Depth = 2, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor3 = new Sensor { ID = 3, Name = "Test3", Depth = 2, RelativeLocation = 104, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor4 = new Sensor { ID = 4, Name = "Test4", Depth = 2, RelativeLocation = 128, PlLineMappings = new[] { PlLineType.Pl1 } }; + + var repository = new SensorRepository(location); + + repository.Add(sensor1); + repository.Add(sensor2); + repository.Add(sensor3); + repository.Add(sensor4); + + IDictionary sensorValues = new Dictionary() + { + {sensor1, 3}, + {sensor2, 3}, + {sensor3, 3}, + {sensor4, 3}, + }; + + var sensorLocation = location.SensorData; + var creator = new SensorPlLine1Creator(sensorLocation, sensorValues); + + #endregion + + // call + PlLine actual = creator.CreatePlLine(); + + // asserts + + // Number of expected points in line + var pl1SensorCount = repository.Sensors.Count(s => s.PlLineMappings.Contains(PlLineType.Pl1)); + Assert.AreEqual(pl1SensorCount + 2 /* intersection points */, actual.Points.Count); + + // P2 + var p2 = actual.Points.FirstOrDefault(p => p.Name == sensor2.Name); + Assert.IsNotNull(p2); + Assert.AreEqual(sensor2.RelativeLocation, p2.X); + var expectedZValueP2 = sensorValues[sensor2]; + Assert.AreEqual(expectedZValueP2, p2.Z); + + // P3 + var p3 = actual.Points.FirstOrDefault(p => p.Name == sensor3.Name); + Assert.IsNotNull(p3); + Assert.AreEqual(sensor3.RelativeLocation, p3.X); + var expectedZValueP3 = sensorValues[sensor3]; + Assert.AreEqual(expectedZValueP3, p3.Z); + + // P4 + var p4 = actual.Points.FirstOrDefault(p => p.Name == sensor4.Name); + Assert.IsNotNull(p4); + Assert.AreEqual(sensor4.RelativeLocation, p4.X); + var expectedZValueP4 = sensorValues[sensor4]; + Assert.AreEqual(expectedZValueP4, p4.Z); + } + + [Test] + public void CreatePlLine_DikeHasDitchAndSensor_DichLevelIsHorizontal() + { + const int polderLevel = 0; + const int waterLevel = 1; + var surfaceLine = new SurfaceLine2 + { + CharacteristicPoints = + { + GeometryMustContainPoint = true + }, + Geometry = new GeometryPointString() + }; + var location = new Location("test") + { + RiverLevel = waterLevel, PolderLevel = polderLevel + }; + #region Setup + + location.AddNewSensorData(); + location.SensorData.Pl1PlLineOffsetBelowDikeToeAtPolder = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtPolder = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.Ignore; + location.SensorData.Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.Ignore; + + location.SensorData.Pl1WaterLevelAtRiver = DataSourceTypeSensors.Sensor; + location.SensorData.Pl1WaterLevelAtPolder = DataSourceTypeSensors.Sensor; + + // Assert.IsTrue(location.SensorData.IsValid()); + + surfaceLine.EnsurePointOfType(0.0, 0.0, CharacteristicPointType.SurfaceLevelOutside); + surfaceLine.EnsurePointOfType(50.0, 0.0, CharacteristicPointType.DikeToeAtRiver); + surfaceLine.EnsurePointOfType(80.0, 5.0, CharacteristicPointType.DikeTopAtRiver); + surfaceLine.EnsurePointOfType(125, 5.0, CharacteristicPointType.DikeTopAtPolder); + surfaceLine.EnsurePointOfType(140.0, 0.0, CharacteristicPointType.DikeToeAtPolder); + surfaceLine.EnsurePointOfType(148.0, 0.5, CharacteristicPointType.DitchDikeSide); + surfaceLine.EnsurePointOfType(150.0, -4.0, CharacteristicPointType.BottomDitchDikeSide); + surfaceLine.EnsurePointOfType(154.0, -4.0, CharacteristicPointType.BottomDitchPolderSide); + surfaceLine.EnsurePointOfType(158.0, 0.0, CharacteristicPointType.DitchPolderSide); + surfaceLine.EnsurePointOfType(190.0, 0.0, CharacteristicPointType.SurfaceLevelInside); + + location.SurfaceLine = surfaceLine; + + var sensor1 = new Sensor { ID = 1, Name = "Test1", Depth = 2, RelativeLocation = 50, PlLineMappings = new[] { PlLineType.Pl1 }, Type = SensorType.WaterLevel }; + var sensor2 = new Sensor { ID = 2, Name = "Test2", Depth = 2, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor3 = new Sensor { ID = 3, Name = "Test3", Depth = 2, RelativeLocation = 104, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor4 = new Sensor { ID = 4, Name = "Test4", Depth = 2, RelativeLocation = 128, PlLineMappings = new[] { PlLineType.Pl1 } }; + var sensor5 = new Sensor { ID = 5, Name = "Test5", Depth = 2, RelativeLocation = 152, PlLineMappings = new[] { PlLineType.Pl1 }, Type = SensorType.PolderLevel }; + + var repository = new SensorRepository(location); + + repository.Add(sensor1); + repository.Add(sensor2); + repository.Add(sensor3); + repository.Add(sensor4); + repository.Add(sensor5); + + IDictionary sensorValues = new Dictionary() + { + {sensor1, waterLevel}, + {sensor2, -1}, + {sensor3, -1}, + {sensor4, -1}, + {sensor5, -1.5} + }; + + var sensorLocation = location.SensorData; + + #endregion + + // call + var creator = new SensorPlLine1Creator(sensorLocation, sensorValues); + PlLine actual = creator.CreatePlLine(); + + // checks + var xDitchDikeSide = creator.GetDitchWaterLevelIntersectionAtXDikeSide(sensorValues[sensor5]); + var xDitchPolderSide = creator.GetDitchWaterLevelIntersectionAtXPolderSide(sensorValues[sensor5]); + Assert.IsTrue(actual.Points.Any(p => p.X == xDitchDikeSide)); + + var zDitchDikeSide = actual.Points.Single(p => p.X == xDitchDikeSide).Z; + var zDitchPolderSide = actual.Points.Single(p => p.X == xDitchPolderSide).Z; + + Assert.IsTrue(actual.Points.Any(p => p.X == xDitchPolderSide)); + Assert.AreEqual(zDitchDikeSide, zDitchPolderSide); + + var endPoint = actual.Points.Last(); + + // End point should be z of x intersection at dike side in ditch (horizontal) + Assert.AreEqual(endPoint.Z, zDitchDikeSide); + } + + [Test] + public void CreatePlLine_Pl3and4_FirstSegmentShouldBeHorizontalEqualToLocationData() + { + // setup + const double xDikeToeAtRiver = 50.0; + const double waterLevel = 1.0; + + var surfaceLine = new SurfaceLine2 + { + CharacteristicPoints = + { + GeometryMustContainPoint = true + }, + Geometry = new GeometryPointString() + }; + var location = new Location("test") + { + RiverLevel = waterLevel, PolderLevel = waterLevel + }; + { + location.AddNewSensorData(); + location.SensorData.Pl3 = DataSourceTypeSensors.Sensor; + location.SensorData.Pl1WaterLevelAtRiver = DataSourceTypeSensors.LocationData; + location.SensorData.Pl1WaterLevelAtPolder = DataSourceTypeSensors.LocationData; + + // Assert.IsTrue(location.SensorData.IsValid()); + + surfaceLine.EnsurePointOfType(0.0, 0.0, CharacteristicPointType.SurfaceLevelOutside); + surfaceLine.EnsurePointOfType(xDikeToeAtRiver, 0.0, CharacteristicPointType.DikeToeAtRiver); + surfaceLine.EnsurePointOfType(80.0, 5.0, CharacteristicPointType.DikeTopAtRiver); + surfaceLine.EnsurePointOfType(125, 5.0, CharacteristicPointType.DikeTopAtPolder); + surfaceLine.EnsurePointOfType(140.0, 0.0, CharacteristicPointType.DikeToeAtPolder); + surfaceLine.EnsurePointOfType(145.0, 0.0, CharacteristicPointType.ShoulderBaseInside); + surfaceLine.EnsurePointOfType(190.0, 0.0, CharacteristicPointType.SurfaceLevelInside); + + location.SurfaceLine = surfaceLine; + + + var sensor1 = new Sensor { ID = 1, Name = "Test1", Depth = waterLevel, RelativeLocation = 50, PlLineMappings = new[] { PlLineType.Pl1 }, Type = SensorType.WaterLevel }; + var sensor5 = new Sensor { ID = 5, Name = "Test5", Depth = -8, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl3 } }; + var sensor6 = new Sensor { ID = 6, Name = "Test6", Depth = -8, RelativeLocation = 123.3, PlLineMappings = new[] { PlLineType.Pl3 } }; + var sensor7 = new Sensor { ID = 7, Name = "Test7", Depth = -8, RelativeLocation = 180, PlLineMappings = new[] { PlLineType.Pl3 } }; + + var repository = new SensorRepository(location); + + repository.Add(sensor1); + repository.Add(sensor5); + repository.Add(sensor6); + repository.Add(sensor7); + + IDictionary sensorValues = new Dictionary() + { + {sensor1, waterLevel}, + {sensor5, waterLevel}, + {sensor6, waterLevel}, + {sensor7, waterLevel}, + }; + + var sensorLocation = location.SensorData; + var creator = new SensorPlLine3Creator(sensorLocation, sensorValues); + + // call + PlLine actual = creator.CreatePlLine(); + + //checks + + // start first segment + Assert.AreEqual(-0.0, actual.Points[0].X); + Assert.AreEqual(waterLevel, actual.Points[0].Z); + + // end of first segment (ends at DikeToeAtRiver) + Assert.AreEqual(xDikeToeAtRiver, actual.Points[1].X); + Assert.AreEqual(waterLevel, actual.Points[1].Z); + } + } + + [Test] + public void CreatePlLine_Pl3and4_FirstSegmentShouldBeHorizontalEqualToWaterLevelSensor() + { + // setup + const double xDikeToeAtRiver = 50.0; + const double waterLevel = -0.25; + + var surfaceLine = new SurfaceLine2 + { + CharacteristicPoints = + { + GeometryMustContainPoint = true + }, + Geometry = new GeometryPointString() + }; + var location = new Location("test") + { + RiverLevel = 1.0, PolderLevel = 1.0 + }; + { + location.AddNewSensorData(); + location.SensorData.Pl3 = DataSourceTypeSensors.Sensor; + location.SensorData.Pl1WaterLevelAtRiver = DataSourceTypeSensors.Sensor; + location.SensorData.Pl1WaterLevelAtPolder = DataSourceTypeSensors.Sensor; + + // Assert.IsTrue(location.SensorData.IsValid()); + + surfaceLine.EnsurePointOfType(0.0, 0.0, CharacteristicPointType.SurfaceLevelOutside); + surfaceLine.EnsurePointOfType(xDikeToeAtRiver, 0.0, CharacteristicPointType.DikeToeAtRiver); + surfaceLine.EnsurePointOfType(80.0, 5.0, CharacteristicPointType.DikeTopAtRiver); + surfaceLine.EnsurePointOfType(125, 5.0, CharacteristicPointType.DikeTopAtPolder); + surfaceLine.EnsurePointOfType(140.0, 0.0, CharacteristicPointType.DikeToeAtPolder); + surfaceLine.EnsurePointOfType(145.0, 0.0, CharacteristicPointType.ShoulderBaseInside); + surfaceLine.EnsurePointOfType(190.0, 0.0, CharacteristicPointType.SurfaceLevelInside); + + location.SurfaceLine = surfaceLine; + + + var sensor1 = new Sensor { ID = 1, Name = "Test1", Depth = -0.25, RelativeLocation = 50, PlLineMappings = new[] { PlLineType.Pl1 }, Type = SensorType.WaterLevel }; + var sensor5 = new Sensor { ID = 5, Name = "Test5", Depth = -8, RelativeLocation = 90, PlLineMappings = new[] { PlLineType.Pl3 } }; + var sensor6 = new Sensor { ID = 6, Name = "Test6", Depth = -8, RelativeLocation = 123.3, PlLineMappings = new[] { PlLineType.Pl3 } }; + var sensor7 = new Sensor { ID = 7, Name = "Test7", Depth = -8, RelativeLocation = 180, PlLineMappings = new[] { PlLineType.Pl3 } }; + + var repository = new SensorRepository(location); + + repository.Add(sensor1); + repository.Add(sensor5); + repository.Add(sensor6); + repository.Add(sensor7); + + IDictionary sensorValues = new Dictionary() + { + {sensor1, waterLevel}, + {sensor5, 1}, + {sensor6, 1}, + {sensor7, 1}, + }; + + var sensorLocation = location.SensorData; + var creator = new SensorPlLine3Creator(sensorLocation, sensorValues); + + // call + PlLine actual = creator.CreatePlLine(); + + //checks + + // start first segment + Assert.AreEqual(-0.0, actual.Points[0].X); + Assert.AreEqual(waterLevel, actual.Points[0].Z); + + // end of first segment (ends at DikeToeAtRiver) + Assert.AreEqual(xDikeToeAtRiver, actual.Points[1].X); + Assert.AreEqual(waterLevel, actual.Points[1].Z); + } + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/SpecificationBase.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/SpecificationBase.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/SpecificationBase.cs (revision 1581) @@ -0,0 +1,72 @@ +// 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. + +namespace Deltares.DamEngine.Data.General.Specifications +{ + /// + /// Abstract specification class that implements base properties + /// + /// The type of the candidate. + public abstract class SpecificationBase : ISpecification + { + protected SpecificationBase() + { + Name = this.GetType().Name; + } + + /// + /// Gets or sets the name of the specification. (Short description) + /// + /// + /// The name string value should be a required property for each specification. + /// + public string Name { get; set; } + + /// + /// Gets or sets the description of the specification. (Long description) + /// + /// + /// The description string value. + /// + public string Description { get; set; } + + /// + /// Determines whether the candidate satisfies the specification. + /// + /// The candidate to test. + /// + /// true if the candidate satisfies the specification otherwise, false. + /// + bool ISpecification.IsSatisfiedBy(object candidate) + { + return IsSatisfiedBy((TCandidate)candidate); + } + + /// + /// Determines whether the candidate satisfies the specification. + /// + /// The candidate to test. + /// + /// true if the candidate satisfies the specification otherwise, false. + /// + public abstract bool IsSatisfiedBy(TCandidate candidate); + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/PolderLevelSensorSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/PolderLevelSensorSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/PolderLevelSensorSpecification.cs (revision 1581) @@ -0,0 +1,33 @@ +// 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 Deltares.DamEngine.Data.General.Specifications; + +namespace Deltares.DamEngine.Data.General.Sensors.Specifications +{ + public class PolderLevelSensorSpecification : PredicateSpecification + { + public PolderLevelSensorSpecification() : + base(s => s.Type == SensorType.PolderLevel) + { + } + } +} Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorLocation.cs =================================================================== diff -u -r1263 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorLocation.cs (.../SensorLocation.cs) (revision 1263) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorLocation.cs (.../SensorLocation.cs) (revision 1581) @@ -24,6 +24,8 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using Deltares.DamEngine.Data.General.Sensors.Specifications; +using Deltares.DamEngine.Data.General.Specifications.Extensions; using Deltares.DamEngine.Data.Geotechnics; using Deltares.DamEngine.Data.Standard; @@ -181,22 +183,22 @@ } /// - /// Gets or sets the PL3 data source type. + /// Gets or sets the Pl3 data source type. /// /// - /// The PL3. + /// The Pl3. /// // [Specification(typeof(SensorOrLocationData))] - public DataSourceTypeSensors PL3 { get; set; } + public DataSourceTypeSensors Pl3 { get; set; } /// - /// Gets or sets the PL4 data source type. + /// Gets or sets the Pl4 data source type. /// /// - /// The PL4. + /// The Pl4. /// // [Specification(typeof(SensorOrLocationData))] - public DataSourceTypeSensors PL4 { get; set; } + public DataSourceTypeSensors Pl4 { get; set; } /// @@ -209,46 +211,46 @@ public DataSourceTypeSensors Pl1WaterLevelAtRiver { get; set; } /// - /// Gets or sets the PL1 PL line offset below dike top at river data source type. + /// Gets or sets the Pl1 PL line offset below dike top at river data source type. /// /// - /// Data source type for PL1 PLLine offset below dike top at river. + /// Data source type for Pl1 PLLine offset below dike top at river. /// // [Specification(typeof(IgnoreOrLocationData))] public DataSourceTypeSensors Pl1PlLineOffsetBelowDikeTopAtRiver { get; set; } /// - /// Gets or sets the PL1 PL line offset below dike top at polder data source type + /// Gets or sets the Pl1 PL line offset below dike top at polder data source type /// /// - /// The PL1 PL line offset below dike top at polder. + /// The Pl1 PL line offset below dike top at polder. /// // [Specification(typeof(IgnoreOrLocationData))] public DataSourceTypeSensors Pl1PlLineOffsetBelowDikeTopAtPolder { get; set; } /// - /// Gets or sets the PL1 PL line offset below shoulder base inside data source type + /// Gets or sets the Pl1 PL line offset below shoulder base inside data source type /// /// - /// The PL1 PL line offset below shoulder base inside. + /// The Pl1 PL line offset below shoulder base inside. /// // [Specification(typeof(IgnoreOrLocationData))] public DataSourceTypeSensors Pl1PlLineOffsetBelowShoulderBaseInside { get; set; } /// - /// Gets or sets the PL1 PL line offset below dike toe at polder data source type. + /// Gets or sets the Pl1 PL line offset below dike toe at polder data source type. /// /// - /// The PL1 PL line offset below dike toe at polder. + /// The Pl1 PL line offset below dike toe at polder. /// //[Specification(typeof(IgnoreOrLocationData))] public DataSourceTypeSensors Pl1PlLineOffsetBelowDikeToeAtPolder { get; set; } /// - /// Gets or sets the PL1 polder level data source type. + /// Gets or sets the Pl1 polder level data source type. /// /// - /// The PL1 polder level. + /// The Pl1 polder level. /// //[Specification(typeof(SensorOrLocationData))] public DataSourceTypeSensors Pl1WaterLevelAtPolder { get; set; } @@ -308,35 +310,35 @@ /// /// Type of the pl line. /// -// internal SortedDictionary GetSensorsSortedByRelativeLocationAlongProfile(PLLineType plLineType) -// { -// var calculatedRelativeLocations = BuildRelativeLocationTable(); -// -// var table = new SortedDictionary(); -// -// // leave out the water level and polder level sensors -// var candidateSensors = -// Sensors.GetBySpecification(new PiezometricHeadSensorSpecification()); -// -// foreach (var sensor in candidateSensors) -// { -// if (sensor.PLLineMappings.Contains(plLineType)) -// { -// double relativeLocation = sensor.RelativeLocationSpecified ? -// sensor.RelativeLocation : calculatedRelativeLocations[sensor]; -// -// if (table.ContainsKey(relativeLocation)) -// { -// throw new InvalidOperationException( -// "Error creating lookup table with sensors sorted by relative location along profile. The x-location " + relativeLocation + " already exists. Sensor " + sensor); -// } -// -// table.Add(relativeLocation, sensor); -// } -// } -// return table; -// } ##Bka + public SortedDictionary GetSensorsSortedByRelativeLocationAlongProfile(PlLineType plLineType) + { + var calculatedRelativeLocations = BuildRelativeLocationTable(); + var table = new SortedDictionary(); + + // leave out the water level and polder level sensors + var candidateSensors = + Sensors.GetBySpecification(new PiezometricHeadSensorSpecification()); + + foreach (var sensor in candidateSensors) + { + if (sensor.PlLineMappings.Contains(plLineType)) + { + double relativeLocation = sensor.RelativeLocationSpecified ? + sensor.RelativeLocation : calculatedRelativeLocations[sensor]; + + if (table.ContainsKey(relativeLocation)) + { + throw new InvalidOperationException( + "Error creating lookup table with sensors sorted by relative location along profile. The x-location " + relativeLocation + " already exists. Sensor " + sensor); + } + + table.Add(relativeLocation, sensor); + } + } + return table; + } + /// /// Builds the relative location table. /// @@ -366,8 +368,8 @@ internal static readonly string OffsetBelowShoulderBaseInside = StaticReflection.GetMemberName(x => x.Pl1PlLineOffsetBelowShoulderBaseInside); internal static readonly string WaterLevelAtRiver = StaticReflection.GetMemberName(x => x.Pl1WaterLevelAtRiver); internal static readonly string PolderLevel = StaticReflection.GetMemberName(x => x.Pl1WaterLevelAtPolder); - internal static readonly string PL3 = StaticReflection.GetMemberName(x => x.PL3); - internal static readonly string PL4 = StaticReflection.GetMemberName(x => x.PL4); + internal static readonly string Pl3 = StaticReflection.GetMemberName(x => x.Pl3); + internal static readonly string Pl4 = StaticReflection.GetMemberName(x => x.Pl4); } public double? GetValue(Expression> expression, IDictionary sensorValues, Sensor sensor) @@ -422,21 +424,21 @@ return sensorValues[sensor]; } - if (memberName == MemberNames.PL3) + if (memberName == MemberNames.Pl3) { - if (PL3 == DataSourceTypeSensors.LocationData) + if (Pl3 == DataSourceTypeSensors.LocationData) return Location.HeadPl3; - if (PL3 == DataSourceTypeSensors.Sensor) + if (Pl3 == DataSourceTypeSensors.Sensor) return sensorValues[sensor]; } - if (memberName == MemberNames.PL4) + if (memberName == MemberNames.Pl4) { - if (PL4 == DataSourceTypeSensors.LocationData) + if (Pl4 == DataSourceTypeSensors.LocationData) return Location.HeadPl4; - if (PL4 == DataSourceTypeSensors.Sensor) + if (Pl4 == DataSourceTypeSensors.Sensor) return sensorValues[sensor]; } Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/MinimumAttribute.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/MinimumAttribute.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/MinimumAttribute.cs (revision 1581) @@ -0,0 +1,41 @@ +// 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; + +namespace Deltares.DamEngine.Data.General.Specifications +{ + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = true)] + public sealed class MinimumAttribute : Attribute + { + private readonly double attributeValue; + + public MinimumAttribute(double minValue) + { + this.attributeValue = minValue; + } + + public double AttributeValue + { + get { return this.attributeValue; } + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine2Creator.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine2Creator.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine2Creator.cs (revision 1581) @@ -0,0 +1,41 @@ +// 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 Deltares.DamEngine.Data.General; +using Deltares.DamEngine.Data.General.PlLines; +using Deltares.DamEngine.Data.General.Sensors; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + internal class SensorPlLine2Creator : SensorPlLineCreatorBase + { + public SensorPlLine2Creator(SensorLocation sensorLocation, IDictionary sensorValues) : base(sensorLocation, PlLineType.Pl2, sensorValues) + { + } + + public override PlLine CreatePlLine() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/PiezometricHeadSensorSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/PiezometricHeadSensorSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/PiezometricHeadSensorSpecification.cs (revision 1581) @@ -0,0 +1,33 @@ +// 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 Deltares.DamEngine.Data.General.Specifications; + +namespace Deltares.DamEngine.Data.General.Sensors.Specifications +{ + internal class PiezometricHeadSensorSpecification : PredicateSpecification + { + public PiezometricHeadSensorSpecification() : + base(s => s.Type == SensorType.PiezometricHead) + { + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/GreaterThanOrEqualToSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/GreaterThanOrEqualToSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/GreaterThanOrEqualToSpecification.cs (revision 1581) @@ -0,0 +1,56 @@ +// 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; + +namespace Deltares.DamEngine.Data.General.Specifications +{ + /// + /// Defines a greater then of equal to specification + /// + /// The type of the candidate. + /// The type of the value. + public class GreaterThanOrEqualToSpecification : ValueBoundSpecification where TValue : IComparable + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the attribute. + /// The attribute value. + public GreaterThanOrEqualToSpecification(string attributeName, TValue attributeValue) + : base(attributeName, attributeValue) + { + } + + /// + /// Determines whether the candidate satisfies the specification. + /// + /// The candidate to test. + /// + /// true if the candidate satisfies the specification otherwise, false. + /// + public override bool IsSatisfiedBy(TCandidate candidate) + { + var actual = this.GetCandidateTValue(candidate) as IComparable; + return (actual.CompareTo(this.AttributeValue) >= 0); + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/PLLineConstructor.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/PLLineConstructor.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/PLLineConstructor.cs (revision 1581) @@ -0,0 +1,74 @@ +// 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.Collections.Generic; +using Deltares.DamEngine.Data.General; +using Deltares.DamEngine.Data.General.PlLines; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + /// + /// This class is responsible for creating a PL line where the coordinates are sorted on x. + /// No logic will be applied on how the PL is constructed. Just a simple data structure with an + /// insert method to insert new points. + /// + internal class PlLineConstructor + { + readonly IDictionary line = new SortedDictionary(); + + /// + /// Inserts the specified point. + /// + /// The point will be inserted in the line based on the X value + /// The point. + public void Insert(PlLinePoint point) + { + if (line.ContainsKey(point.X)) + line[point.X] = point; + else + line.Add(point.X, point); + } + + /// + /// Inserts the specified point. + /// + /// The point will be inserted in the line based on the X value + /// The x. + /// The z. + public void Insert(double x, double z) + { + Insert(new PlLinePoint(x, z)); + } + + /// + /// Gets the PL line from the internal constructed line. + /// + public PlLine CreatePlLine(PlLineType type) + { + var plLine = new PlLine() { PlLineType = type }; + foreach (var kvp in line) + { + plLine.Points.Add(kvp.Value); + } + return plLine; + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/UseLocationAsDataSource.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/UseLocationAsDataSource.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/Specifications/UseLocationAsDataSource.cs (revision 1581) @@ -0,0 +1,33 @@ +// 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 Deltares.DamEngine.Data.General.Specifications; + +namespace Deltares.DamEngine.Data.General.Sensors.Specifications +{ + public class UseLocationAsDataSource : PredicateSpecification + { + public UseLocationAsDataSource() : + base(x => x == DataSourceTypeSensors.LocationData) + { + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/Extensions/SpecificationExtensions.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/Extensions/SpecificationExtensions.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/Extensions/SpecificationExtensions.cs (revision 1581) @@ -0,0 +1,142 @@ +// 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 System.Reflection; +using Deltares.DamEngine.Data.Standard.Validation; + +namespace Deltares.DamEngine.Data.General.Specifications.Extensions +{ + public static class SpecificationExtensions + { + public static IEnumerable GetBySpecification(this IEnumerable collection, ISpecification specification) + { + return collection.Where(specification.IsSatisfiedBy); + } + + public static bool IsSatisfiedBySpecification(this T source, ISpecification specification) + { + return specification.IsSatisfiedBy(source); + } + + public static bool IsNotSatisfiedBySpecification(this T source, ISpecification specification) + { + return !IsSatisfiedBySpecification(source, specification); + } + + public static IEnumerable Validate(this T source, string groupName) + { + throw new NotImplementedException(); + } + + public static IEnumerable Validate(this T source) + { + var validationResults = new List(); + + foreach (var attribute in source.GetType().GetCustomAttributes(false)) + { + var specAttr = attribute as SpecificationBaseAttribute; + if (specAttr != null) + { + if (IsNotSatisfiedBySpecification(source, (ISpecification)specAttr.Specification)) + AddValidationResult(source, validationResults, specAttr); + } + } + + foreach (PropertyInfo property in source.GetType().GetProperties()) + { + foreach (object attribute in property.GetCustomAttributes(true)) + { + var specAttr = attribute as SpecificationBaseAttribute; + if (specAttr != null) + { + object candidateValue = property.GetValue(source, null); + if (!specAttr.Specification.IsSatisfiedBy(candidateValue)) + AddValidationResult(property, validationResults, specAttr); + } + else + { + var minAttr = attribute as MinimumAttribute; + if (minAttr != null) + { + var gte = + new GreaterThanOrEqualToSpecification(property.Name, minAttr.AttributeValue); + if (!gte.IsSatisfiedBy(source)) + { + validationResults.Add(new ValidationResult() + { + Subject = property, + MessageType = ValidationResultType.Error, + Text = string.Format("The property or field {0} has the value {1} which is less then the specification {2}", + property.Name, gte.CandidateValue, minAttr.AttributeValue) + }); + } + } + else + { + var maxAttr = attribute as MaximumAttribute; + if (maxAttr != null) + { + var lte = + new LessThanOrEqualToSpecification(property.Name, maxAttr.AttributeValue); + if (!lte.IsSatisfiedBy(source)) + { + validationResults.Add(new ValidationResult() + { + Subject = property, + MessageType = ValidationResultType.Error, + Text = string.Format("The property or field {0} has the value {1} which is greater then the specification {2}", + property.Name, lte.CandidateValue, maxAttr.AttributeValue) + }); + } + } + } + } + } + } + + return validationResults; + } + + private static void AddValidationResult(object subject, ICollection validationResults, SpecificationBaseAttribute specAttr) + { + string message = specAttr.NotSatisfiedText; + + if (string.IsNullOrWhiteSpace(message)) + message = specAttr.Specification.Description; + + if (string.IsNullOrWhiteSpace(message)) + message = specAttr.Specification.Name; + + if (string.IsNullOrWhiteSpace(message)) + message = specAttr.Specification.ToString(); + + validationResults.Add(new ValidationResult() + { + Subject = subject, + MessageType = ValidationResultType.Error, + Text = message + }); + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/MaximumAttribute.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/MaximumAttribute.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/MaximumAttribute.cs (revision 1581) @@ -0,0 +1,41 @@ +// 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; + +namespace Deltares.DamEngine.Data.General.Specifications +{ + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = true)] + public sealed class MaximumAttribute : Attribute + { + private readonly double attribteValue; + + public MaximumAttribute(double maxValue) + { + this.attribteValue = maxValue; + } + + public double AttributeValue + { + get { return this.attribteValue; } + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/PredicateSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/PredicateSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/PredicateSpecification.cs (revision 1581) @@ -0,0 +1,54 @@ +// 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; + +namespace Deltares.DamEngine.Data.General.Specifications +{ + public class PredicateSpecification : CompositeSpecification + { + private readonly Predicate predicate; + + public PredicateSpecification(Predicate predicate) + { + if (predicate == default(Predicate)) + throw new ArgumentNullException("predicate"); + + this.predicate = predicate; + } + + #region ISpecification Members + + /// + /// Determines whether the candidate satisfies the specification. + /// + /// The candidate. + /// + /// true if the candidate satisfies the specification otherwise, false. + /// + public override bool IsSatisfiedBy(T candidate) + { + return this.predicate(candidate); + } + + #endregion + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine3Or4CreatorBase.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine3Or4CreatorBase.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine3Or4CreatorBase.cs (revision 1581) @@ -0,0 +1,125 @@ +// 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.Collections.Generic; +using System.Linq; +using Deltares.DamEngine.Data.General; +using Deltares.DamEngine.Data.General.PlLines; +using Deltares.DamEngine.Data.General.Sensors; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + internal abstract class SensorPlLine3Or4CreatorBase : SensorPlLineCreatorBase + { + protected SensorPlLine3Or4CreatorBase(SensorLocation sensorLocation, IDictionary sensorValues, PlLineType PlLineType) + : base(sensorLocation, PlLineType, sensorValues) + { + } + + /// + /// Gets the start z level of the PL line + /// + /// + /// The start level is based on the PL3 or PL4 configuration (locationdata or sensor) + /// and the position of the first sensor + /// + /// + public override PlLine CreatePlLine() + { + var lineConstructor = new PlLineConstructor(); + + double currentLevel = 0; + + currentLevel = GetStartLevel(); + + // Add begin boundary + lineConstructor.Insert(new PlLinePoint(XBeginBoundary, currentLevel)); + + // Add point toe at river + lineConstructor.Insert(new PlLinePoint(XDikeToeAtRiver, currentLevel)); + + var firstSensor = SensorsSortedAlongProfile.FirstOrDefault(); + if (firstSensor != null) + { + bool skipFirstSensor = WaterLevelSensor.RelativeLocation > firstSensor.RelativeLocation; + + IEnumerable sensorsSortedAlongProfile = skipFirstSensor + ? SensorsSortedAlongProfile.Skip(1) + : SensorsSortedAlongProfile; + + foreach (var sensor in sensorsSortedAlongProfile) + { + var x = GetSensorXValue(sensor); + var z = currentLevel = GetSensorZValue(sensor); + + var point = new PlLinePoint(x, z) { Name = sensor.Name }; + + //Logger.LogDebug(string.Format("Point '{0}' added to PL Line", sensor.Name)); + + lineConstructor.Insert(point); + } + } + + // Add end boundary + lineConstructor.Insert(new PlLinePoint(XEndBoundary, currentLevel)); + + return lineConstructor.CreatePlLine(PlLineType); + } + + /// + /// Gets the water level at river. + /// + /// + /// If the position of the water level sensor is after the position of the + /// first sensor then the value of the latter is used + /// + public double WaterLevelAtRiver + { + get + { + var firstSensor = SensorsSortedAlongProfile.First(); + double level = SensorValues[firstSensor]; + + if (WaterLevelSensor.RelativeLocation < firstSensor.RelativeLocation) + level = SensorValues[WaterLevelSensor]; + + level = level - Correction; + + return level; + } + } + + /// + /// Gets or sets the (stijghoogtedemping) correction value. + /// + /// + /// The correction. + /// + protected double Correction { get; set; } + + + /// + /// Gets the start level. + /// + /// + abstract protected double GetStartLevel(); + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/LessThanOrEqualToSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/LessThanOrEqualToSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/LessThanOrEqualToSpecification.cs (revision 1581) @@ -0,0 +1,39 @@ +// 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; + +namespace Deltares.DamEngine.Data.General.Specifications +{ + public class LessThanOrEqualToSpecification : ValueBoundSpecification where TValue : IComparable + { + public LessThanOrEqualToSpecification(string attributeName, TValue attributeValue) + : base(attributeName, attributeValue) + { + } + + public override bool IsSatisfiedBy(TCandidate candidate) + { + var actual = this.GetCandidateTValue(candidate) as IComparable; + return (actual.CompareTo(this.AttributeValue) <= 0); + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Location.cs =================================================================== diff -u -r1289 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Location.cs (.../Location.cs) (revision 1289) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Location.cs (.../Location.cs) (revision 1581) @@ -1594,6 +1594,14 @@ public override string ToString() { return Name; - } + } + /// + /// Adds new sensor data. + /// + public void AddNewSensorData() + { + var factory = new SensorFactory(); + SensorData = factory.CreateSensorLocation(this); + } } } \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Standard/TypeHelper.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/Standard/TypeHelper.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Standard/TypeHelper.cs (revision 1581) @@ -0,0 +1,48 @@ +// 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; + +namespace Deltares.DamEngine.Data.Standard +{ + public static class TypeHelper + { + public static Type[] GetGenericArgumentsFromFirstGenericSuperClass(this Type type) + { + if (type.IsGenericType) + return type.GetGenericArguments(); + Type type1 = ((IEnumerable)type.GetInterfaces()).FirstOrDefault((Func)(i => + { + if (i.IsGenericType) + return i.GetGenericTypeDefinition() == typeof(IList<>); + return false; + })); + if (type1 != (Type)null) + return type1.GetGenericArguments(); + Type baseType = type.BaseType; + if (!(baseType == (Type)null)) + return baseType.GetGenericArgumentsFromFirstGenericSuperClass(); + return new Type[0]; + } + } +} Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/ValidationException.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/ValidationException.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/ValidationException.cs (revision 1581) @@ -0,0 +1,48 @@ +// 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.Runtime.Serialization; + +namespace Deltares.DamEngine.Data.General.Specifications +{ + [Serializable] + public class ValidationException : Exception + { + public ValidationException() + { + } + + public ValidationException(string message) : base(message) + { + } + + public ValidationException(string message, Exception inner) : base(message, inner) + { + } + + protected ValidationException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/NotSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/NotSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/NotSpecification.cs (revision 1581) @@ -0,0 +1,50 @@ +// 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. + +namespace Deltares.DamEngine.Data.General.Specifications +{ + public class NotSpecification : CompositeSpecification + { + private readonly ISpecification specification; + + public NotSpecification(ISpecification specification) + { + this.specification = specification; + } + + /// + /// Determines whether the candidate satisfies the specification. + /// + /// The candidate to test. + /// + /// true if the candidate satisfies the specification otherwise, false. + /// + public override bool IsSatisfiedBy(TCandidate candidate) + { + bool satisfied = !(this.specification.IsSatisfiedBy(candidate)); + if (!satisfied) + { + base.Description = this.specification.Description; + } + return satisfied; + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/Deltares.DamEngine.Calculators.csproj =================================================================== diff -u -r1497 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/Deltares.DamEngine.Calculators.csproj (.../Deltares.DamEngine.Calculators.csproj) (revision 1497) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/Deltares.DamEngine.Calculators.csproj (.../Deltares.DamEngine.Calculators.csproj) (revision 1581) @@ -66,7 +66,17 @@ + + + + + + + + + + Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine4Creator.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine4Creator.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine4Creator.cs (revision 1581) @@ -0,0 +1,72 @@ +// 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 Deltares.DamEngine.Data.General; +using Deltares.DamEngine.Data.General.Sensors; +using Deltares.DamEngine.Data.General.Sensors.Specifications; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + internal class SensorPlLine4Creator : SensorPlLine3Or4CreatorBase + { + public SensorPlLine4Creator(SensorLocation sensorLocation, IDictionary sensorValues) + : base(sensorLocation, sensorValues, PlLineType.Pl4) + { + } + + /// + /// Gets the start z level of the PL line + /// + /// + /// The start level is based on the Pl4 configuration (locationdata or sensor) + /// and the position of the first sensor + /// + /// + protected override double GetStartLevel() + { + var locationAsDataSource = new UseLocationAsDataSource(); + bool useLocationAsDataSource = locationAsDataSource.IsSatisfiedBy(SensorLocation.Pl4); + if (useLocationAsDataSource) + { + if (!SensorLocation.HeadPl4.HasValue) + throw new PlLinesCreatorException("Pl4 head has no value defined on this location"); + + return SensorLocation.HeadPl4.Value; + } + + double level; + + try + { + level = WaterLevelAtRiver; + } + catch (Exception e) + { + throw new PlLinesCreatorException("There was an error trying to read the water level", e); + } + + return level; + } + + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/AndSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/AndSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/AndSpecification.cs (revision 1581) @@ -0,0 +1,56 @@ +// 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. + +namespace Deltares.DamEngine.Data.General.Specifications +{ + /// + /// The and specification is a compositable one. + /// + /// The type of the candidate. + public class AndSpecification : CompositeSpecification + { + private readonly ISpecification one; + private readonly ISpecification other; + + public AndSpecification(ISpecification one, ISpecification other) + { + this.one = one; + this.other = other; + } + + /// + /// Determines whether this candidate satisfies the specification AND the other. + /// + /// The candidate. + /// + /// true if the candidate satisfies the specification otherwise, false. + /// + public override bool IsSatisfiedBy(TCandidate candidate) + { + bool satisfied = (this.one.IsSatisfiedBy(candidate) && this.other.IsSatisfiedBy(candidate)); + if (!satisfied) + { + base.Description = this.Description; + } + return satisfied; + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Sensors/SensorLocationTests.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Sensors/SensorLocationTests.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Sensors/SensorLocationTests.cs (revision 1581) @@ -0,0 +1,522 @@ +// 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.Collections.Generic; +using Deltares.DamEngine.Data.General; +using Deltares.DamEngine.Data.General.Sensors; +using NUnit.Framework; + +namespace Deltares.DamEngine.Calculators.Tests.Sensors +{ + [TestFixture] + public class SensorLocationTest + { + readonly Location location = new Location(); + + #region Setup + + [TestFixtureSetUp] + public void FixtureSetup() + { + } + + [TestFixtureTearDown] + public void FixtureTearDown() + { + } + + [SetUp] + public void TestSetup() + { + } + + [TearDown] + public void TestTearDown() + { + } + + #endregion + + + #region Sensor Or Location Data Value Tests + + [Test] + public void GetValue_Pl1PlLineOffsetBelowDikeToeAtPolderAndDataSourceTypeIgnore_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { {sensor, sensorValue}}; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1PlLineOffsetBelowDikeToeAtPolder = DataSourceTypeSensors.Ignore; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowDikeToeAtPolder, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_Pl1PlLineOffsetBelowDikeToeAtPolderAndDataSourceTypeLocationData_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double testValue = 1.1; + var sensorValues = new Dictionary { { sensor, testValue } }; + var sensorLocation = CreateValidSensorLocation(); + location.PlLineOffsetBelowDikeToeAtPolder = testValue; + + sensorLocation.Pl1PlLineOffsetBelowDikeToeAtPolder = DataSourceTypeSensors.LocationData; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowDikeToeAtPolder, sensorValues, sensor); + + // assert + Assert.AreEqual(testValue, actualValue); + } + + [Test] + public void GetValue_Pl1PlLineOffsetBelowDikeToeAtPolderAndDataSourceTypeSensor_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1PlLineOffsetBelowDikeToeAtPolder = DataSourceTypeSensors.Sensor; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowDikeToeAtPolder, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_Pl1PLLineOffsetBelowDikeTopAtPolderAndDataSourceTypeIgnore_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1PlLineOffsetBelowDikeTopAtPolder = DataSourceTypeSensors.Ignore; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowDikeTopAtPolder, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_Pl1PLLineOffsetBelowDikeTopAtPolderAndDataSourceTypeLocationData_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double testValue = 1.1; + var sensorValues = new Dictionary { { sensor, testValue } }; + var sensorLocation = CreateValidSensorLocation(); + location.PlLineOffsetBelowDikeTopAtPolder = testValue; + + sensorLocation.Pl1PlLineOffsetBelowDikeTopAtPolder = DataSourceTypeSensors.LocationData; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowDikeTopAtPolder, sensorValues, sensor); + + // assert + Assert.AreEqual(testValue, actualValue); + } + + [Test] + public void GetValue_Pl1PLLineOffsetBelowDikeTopAtPolderAndDataSourceTypeSensor_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1PlLineOffsetBelowDikeTopAtPolder = DataSourceTypeSensors.Sensor; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowDikeTopAtPolder, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_Pl1PlLineOffsetBelowDikeTopAtRiverAndDataSourceTypeIgnore_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.Ignore; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowDikeTopAtRiver, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_Pl1PlLineOffsetBelowDikeTopAtRiverAndDataSourceTypeLocationData_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double testValue = 1.1; + var sensorValues = new Dictionary { { sensor, testValue } }; + var sensorLocation = CreateValidSensorLocation(); + location.PlLineOffsetBelowDikeTopAtRiver = testValue; + + sensorLocation.Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.LocationData; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowDikeTopAtRiver, sensorValues, sensor); + + // assert + Assert.AreEqual(testValue, actualValue); + } + + [Test] + public void GetValue_Pl1PlLineOffsetBelowDikeTopAtRiverAndDataSourceTypeSensor_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.Sensor; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowDikeTopAtRiver, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_Pl1PLLineOffsetBelowShoulderBaseInsideAndDataSourceTypeIgnore_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.Ignore; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowShoulderBaseInside, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_Pl1PLLineOffsetBelowShoulderBaseInsideAndDataSourceTypeLocationData_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double testValue = 1.1; + var sensorValues = new Dictionary { { sensor, testValue } }; + var sensorLocation = CreateValidSensorLocation(); + location.PlLineOffsetBelowShoulderBaseInside = testValue; + + sensorLocation.Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.LocationData; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowShoulderBaseInside, sensorValues, sensor); + + // assert + Assert.AreEqual(testValue, actualValue); + } + + [Test] + public void GetValue_Pl1PLLineOffsetBelowShoulderBaseInsideAndDataSourceTypeSensor_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.Sensor; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1PlLineOffsetBelowShoulderBaseInside, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_WaterLevelAtPolderAndDataSourceTypeIgnore_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1WaterLevelAtPolder = DataSourceTypeSensors.Ignore; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1WaterLevelAtPolder, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_WaterLevelAtPolderAndDataSourceTypeLocationData_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double testValue = 1.1; + var sensorValues = new Dictionary { { sensor, testValue } }; + var sensorLocation = CreateValidSensorLocation(); + location.PolderLevel = testValue; + + sensorLocation.Pl1WaterLevelAtPolder = DataSourceTypeSensors.LocationData; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1WaterLevelAtPolder, sensorValues, sensor); + + // assert + Assert.AreEqual(testValue, actualValue); + } + + [Test] + public void GetValue_WaterLevelAtPolderAndDataSourceTypeSensor_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double expectedValue = 1.1; + var sensorValues = new Dictionary { { sensor, expectedValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1WaterLevelAtPolder = DataSourceTypeSensors.Sensor; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1WaterLevelAtPolder, sensorValues, sensor); + + // assert + Assert.AreEqual(expectedValue, actualValue); + } + + [Test] + public void GetValue_WaterLevelAtRiverAndDataSourceTypeIgnore_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1WaterLevelAtRiver = DataSourceTypeSensors.Ignore; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1WaterLevelAtRiver, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_WaterLevelAtRiverAndDataSourceTypeLocationData_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double testValue = 1.1; + var sensorValues = new Dictionary { { sensor, testValue } }; + var sensorLocation = CreateValidSensorLocation(); + + location.RiverLevel = testValue; + Assert.AreEqual(testValue, location.RiverLevel); + + sensorLocation.Pl1WaterLevelAtRiver = DataSourceTypeSensors.LocationData; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1WaterLevelAtRiver, sensorValues, sensor); + + // assert + Assert.AreEqual(testValue, actualValue); + } + + [Test] + public void GetValue_WaterLevelAtRiverAndDataSourceTypeSensor_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double expectedValue = 1.1; + var sensorValues = new Dictionary { { sensor, expectedValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl1WaterLevelAtRiver = DataSourceTypeSensors.Sensor; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl1WaterLevelAtRiver, sensorValues, sensor); + + // assert + Assert.AreEqual(expectedValue, actualValue); + } + + [Test] + public void GetValue_Pl3AndDataSourceTypeIgnore_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl3 = DataSourceTypeSensors.Ignore; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl3, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_Pl3AndDataSourceTypeLocationData_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double testValue = 1.1; + var sensorValues = new Dictionary { { sensor, testValue } }; + var sensorLocation = CreateValidSensorLocation(); + + location.HeadPl3 = testValue; + sensorLocation.Pl3 = DataSourceTypeSensors.LocationData; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl3, sensorValues, sensor); + + // assert + Assert.AreEqual(testValue, actualValue); + } + + [Test] + public void GetValue_Pl3AndDataSourceTypeSensor_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double expectedValue = 1.1; + var sensorValues = new Dictionary { { sensor, expectedValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl3 = DataSourceTypeSensors.Sensor; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl3, sensorValues, sensor); + + // assert + Assert.AreEqual(expectedValue, actualValue); + } + + [Test] + public void GetValue_Pl4AndDataSourceTypeIgnore_ShouldReturnNull() + { + // setup + var sensor = new Sensor(); + const double sensorValue = 1.1; + var sensorValues = new Dictionary { { sensor, sensorValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl4 = DataSourceTypeSensors.Ignore; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl4, sensorValues, sensor); + + // assert + Assert.IsNull(actualValue); + } + + [Test] + public void GetValue_Pl4AndDataSourceTypeLocationData_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double testValue = 1.1; + var sensorValues = new Dictionary { { sensor, testValue } }; + var sensorLocation = CreateValidSensorLocation(); + + location.HeadPl4 = testValue; + sensorLocation.Pl4 = DataSourceTypeSensors.LocationData; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl4, sensorValues, sensor); + + // assert + Assert.AreEqual(testValue, actualValue); + } + + [Test] + public void GetValue_Pl4AndDataSourceTypeSensor_ShouldReturnValue() + { + // setup + var sensor = new Sensor(); + const double expectedValue = 1.1; + var sensorValues = new Dictionary { { sensor, expectedValue } }; + var sensorLocation = CreateValidSensorLocation(); + + sensorLocation.Pl4 = DataSourceTypeSensors.Sensor; + + // call + var actualValue = sensorLocation.GetValue(x => x.Pl4, sensorValues, sensor); + + // assert + Assert.AreEqual(expectedValue, actualValue); + } + + #endregion + + + + #region Helper methods + + /// + /// Creates and initializes a valid sensor location for testing. + /// + /// + private SensorLocation CreateValidSensorLocation() + { + var factory = new SensorFactory(); + var sensorLocation = factory.CreateSensorLocation(location); + return sensorLocation; + } + + #endregion + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Sensors/SensorTest.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Sensors/SensorTest.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/Sensors/SensorTest.cs (revision 1581) @@ -0,0 +1,81 @@ +// 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 Deltares.DamEngine.Data.General.Sensors; +using NUnit.Framework; + +namespace Deltares.DamEngine.Calculators.Tests.Sensors +{ + [TestFixture] + public class SensorTest + { + #region Setup + + [TestFixtureSetUp] + public void FixtureSetup() + { + } + + [TestFixtureTearDown] + public void FixtureTearDown() + { + } + + [SetUp] + public void TestSetup() + { + } + + [TearDown] + public void TestTearDown() + { + } + + #endregion + + [Test] + public void IsTransient_DefaultConstructedSensorAndNotSettingIDExplicitly_ShouldBeTrue() + { + // setup + var sensor = new Sensor(); + + // call + bool actual = sensor.IsTransient(); + + // assert + Assert.IsTrue(actual); + } + + [Test] + public void IsTransient_DefaultSensorConstructedWithValidID_ShouldBeFalse() + { + // setup + var sensor = new Sensor { ID = 1 }; // ID should be greater then 0 + + // call + bool actual = sensor.IsTransient(); + + // assert + Assert.IsFalse(actual); + } + + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorFactory.cs =================================================================== diff -u -r1122 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorFactory.cs (.../SensorFactory.cs) (revision 1122) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorFactory.cs (.../SensorFactory.cs) (revision 1581) @@ -37,6 +37,18 @@ return CreateSensorLocation(location, new Group()); } + internal Sensor CreateUniqueSensor(IEnumerable sensors) + { + if (sensors == null) throw new ArgumentNullException("sensors"); + + // TODO: see of these calls can be combined for performance reason + var id = sensors.GetUniqueID(x => x.ID); + + Sensor sensor = new Sensor { ID = id, Type = SensorType.PiezometricHead }; + UniqueNameProvider.ProvideUniqueName(new List(sensors), sensor); + + return sensor; + } /// /// Creates a sensor location. /// @@ -55,8 +67,8 @@ Pl1PlLineOffsetBelowDikeTopAtRiver = DataSourceTypeSensors.LocationData, Pl1PlLineOffsetBelowShoulderBaseInside = DataSourceTypeSensors.LocationData, Pl1WaterLevelAtPolder = DataSourceTypeSensors.LocationData, - PL3 = DataSourceTypeSensors.LocationData, - PL4 = DataSourceTypeSensors.LocationData + Pl3 = DataSourceTypeSensors.LocationData, + Pl4 = DataSourceTypeSensors.LocationData }; } } Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Standard/TypeExtensions.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/Standard/TypeExtensions.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Standard/TypeExtensions.cs (revision 1581) @@ -0,0 +1,36 @@ +// 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.Reflection; + +namespace Deltares.DamEngine.Data.Standard +{ + public static class TypeExtensions + { + public static bool HasDefaultConstructor(this Type type) + { + if (type.IsValueType) + return true; + return type.GetConstructor(Type.EmptyTypes) != (ConstructorInfo)null; + } + } +} Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/ValueBoundSpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/ValueBoundSpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/ValueBoundSpecification.cs (revision 1581) @@ -0,0 +1,91 @@ +// 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. + +namespace Deltares.DamEngine.Data.General.Specifications +{ + /// + /// With this class a value of an object bound to a property can be tested against the specification. + /// This is a variant of the ParameterizedSpecification + /// + /// The candidate type + /// The candidate property type + public abstract class ValueBoundSpecification : SpecificationBase + { + private readonly string attributeName; + private readonly TValue attributeValue; + private TValue candidateValue; + + protected ValueBoundSpecification(string attributeName, TValue attributeValue) + { + this.attributeName = attributeName; + this.attributeValue = attributeValue; + } + + /// + /// Gets the attribute value. + /// + public TValue AttributeValue + { + get { return attributeValue; } + } + + /// + /// Gets the candidate value. + /// + public TValue CandidateValue + { + get { return candidateValue; } + } + + /// + /// Gets the candidate string value. + /// + /// The candidate. + /// + protected string GetCandidateStringValue(TCandidate candidate) + { + return GetCandidateObjectValue(candidate).ToString(); + } + + /// + /// Gets the candidate T value. + /// + /// The candidate. + /// + protected TValue GetCandidateTValue(TCandidate candidate) + { + if (candidateValue.Equals(default(TValue))) + candidateValue = (TValue) GetCandidateObjectValue(candidate); + + return candidateValue; + } + + /// + /// Gets the candidate object value. + /// + /// The candidate. + /// + private object GetCandidateObjectValue(TCandidate candidate) + { + return candidate.GetType().GetProperty(attributeName).GetValue(candidate, null); + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorRepository.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorRepository.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorRepository.cs (revision 1581) @@ -0,0 +1,153 @@ +// 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 Deltares.DamEngine.Data.Standard; + +namespace Deltares.DamEngine.Data.General.Sensors +{ + public class SensorRepository + { + private SensorLocation sensorLocation; + private readonly HashSet names; + private readonly HashSet ids; + + private SensorRepository() + { + names = new HashSet(); + ids = new HashSet(); + } + + public SensorRepository(Location location) : this() + { + if (location.SensorData == null) + { + location.AddNewSensorData(); + } + + sensorLocation = location.SensorData; + + if (sensorLocation.Group == null) + sensorLocation.Group = new Group(); + } + + internal Group Group + { + get { return sensorLocation.Group; } + } + + public IEnumerable Sensors + { + get { return Group.Selection; } + } + + internal int GetUniqueSensorId() + { + return Group.Selection.GetUniqueID(x => x.ID); + } + + /// + /// Adds a new sensor to the location sensor group. + /// + /// The created Sensor + public Sensor AddNew() + { + var factory = new SensorFactory(); + Sensor sensor = factory.CreateUniqueSensor(Group.Selection); + AddSensor(sensor); + return sensor; + } + + /// + /// Adds the collection of sensors to the group. + /// + /// The sensor needs to meet the business rules before it can be added + /// The sensor collection to add to the group of this location. + /// + /// + public void Add(IEnumerable sensors) + { + if (sensors == null) throw new ArgumentNullException("sensors"); + foreach (var sensor in sensors) + Add(sensor); + } + + /// + /// Adds the specified sensor. + /// + /// The sensor needs to meet the business rules before it can be added + /// The sensor to add. + /// + /// + public void Add(Sensor sensor) + { + if (sensor == null) + throw new ArgumentNullException("sensor"); + + if (string.IsNullOrWhiteSpace(sensor.Name)) + throw new ArgumentException("Cant add this sensor because its name is empty"); + + if (sensor.IsTransient()) + throw new ArgumentException("Cant add this sensor because its ID is not valid. The ID must be greater then -1"); + + if (ids.Contains(sensor.ID)) + throw new ArgumentException("Cant add this sensor becuase its ID is already added"); + + if (names.Contains(sensor.Name)) + throw new ArgumentException("Cant add this sensor because its name is already used"); + + AddSensor(sensor); + } + + /// + /// Adds the sensor unconditionally + /// + /// The sensor. + private void AddSensor(Sensor sensor) + { + ids.Add(sensor.ID); + names.Add(sensor.Name); + sensorLocation.Group.Add(sensor); + } + + /// + /// Serializes the state of this repository to a XML string. + /// + /// + internal string Serialize() + { + throw new NotImplementedException(); + // return sensorLocation.Serialize(); + } + + /// + /// Deserializes the specified XML to restore the state of this repository + /// + /// The XML. + internal static SensorRepository Deserialize(string xml) + { + throw new NotImplementedException(); +// if (string.IsNullOrWhiteSpace(xml)) throw new ArgumentException("xml"); +// return new SensorRepository() { sensorLocation = SensorLocation.Deserialize(xml) }; + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Deltares.DamEngine.Data.csproj =================================================================== diff -u -r1497 -r1581 --- DamEngine/trunk/src/Deltares.DamEngine.Data/Deltares.DamEngine.Data.csproj (.../Deltares.DamEngine.Data.csproj) (revision 1497) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Deltares.DamEngine.Data.csproj (.../Deltares.DamEngine.Data.csproj) (revision 1581) @@ -77,8 +77,29 @@ + + + + + + + + + + + + + + + + + + + + + @@ -163,6 +184,9 @@ + + + Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/ISpecification.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/ISpecification.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Specifications/ISpecification.cs (revision 1581) @@ -0,0 +1,70 @@ +// 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. + +namespace Deltares.DamEngine.Data.General.Specifications +{ + /// + /// + /// + public interface ISpecification + { + /// + /// Gets or sets the name of the specification. (Short description) + /// + /// + /// The name string value should be a required property for each specification. + /// + string Name { get; set; } + + /// + /// Gets or sets the description of the specification. (Long description) + /// + /// + /// The description string value. + /// + string Description { get; set; } + + /// + /// Determines whether the candidate satisfies the specification. + /// + /// The candidate. + /// + /// true if the candidate satisfies the specification otherwise, false. + /// + bool IsSatisfiedBy(object candidate); + } + + /// + /// + /// + /// The type of the candidate. + public interface ISpecification : ISpecification + { + /// + /// Determines whether the candidate satisfies the specification. + /// + /// The candidate. + /// + /// true if the candidate satisfies the specification otherwise, false. + /// + bool IsSatisfiedBy(TCandidate candidate); + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/IPlLineCreator.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/IPlLineCreator.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/IPlLineCreator.cs (revision 1581) @@ -0,0 +1,30 @@ +// 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 Deltares.DamEngine.Data.General.PlLines; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + public interface IPlLineCreator + { + PlLine CreatePlLine(); + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine3Creator.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine3Creator.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine3Creator.cs (revision 1581) @@ -0,0 +1,76 @@ +// 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 Deltares.DamEngine.Data.General.Sensors; +using Deltares.DamEngine.Data.General; +using Deltares.DamEngine.Data.General.Sensors.Specifications; + +namespace Deltares.DamEngine.Calculators.PlLinesCreator +{ + internal class SensorPlLine3Creator : SensorPlLine3Or4CreatorBase + { + /// + /// Initializes a new instance of the class. + /// + /// The sensor location. + /// The sensor values. + public SensorPlLine3Creator(SensorLocation sensorLocation, IDictionary sensorValues) + : base(sensorLocation, sensorValues, PlLineType.Pl3) + { + } + + /// + /// Gets the start z level of the PL line + /// + /// + /// The start level is based on the Pl3 configuration (location data or sensor) + /// and the position of the first sensor + /// + /// + protected override double GetStartLevel() + { + double currentLevel; + bool useLocationAsDataSource = new UseLocationAsDataSource().IsSatisfiedBy(SensorLocation.Pl3); + if (useLocationAsDataSource) + { + if (!SensorLocation.HeadPl3.HasValue) + throw new PlLinesCreatorException("Pl3 head has no value defined on this location"); + + currentLevel = SensorLocation.HeadPl3.Value; + } + else + { + try + { + currentLevel = WaterLevelAtRiver; + } + catch (Exception e) + { + throw new PlLinesCreatorException("There was an error trying to read the water level", e); + } + } + + return currentLevel; + } + } +} \ No newline at end of file