// Copyright (C) Stichting Deltares 2024. 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) {} /// /// Gets the polder level. /// public double PolderLevel { get { Sensor sensor = PolderLevelSensor; if (sensor != null) { double? value = SensorLocation.GetValue(x => x.SourceTypePl1WaterLevelAtPolder, SensorValues, sensor); if (value.HasValue) { return value.Value; } var message = $"Polder level at river for the location '{SensorLocation.LocationName}' was not set or initialized"; throw new PlLinesCreatorException(message); } 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 Polders the level intersection at X dike side. /// /// The polder level. /// public double? GetDitchWaterLevelIntersectionAtXDikeSide(double level) { GeometryPoint 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"); } GeometryPoint 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"); } GeometryPoint 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) { GeometryPoint 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"); } GeometryPoint 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"); } GeometryPoint 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; } /// /// 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); } // Add begin boundary lineConstructor.Insert(new PlLinePoint(XBeginBoundary, waterLevel)); // add all sensors to the pl line List sortedSensors = SensorsSortedAlongProfile.ToList(); double lastZ = waterLevel; double lastX = XBeginBoundary; foreach (Sensor sensor in sortedSensors) { double x = lastX = GetSensorXValue(sensor); double z = lastZ = GetSensorZValue(sensor); var point = new PlLinePoint(x, z) { Name = sensor.Name }; lineConstructor.Insert(point); } // insert intersection point (at dike river side) double? xIntersection = IntersectionXAtRiverWaterLevel(waterLevel); if (xIntersection != null) { lineConstructor.Insert(new PlLinePoint(xIntersection.Value, waterLevel) { Name = "Intersection point dike river side" }); } else { GeometryPoint dikeToeAtRiver = SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver); var point = new PlLinePoint(dikeToeAtRiver.X, dikeToeAtRiver.Z); lineConstructor.Insert(point); } // insert offset below dike top at river point? var useLocationAsDataSource = new UseLocationAsDataSource(); if (useLocationAsDataSource.IsSatisfiedBy(SensorLocation.SourceTypePl1PlLineOffsetBelowDikeTopAtRiver)) { double offset = waterLevel - SensorLocation.PlLineOffsetBelowDikeTopAtRiver; lineConstructor.Insert(new PlLinePoint(XDikeTopAtRiver, offset)); } // insert offset below dike top at polder point? if (useLocationAsDataSource.IsSatisfiedBy(SensorLocation.SourceTypePl1PlLineOffsetBelowDikeTopAtPolder)) { double offset = waterLevel - SensorLocation.PlLineOffsetBelowDikeTopAtPolder; double x = XDikeTopAtPolder; lineConstructor.Insert(new PlLinePoint(x, offset)); } // insert offset below shoulder base inside? if (useLocationAsDataSource.IsSatisfiedBy(SensorLocation.SourceTypePl1PlLineOffsetBelowShoulderBaseInside)) { double offset = lastZ = ZShouldeBaseInside - SensorLocation.PlLineOffsetDryBelowShoulderBaseInside; lineConstructor.Insert(new PlLinePoint(XShoulderBaseInside, offset)); } // insert offset below dike toe at polder? if (useLocationAsDataSource.IsSatisfiedBy(SensorLocation.SourceTypePl1PlLineOffsetBelowDikeToeAtPolder)) { double offset = lastZ = ZDikeToeAtPolder - SensorLocation.PlLineOffsetBelowDikeToeAtPolder; double x = XDikeToeAtPolder; lineConstructor.Insert(new PlLinePoint(x, offset)); if (lastX < x) { lastX = x; } } if (DikeHasDitch) { Sensor ditchWaterLevelSensor = SensorLocation.Sensors .GetBySpecification(new DitchWaterLevelSensorSpecification(SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchDikeSide).X, SensorLocation.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DitchPolderSide).X)) .FirstOrDefault(); double 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.SourceTypePl1PlLineOffsetBelowDikeToeAtPolder)) { depth = ZDikeToeAtPolder - SensorLocation.PlLineOffsetBelowDikeToeAtPolder; } else { Sensor 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 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); } double? value = SensorLocation.GetValue(x => x.SourceTypePl1WaterLevelAtRiver, SensorValues, sensor); if (value.HasValue) { return value.Value; } var message = $"Water level at river for the location '{SensorLocation.LocationName}' was not set or initialized"; throw new PlLinesCreatorException(message); } } }