// Copyright (C) Stichting Deltares 2016. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using Core.Common.Base;
using Core.Common.Base.Data;
using Core.Common.Base.Geometry;
using Ringtoets.Common.Data.Calculation;
using Ringtoets.Common.Data.DikeProfiles;
using Ringtoets.HydraRing.Data;
using Ringtoets.Revetment.Data.Properties;
using RingtoetsCommonDataResources = Ringtoets.Common.Data.Properties.Resources;
namespace Ringtoets.Revetment.Data
{
///
/// Class that holds all wave conditions calculation specific input parameters.
///
public class WaveConditionsInput : Observable, ICalculationInput, IUseBreakWater, IUseForeshore
{
private const double designWaterLevelSubstraction = 0.01;
private ForeshoreProfile foreshoreProfile;
private RoundedDouble upperBoundaryRevetment;
private RoundedDouble lowerBoundaryRevetment;
private RoundedDouble upperBoundaryWaterLevels;
private RoundedDouble lowerBoundaryWaterLevels;
private RoundedDouble orientation;
///
/// Creates a new instance of .
///
public WaveConditionsInput()
{
orientation = new RoundedDouble(2);
upperBoundaryRevetment = new RoundedDouble(2, double.NaN);
lowerBoundaryRevetment = new RoundedDouble(2, double.NaN);
StepSize = WaveConditionsInputStepSize.Half;
upperBoundaryWaterLevels = new RoundedDouble(2, double.NaN);
lowerBoundaryWaterLevels = new RoundedDouble(2, double.NaN);
UpdateForeshoreProfileParameters();
}
///
/// Gets or sets the hydraulic boundary location from which to use the assessment level.
///
public HydraulicBoundaryLocation HydraulicBoundaryLocation { get; set; }
///
/// Gets the assessment level from the current hydraulic boundary location, or if there is no
/// location selected.
///
public RoundedDouble AssessmentLevel
{
get
{
return HydraulicBoundaryLocation != null ? HydraulicBoundaryLocation.DesignWaterLevel : new RoundedDouble(2, double.NaN);
}
}
///
/// Gets or sets the foreshore profile.
///
public ForeshoreProfile ForeshoreProfile
{
get
{
return foreshoreProfile;
}
set
{
foreshoreProfile = value;
UpdateForeshoreProfileParameters();
}
}
///
/// Gets or sets the orientation of the foreshore profile geometry with respect to North
/// in degrees. A positive value equals a clockwise rotation.
///
///Thrown when the value of the orientation
/// is not in the interval [0, 360].
public RoundedDouble Orientation
{
get
{
return orientation;
}
set
{
RoundedDouble newOrientation = value.ToPrecision(orientation.NumberOfDecimalPlaces);
if (!double.IsNaN(newOrientation) && (newOrientation < 0 || newOrientation > 360))
{
throw new ArgumentOutOfRangeException("value", RingtoetsCommonDataResources.Orientation_Value_needs_to_be_between_0_and_360);
}
orientation = newOrientation;
}
}
///
/// Gets the upper boundary based on .
///
public RoundedDouble UpperBoundaryDesignWaterLevel
{
get
{
return new RoundedDouble(2, AssessmentLevel - designWaterLevelSubstraction);
}
}
///
/// Gets or sets the lower boundary of the revetment.
///
/// Thrown when value is larger than or equal to .
/// When the value is smaller than -50, it will be set to -50.
public RoundedDouble LowerBoundaryRevetment
{
get
{
return lowerBoundaryRevetment;
}
set
{
RoundedDouble newLowerBoundaryRevetment = value.ToPrecision(lowerBoundaryRevetment.NumberOfDecimalPlaces);
newLowerBoundaryRevetment = ValidateLowerBoundaryInRange(newLowerBoundaryRevetment);
ValidateRevetmentBoundaries(newLowerBoundaryRevetment, UpperBoundaryRevetment);
lowerBoundaryRevetment = newLowerBoundaryRevetment;
}
}
///
/// Gets or sets the upper boundary of the revetment.
///
/// Thrown when value is smaller than or equal to .
/// When the value is larger than 1000, it will be set to 1000.
public RoundedDouble UpperBoundaryRevetment
{
get
{
return upperBoundaryRevetment;
}
set
{
RoundedDouble newUpperBoundaryRevetment = value.ToPrecision(upperBoundaryRevetment.NumberOfDecimalPlaces);
newUpperBoundaryRevetment = ValidateUpperBoundaryInRange(newUpperBoundaryRevetment);
ValidateRevetmentBoundaries(LowerBoundaryRevetment, newUpperBoundaryRevetment);
upperBoundaryRevetment = newUpperBoundaryRevetment;
}
}
///
/// Gets or sets the step size used for determining .
///
public WaveConditionsInputStepSize StepSize { get; set; }
///
/// Gets or sets the lower boundary of the range.
///
/// Thrown when value is larger than or equal to .
///
///
/// - Setting this property is optional when it comes to determining ; if the value
/// equals , only will be taken into account.
/// - When the value is smaller than -50, it will be set to -50.
///
///
public RoundedDouble LowerBoundaryWaterLevels
{
get
{
return lowerBoundaryWaterLevels;
}
set
{
RoundedDouble newLowerBoundaryWaterLevels = value.ToPrecision(lowerBoundaryWaterLevels.NumberOfDecimalPlaces);
newLowerBoundaryWaterLevels = ValidateLowerBoundaryInRange(newLowerBoundaryWaterLevels);
ValidateWaterLevelBoundaries(newLowerBoundaryWaterLevels, UpperBoundaryWaterLevels);
lowerBoundaryWaterLevels = newLowerBoundaryWaterLevels;
}
}
///
/// Gets or sets the upper boundary of the range.
///
/// Thrown when value is smaller than or equal to .
///
///
/// - Setting this property is optional when it comes to determining ; if the value
/// equals , only and
/// will be taken into account.
/// - When the value is larger than 1000, it will be set to 1000.
///
///
public RoundedDouble UpperBoundaryWaterLevels
{
get
{
return upperBoundaryWaterLevels;
}
set
{
RoundedDouble newUpperBoundaryWaterLevels = value.ToPrecision(upperBoundaryWaterLevels.NumberOfDecimalPlaces);
newUpperBoundaryWaterLevels = ValidateUpperBoundaryInRange(newUpperBoundaryWaterLevels);
ValidateWaterLevelBoundaries(LowerBoundaryWaterLevels, newUpperBoundaryWaterLevels);
upperBoundaryWaterLevels = newUpperBoundaryWaterLevels;
}
}
///
/// Gets the water levels to perform a wave conditions calculation for.
///
public IEnumerable WaterLevels
{
get
{
return DetermineWaterLevels();
}
}
///
/// Gets or sets whether needs to be taken into account.
///
public bool UseBreakWater { get; set; }
///
/// Gets the .
///
public BreakWater BreakWater { get; private set; }
///
/// Gets or sets whether the needs to be taken into account.
///
public bool UseForeshore { get; set; }
///
/// Gets the geometry of the foreshore.
///
public RoundedPoint2DCollection ForeshoreGeometry
{
get
{
return foreshoreProfile != null
? foreshoreProfile.Geometry
: new RoundedPoint2DCollection(2, Enumerable.Empty());
}
}
private static RoundedDouble ValidateUpperBoundaryInRange(RoundedDouble boundary)
{
if (boundary > 1000)
{
boundary = new RoundedDouble(boundary.NumberOfDecimalPlaces, 1000);
}
return boundary;
}
private static RoundedDouble ValidateLowerBoundaryInRange(RoundedDouble boundary)
{
if (boundary < -50)
{
boundary = new RoundedDouble(boundary.NumberOfDecimalPlaces, -50);
}
return boundary;
}
private static void ValidateRevetmentBoundaries(RoundedDouble lowerBoundary, RoundedDouble upperBoundary)
{
ValidateBoundaries(lowerBoundary, upperBoundary, Resources.WaveConditionsInput_ValidateRevetmentBoundaries_Upper_boundary_revetment_must_be_above_lower_boundary_revetment);
}
private static void ValidateWaterLevelBoundaries(RoundedDouble lowerBoundary, RoundedDouble upperBoundary)
{
ValidateBoundaries(lowerBoundary, upperBoundary, Resources.WaveConditionsInput_ValidateWaterLevelBoundaries_Upper_boundary_water_levels_must_be_above_lower_boundary_water_levels);
}
private static void ValidateBoundaries(RoundedDouble lowerBoundary, RoundedDouble upperBoundary, string exceptionMessage)
{
if (!double.IsNaN(lowerBoundary)
&& !double.IsNaN(upperBoundary)
&& lowerBoundary >= upperBoundary)
{
throw new ArgumentOutOfRangeException(null, exceptionMessage);
}
}
private IEnumerable DetermineWaterLevels()
{
var waterLevels = new List();
var upperBoundary = new RoundedDouble(2, Math.Min(UpperBoundaryDesignWaterLevel,
Math.Min(UpperBoundaryRevetment,
!double.IsNaN(UpperBoundaryWaterLevels)
? UpperBoundaryWaterLevels
: double.MaxValue)));
var lowerBoundary = new RoundedDouble(2, Math.Max(LowerBoundaryRevetment,
!double.IsNaN(LowerBoundaryWaterLevels)
? LowerBoundaryWaterLevels
: double.MinValue));
if (double.IsNaN(upperBoundary)
|| double.IsNaN(lowerBoundary)
|| (lowerBoundary >= upperBoundary))
{
return waterLevels;
}
waterLevels.Add(upperBoundary);
double stepSizeValue = StepSize.AsValue();
RoundedDouble currentWaterLevel = new RoundedDouble(2, Math.Ceiling(upperBoundary/stepSizeValue)*stepSizeValue - stepSizeValue);
while (currentWaterLevel > lowerBoundary)
{
waterLevels.Add(currentWaterLevel);
currentWaterLevel = new RoundedDouble(currentWaterLevel.NumberOfDecimalPlaces, currentWaterLevel - stepSizeValue);
}
waterLevels.Add(lowerBoundary);
return waterLevels;
}
private void UpdateForeshoreProfileParameters()
{
if (foreshoreProfile == null)
{
Orientation = (RoundedDouble) double.NaN;
UseForeshore = false;
UseBreakWater = false;
BreakWater = GetDefaultBreakWater();
}
else
{
Orientation = foreshoreProfile.Orientation;
UseForeshore = foreshoreProfile.Geometry.Count() > 1;
UseBreakWater = foreshoreProfile.HasBreakWater;
BreakWater = foreshoreProfile.HasBreakWater
? new BreakWater(foreshoreProfile.BreakWater.Type, foreshoreProfile.BreakWater.Height)
: GetDefaultBreakWater();
}
}
private static BreakWater GetDefaultBreakWater()
{
return new BreakWater(BreakWaterType.Dam, 0.0);
}
}
}