// Copyright (C) Stichting Deltares 2017. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Core.Common.Base;
using Core.Common.Base.Data;
using Core.Common.Base.Geometry;
using Ringtoets.Common.Service;
using Ringtoets.Common.Service.ValidationRules;
using Ringtoets.MacroStabilityInwards.Data;
using Ringtoets.MacroStabilityInwards.Data.SoilProfile;
using Ringtoets.MacroStabilityInwards.Primitives;
using Ringtoets.MacroStabilityInwards.Service.Properties;
using RingtoetsCommonFormsResources = Ringtoets.Common.Forms.Properties.Resources;
using RingtoetsCommonServiceResources = Ringtoets.Common.Service.Properties.Resources;
namespace Ringtoets.MacroStabilityInwards.Service
{
///
/// Validator for for use in .
///
public static class MacroStabilityInwardsInputValidator
{
private const double withinSurfaceLineLevelLimit = 0.05;
///
/// Performs validation over the values on the given .
///
/// The for which to validate the values.
/// Validation errors, if any.
/// The normative assessment level to use in case the manual assessment level is not applicable.
/// Thrown when is null.
public static IEnumerable Validate(MacroStabilityInwardsInput inputParameters, RoundedDouble normativeAssessmentLevel)
{
if (inputParameters == null)
{
throw new ArgumentNullException(nameof(inputParameters));
}
var validationResults = new List();
validationResults.AddRange(ValidateHydraulics(inputParameters, normativeAssessmentLevel));
IEnumerable coreValidationError = ValidateCoreSurfaceLineAndSoilProfileProperties(inputParameters).ToArray();
validationResults.AddRange(coreValidationError);
if (!coreValidationError.Any())
{
validationResults.AddRange(ValidateSoilLayers(inputParameters));
validationResults.AddRange(ValidateZoneBoundaries(inputParameters));
validationResults.AddRange(ValidateTangentLines(inputParameters));
}
return validationResults;
}
private static IEnumerable ValidateSoilLayers(MacroStabilityInwardsInput inputParameters)
{
var soilProfile1D = inputParameters.StochasticSoilProfile.SoilProfile as MacroStabilityInwardsSoilProfile1D;
if (soilProfile1D != null)
{
if (!ValidateTopOfProfileExceedsSurfaceLineTop(inputParameters, soilProfile1D))
{
yield return Resources.MacroStabilityInwardsCalculationService_ValidateInput_SoilLayerTop_must_be_larger_than_SurfaceLineTop;
}
yield break;
}
var soilProfile2D = inputParameters.StochasticSoilProfile.SoilProfile as MacroStabilityInwardsSoilProfile2D;
if (soilProfile2D != null
&& !ValidateSurfaceLineIsNearSoilProfile(inputParameters, soilProfile2D))
{
yield return Resources.MacroStabilityInwardsCalculationService_ValidateInput_SurfaceLine_must_be_on_SoilLayer;
}
}
private static IEnumerable ValidateZoneBoundaries(MacroStabilityInwardsInput inputParameters)
{
if (!inputParameters.CreateZones
|| inputParameters.ZoningBoundariesDeterminationType == MacroStabilityInwardsZoningBoundariesDeterminationType.Automatic)
{
yield break;
}
RoundedDouble zoneBoundaryLeft = inputParameters.ZoneBoundaryLeft;
RoundedDouble zoneBoundaryRight = inputParameters.ZoneBoundaryRight;
if (zoneBoundaryLeft > zoneBoundaryRight)
{
yield return Resources.MacroStabilityInwardsInputValidator_ValidateZoneBoundaries_ZoneBoundaries_BoundaryLeft_should_be_smaller_than_or_equal_to_BoundaryRight;
}
MacroStabilityInwardsSurfaceLine surfaceLine = inputParameters.SurfaceLine;
if (!surfaceLine.ValidateInRange(zoneBoundaryLeft) || !surfaceLine.ValidateInRange(zoneBoundaryRight))
{
var validityRange = new Range(surfaceLine.LocalGeometry.First().X, surfaceLine.LocalGeometry.Last().X);
yield return string.Format(Resources.MacroStabilityInwardsInputValidator_ValidateZoneBoundaries_ZoneBoundaries_must_be_in_Range_0,
validityRange.ToString(FormattableConstants.ShowAtLeastOneDecimal, CultureInfo.CurrentCulture));
}
}
private static IEnumerable ValidateTangentLines(MacroStabilityInwardsInput inputParameters)
{
if (inputParameters.TangentLineZTop == inputParameters.TangentLineZBottom
&& inputParameters.TangentLineNumber != 1)
{
yield return Resources.MacroStabilityInwardsInputValidator_ValidateTangentLines_TangentLineNumber_must_be_one_when_TangentLineTop_equals_TangentLineBottom;
}
}
private static bool ValidateTopOfProfileExceedsSurfaceLineTop(IMacroStabilityInwardsWaternetInput inputParameters,
MacroStabilityInwardsSoilProfile1D soilProfile1D)
{
double layerTop = soilProfile1D.Layers.Max(l => l.Top);
double surfaceLineTop = inputParameters.SurfaceLine.LocalGeometry.Max(p => p.Y);
return layerTop + withinSurfaceLineLevelLimit >= surfaceLineTop;
}
private static bool ValidateSurfaceLineIsNearSoilProfile(MacroStabilityInwardsInput inputParameters,
MacroStabilityInwardsSoilProfile2D soilProfile2D)
{
IEnumerable discretizedSurfaceLineXCoordinates = GetClippedDiscretizedXCoordinatesOfSurfaceLine(inputParameters.SurfaceLine.LocalGeometry,
soilProfile2D);
IEnumerable surfaceLineWithInterpolations = GetSurfaceLineWithInterpolations(inputParameters, discretizedSurfaceLineXCoordinates);
IEnumerable> layerPolygons = GetLayerPolygons(soilProfile2D);
foreach (Point2D surfaceLinePoint in surfaceLineWithInterpolations)
{
IEnumerable intersectingCoordinates = layerPolygons.SelectMany(lp => Math2D.SegmentsIntersectionWithVerticalLine(lp, surfaceLinePoint.X));
if (!intersectingCoordinates.Any())
{
return false;
}
double maxYCoordinate = intersectingCoordinates.Select(p => p.Y).Max();
if (Math.Abs(surfaceLinePoint.Y - maxYCoordinate) - withinSurfaceLineLevelLimit >= 1e-5)
{
return false;
}
}
return true;
}
private static IEnumerable GetClippedDiscretizedXCoordinatesOfSurfaceLine(IEnumerable surfaceLinePoints,
MacroStabilityInwardsSoilProfile2D soilProfile2D)
{
IEnumerable surfaceLineXCoordinates = surfaceLinePoints.Select(p => p.X).ToArray();
IEnumerable soilProfileXCoordinates = GetSoilProfile2DXCoordinates(soilProfile2D).ToArray();
double maximumXCoordinateSurfaceLine = surfaceLineXCoordinates.Max();
double maximumXCoordinateSoilProfile = soilProfileXCoordinates.Max();
double maxXCoordinate = Math.Min(maximumXCoordinateSoilProfile, maximumXCoordinateSurfaceLine);
double minimumXCoordinateSurfaceLine = surfaceLineXCoordinates.Min();
double minimumXCoordinateSoilProfile = soilProfileXCoordinates.Min();
double minXCoordinate = Math.Max(minimumXCoordinateSoilProfile, minimumXCoordinateSurfaceLine);
IEnumerable clippedSoilProfileXCoordinates = soilProfileXCoordinates.Where(xCoordinate => IsXCoordinateInRange(xCoordinate, minXCoordinate, maxXCoordinate));
IEnumerable clippedSurfaceLineXCoordinates = surfaceLineXCoordinates.Where(xCoordinate => IsXCoordinateInRange(xCoordinate, minXCoordinate, maxXCoordinate));
double[] uniqueClippedXCoordinates = clippedSoilProfileXCoordinates.Concat(clippedSurfaceLineXCoordinates)
.Distinct()
.OrderBy(xCoordinate => xCoordinate)
.ToArray();
var xCoordinates = new List();
for (var i = 0; i < uniqueClippedXCoordinates.Length - 1; i++)
{
double firstXCoordinate = uniqueClippedXCoordinates[i];
double secondXCoordinate = uniqueClippedXCoordinates[i + 1];
xCoordinates.AddRange(GetDiscretizedXCoordinatesBetweenInterval(firstXCoordinate, secondXCoordinate));
}
xCoordinates.Add(uniqueClippedXCoordinates.Last());
return xCoordinates;
}
private static IEnumerable GetDiscretizedXCoordinatesBetweenInterval(double startXCoordinate, double endXCoordinate)
{
double xCoordinate = startXCoordinate;
var discretizedXCoordinates = new List();
while (xCoordinate < endXCoordinate)
{
discretizedXCoordinates.Add(xCoordinate);
xCoordinate += withinSurfaceLineLevelLimit;
}
return discretizedXCoordinates;
}
private static bool IsXCoordinateInRange(double xCoordinate, double minXCoordinate, double maxXCoordinate)
{
return xCoordinate <= maxXCoordinate
&& xCoordinate >= minXCoordinate;
}
private static IEnumerable> GetLayerPolygons(MacroStabilityInwardsSoilProfile2D soilProfile2D)
{
return soilProfile2D.Layers
.Select(l => Math2D.ConvertPointsToPolygonSegments(l.OuterRing.Points))
.ToArray();
}
private static IEnumerable GetSoilProfile2DXCoordinates(MacroStabilityInwardsSoilProfile2D soilProfile2D)
{
return soilProfile2D.Layers
.SelectMany(l => l.OuterRing
.Points
.Select(outerRingPoint => outerRingPoint.X));
}
private static IEnumerable GetSurfaceLineWithInterpolations(MacroStabilityInwardsInput inputParameters,
IEnumerable uniqueXs)
{
Segment2D[] surfaceLineSegments = Math2D.ConvertPointsToLineSegments(inputParameters.SurfaceLine.LocalGeometry).ToArray();
foreach (double x in uniqueXs)
{
double y = surfaceLineSegments.Interpolate(x);
yield return new Point2D(x, y);
}
}
private static IEnumerable ValidateHydraulics(MacroStabilityInwardsInput inputParameters, RoundedDouble normativeAssessmentLevel)
{
var validationResults = new List();
if (!inputParameters.UseAssessmentLevelManualInput && inputParameters.HydraulicBoundaryLocation == null)
{
validationResults.Add(RingtoetsCommonServiceResources.CalculationService_ValidateInput_No_hydraulic_boundary_location_selected);
}
else
{
validationResults.AddRange(ValidateAssessmentLevel(inputParameters, normativeAssessmentLevel));
}
return validationResults;
}
private static IEnumerable ValidateAssessmentLevel(MacroStabilityInwardsInput inputParameters, RoundedDouble normativeAssessmentLevel)
{
var validationResult = new List();
if (inputParameters.UseAssessmentLevelManualInput)
{
validationResult.AddRange(new NumericInputRule(inputParameters.AssessmentLevel, ParameterNameExtractor.GetFromDisplayName(RingtoetsCommonFormsResources.WaterLevel_DisplayName)).Validate());
}
else
{
if (double.IsNaN(normativeAssessmentLevel))
{
validationResult.Add(RingtoetsCommonServiceResources.CalculationService_ValidateInput_Cannot_determine_AssessmentLevel);
}
}
return validationResult;
}
private static IEnumerable ValidateCoreSurfaceLineAndSoilProfileProperties(MacroStabilityInwardsInput inputParameters)
{
var validationResults = new List();
if (inputParameters.SurfaceLine == null)
{
validationResults.Add(Resources.MacroStabilityInwardsCalculationService_ValidateInput_No_SurfaceLine_selected);
}
if (inputParameters.StochasticSoilProfile == null)
{
validationResults.Add(Resources.MacroStabilityInwardsCalculationService_ValidateInput_No_StochasticSoilProfile_selected);
}
return validationResults;
}
}
}