// 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);
}
}
}