// 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 System.Windows.Forms;
using Core.Common.Base;
using Core.Common.Base.Data;
using Core.Common.Base.Geometry;
using Core.Common.Controls.DataGrid;
using Core.Common.Controls.Views;
using Core.Common.Gui.Selection;
using Ringtoets.Common.Data.AssessmentSection;
using Ringtoets.Common.Data.FailureMechanism;
using Ringtoets.HydraRing.Data;
using Ringtoets.Piping.Data;
using Ringtoets.Piping.Forms.PresentationObjects;
using Ringtoets.Piping.Forms.Properties;
using Ringtoets.Piping.Primitives;
using RingtoetsCommonFormsResources = Ringtoets.Common.Forms.Properties.Resources;
namespace Ringtoets.Piping.Forms.Views
{
///
/// This class is a view for configuring piping calculations.
///
public partial class PipingCalculationsView : UserControl, IView
{
private readonly Observer assessmentSectionObserver;
private readonly RecursiveObserver pipingCalculationGroupObserver;
private readonly RecursiveObserver pipingCalculationObserver;
private readonly Observer pipingFailureMechanismObserver;
private readonly RecursiveObserver pipingInputObserver;
private readonly Observer pipingStochasticSoilModelsObserver;
private IAssessmentSection assessmentSection;
private DataGridViewComboBoxColumn hydraulicBoundaryLocationColumn;
private PipingCalculationGroup pipingCalculationGroup;
private PipingFailureMechanism pipingFailureMechanism;
private DataGridViewComboBoxColumn stochasticSoilModelColumn;
private DataGridViewComboBoxColumn stochasticSoilProfileColumn;
private bool updatingDataSource;
///
/// Creates a new instance of the class.
///
public PipingCalculationsView()
{
InitializeComponent();
InitializeDataGridView();
InitializeListBox();
pipingStochasticSoilModelsObserver = new Observer(OnStochasticSoilModelsUpdate);
pipingFailureMechanismObserver = new Observer(OnPipingFailureMechanismUpdate);
assessmentSectionObserver = new Observer(UpdateHydraulicBoundaryLocationsColumn);
pipingInputObserver = new RecursiveObserver(UpdateDataGridViewDataSource, pcg => pcg.Children.Concat(pcg.Children.OfType().Select(pc => pc.InputParameters)));
pipingCalculationObserver = new RecursiveObserver(RefreshDataGridView, pcg => pcg.Children);
pipingCalculationGroupObserver = new RecursiveObserver(UpdateDataGridViewDataSource, pcg => pcg.Children);
}
///
/// Gets or sets the piping failure mechanism.
///
public PipingFailureMechanism PipingFailureMechanism
{
get
{
return pipingFailureMechanism;
}
set
{
pipingFailureMechanism = value;
pipingStochasticSoilModelsObserver.Observable = pipingFailureMechanism != null ? pipingFailureMechanism.StochasticSoilModels : null;
pipingFailureMechanismObserver.Observable = pipingFailureMechanism;
UpdateStochasticSoilModelColumn();
UpdateStochasticSoilProfileColumn();
UpdateSectionsListBox();
UpdateGenerateScenariosButtonState();
}
}
///
/// Gets or sets the assessment section.
///
public IAssessmentSection AssessmentSection
{
get
{
return assessmentSection;
}
set
{
assessmentSection = value;
assessmentSectionObserver.Observable = assessmentSection;
UpdateHydraulicBoundaryLocationsColumn();
}
}
///
/// Gets or sets the .
///
public IApplicationSelection ApplicationSelection { get; set; }
public object Data
{
get
{
return pipingCalculationGroup;
}
set
{
pipingCalculationGroup = value as PipingCalculationGroup;
if (pipingCalculationGroup != null)
{
UpdateDataGridViewDataSource();
pipingInputObserver.Observable = pipingCalculationGroup;
pipingCalculationObserver.Observable = pipingCalculationGroup;
pipingCalculationGroupObserver.Observable = pipingCalculationGroup;
}
else
{
dataGridView.DataSource = null;
pipingInputObserver.Observable = null;
pipingCalculationObserver.Observable = null;
pipingCalculationGroupObserver.Observable = null;
}
}
}
protected override void Dispose(bool disposing)
{
AssessmentSection = null;
PipingFailureMechanism = null;
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeDataGridView()
{
dataGridView.CurrentCellDirtyStateChanged += DataGridViewCurrentCellDirtyStateChanged;
dataGridView.CellClick += DataGridViewOnCellClick;
dataGridView.CellValidating += DataGridViewCellValidating;
dataGridView.DataError += DataGridViewDataError;
var nameColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "Name",
HeaderText = Resources.PipingCalculation_Name_DisplayName,
Name = "column_Name"
};
stochasticSoilModelColumn = new DataGridViewComboBoxColumn
{
DataPropertyName = "StochasticSoilModel",
HeaderText = Resources.PipingInput_StochasticSoilModel_DisplayName,
Name = "column_SoilModel",
ValueMember = "This",
ValueType = typeof(DataGridViewComboBoxItemWrapper),
DisplayMember = "DisplayName"
};
stochasticSoilProfileColumn = new DataGridViewComboBoxColumn
{
DataPropertyName = "StochasticSoilProfile",
HeaderText = Resources.PipingInput_StochasticSoilProfile_DisplayName,
Name = "column_SoilProfile",
ValueMember = "This",
DisplayMember = "DisplayName"
};
hydraulicBoundaryLocationColumn = new DataGridViewComboBoxColumn
{
DataPropertyName = "HydraulicBoundaryLocation",
HeaderText = Resources.PipingInput_HydraulicBoundaryLocation_DisplayName,
Name = "column_HydraulicBoundaryLocation",
ValueMember = "This",
DisplayMember = "DisplayName"
};
var dampingFactorExitHeader = Resources.PipingInput_DampingFactorExit_DisplayName;
dampingFactorExitHeader = char.ToLowerInvariant(dampingFactorExitHeader[0]) + dampingFactorExitHeader.Substring(1);
var dampingFactorExitMeanColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "DampingFactorExitMean",
HeaderText = string.Format("{0} {1}", Resources.Probabilistics_Mean_Symbol, dampingFactorExitHeader),
Name = "column_DampingFactorExitMean"
};
var phreaticLevelExitHeader = Resources.PipingInput_PhreaticLevelExit_DisplayName;
phreaticLevelExitHeader = char.ToLowerInvariant(phreaticLevelExitHeader[0]) + phreaticLevelExitHeader.Substring(1);
var phreaticLevelExitMeanColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "PhreaticLevelExitMean",
HeaderText = string.Format("{0} {1}", Resources.Probabilistics_Mean_Symbol, phreaticLevelExitHeader),
Name = "column_PhreaticLevelExitMean"
};
var entryPointLColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "EntryPointL",
HeaderText = Resources.PipingInput_EntryPointL_DisplayName,
Name = "column_EntryPointL"
};
var exitPointLColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "ExitPointL",
HeaderText = Resources.PipingInput_ExitPointL_DisplayName,
Name = "column_ExitPointL"
};
dataGridView.AutoGenerateColumns = false;
dataGridView.Columns.AddRange(
nameColumn,
stochasticSoilModelColumn,
stochasticSoilProfileColumn,
hydraulicBoundaryLocationColumn,
dampingFactorExitMeanColumn,
phreaticLevelExitMeanColumn,
entryPointLColumn,
exitPointLColumn);
foreach (var column in dataGridView.Columns.OfType())
{
column.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
column.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
UpdateHydraulicBoundaryLocationsColumn();
UpdateStochasticSoilModelColumn();
UpdateStochasticSoilProfileColumn();
}
private void InitializeListBox()
{
listBox.DisplayMember = "Name";
listBox.SelectedValueChanged += ListBoxOnSelectedValueChanged;
}
private void UpdateHydraulicBoundaryLocationsColumn()
{
using (new SuspendDataGridViewColumnResizes(hydraulicBoundaryLocationColumn))
{
var hydraulicBoundaryLocations = assessmentSection != null && assessmentSection.HydraulicBoundaryDatabase != null
? assessmentSection.HydraulicBoundaryDatabase.Locations
: null;
SetItemsOnObjectCollection(hydraulicBoundaryLocationColumn.Items, GetHydraulicBoundaryLocationsDataSource(hydraulicBoundaryLocations).ToArray());
}
}
private void OnStochasticSoilModelsUpdate()
{
UpdateGenerateScenariosButtonState();
UpdateStochasticSoilModelColumn();
UpdateStochasticSoilProfileColumn();
}
private void UpdateStochasticSoilModelColumn()
{
using (new SuspendDataGridViewColumnResizes(stochasticSoilModelColumn))
{
foreach (DataGridViewRow dataGridViewRow in dataGridView.Rows)
{
FillAvailableSoilModelsList(dataGridViewRow);
}
}
}
private void UpdateStochasticSoilProfileColumn()
{
using (new SuspendDataGridViewColumnResizes(stochasticSoilProfileColumn))
{
foreach (DataGridViewRow dataGridViewRow in dataGridView.Rows)
{
FillAvailableSoilProfilesList(dataGridViewRow);
}
}
}
private void UpdateGenerateScenariosButtonState()
{
buttonGenerateScenarios.Enabled = pipingFailureMechanism != null &&
pipingFailureMechanism.SurfaceLines.Any() &&
pipingFailureMechanism.StochasticSoilModels.Any();
}
private void RefreshDataGridView()
{
dataGridView.Refresh();
dataGridView.AutoResizeColumns();
}
private void UpdateDataGridViewDataSource()
{
// Skip changes coming from the view itself
if (dataGridView.IsCurrentCellInEditMode)
{
updatingDataSource = true;
UpdateStochasticSoilProfileColumn();
updatingDataSource = false;
dataGridView.AutoResizeColumns();
return;
}
var failureMechanismSection = listBox.SelectedItem as FailureMechanismSection;
if (failureMechanismSection == null)
{
dataGridView.DataSource = null;
return;
}
var lineSegments = Math2D.ConvertLinePointsToLineSegments(failureMechanismSection.Points);
var pipingCalculations = pipingCalculationGroup
.GetPipingCalculations()
.Where(pc => IsSurfaceLineIntersectionWithReferenceLineInSection(pc.InputParameters.SurfaceLine, lineSegments));
updatingDataSource = true;
PrefillComboBoxListItemsAtColumnLevel();
dataGridView.DataSource = pipingCalculations
.Select(pc => new PipingCalculationRow(pc))
.ToList();
UpdateStochasticSoilModelColumn();
UpdateStochasticSoilProfileColumn();
updatingDataSource = false;
}
private static bool IsSurfaceLineIntersectionWithReferenceLineInSection(RingtoetsPipingSurfaceLine surfaceLine, IEnumerable lineSegments)
{
if (surfaceLine == null)
{
return false;
}
var minimalDistance = lineSegments.Min(segment => segment.GetEuclideanDistanceToPoint(surfaceLine.ReferenceLineIntersectionWorldPoint));
return minimalDistance < 1.0e-6;
}
private void FillAvailableSoilModelsList(DataGridViewRow dataGridViewRow)
{
var rowData = (PipingCalculationRow) dataGridViewRow.DataBoundItem;
IEnumerable stochasticSoilModels = GetSoilModelsForCalculation(rowData.PipingCalculation);
var cell = (DataGridViewComboBoxCell) dataGridViewRow.Cells[stochasticSoilModelColumn.Index];
SetItemsOnObjectCollection(cell.Items, GetStochasticSoilModelColumnsDataSource(stochasticSoilModels).ToArray());
}
private void FillAvailableSoilProfilesList(DataGridViewRow dataGridViewRow)
{
var rowData = (PipingCalculationRow) dataGridViewRow.DataBoundItem;
IEnumerable stochasticSoilProfiles = GetSoilProfilesForCalculation(rowData.PipingCalculation);
var cell = (DataGridViewComboBoxCell) dataGridViewRow.Cells[stochasticSoilProfileColumn.Index];
SetItemsOnObjectCollection(cell.Items, GetSoilProfilesDataSource(stochasticSoilProfiles).ToArray());
}
private IEnumerable GetSoilModelsForCalculation(PipingCalculation pipingCalculation)
{
if (pipingFailureMechanism == null)
{
return Enumerable.Empty();
}
return PipingCalculationConfigurationHelper.GetStochasticSoilModelsForSurfaceLine(
pipingCalculation.InputParameters.SurfaceLine,
pipingFailureMechanism.StochasticSoilModels);
}
private IEnumerable GetSoilProfilesForCalculation(PipingCalculation pipingCalculation)
{
if (pipingCalculation.InputParameters.StochasticSoilModel == null)
{
return Enumerable.Empty();
}
return pipingCalculation.InputParameters.StochasticSoilModel.StochasticSoilProfiles;
}
private static void SetItemsOnObjectCollection(DataGridViewComboBoxCell.ObjectCollection objectCollection, object[] comboBoxItems)
{
objectCollection.Clear();
objectCollection.AddRange(comboBoxItems);
}
private void OnPipingFailureMechanismUpdate()
{
UpdateGenerateScenariosButtonState();
UpdateSectionsListBox();
}
private void UpdateSectionsListBox()
{
listBox.Items.Clear();
if (pipingFailureMechanism != null && pipingFailureMechanism.Sections.Any())
{
listBox.Items.AddRange(pipingFailureMechanism.Sections.Cast().ToArray());
listBox.SelectedItem = pipingFailureMechanism.Sections.First();
}
}
private static IEnumerable> GetStochasticSoilModelColumnsDataSource(IEnumerable stochasticSoilModels = null)
{
yield return new DataGridViewComboBoxItemWrapper(null);
if (stochasticSoilModels != null)
{
foreach (StochasticSoilModel stochasticSoilModel in stochasticSoilModels)
{
yield return new DataGridViewComboBoxItemWrapper(stochasticSoilModel);
}
}
}
private static IEnumerable> GetSoilProfilesDataSource(IEnumerable stochasticSoilProfiles = null)
{
yield return new DataGridViewComboBoxItemWrapper(null);
if (stochasticSoilProfiles != null)
{
foreach (StochasticSoilProfile stochasticSoilProfile in stochasticSoilProfiles)
{
yield return new DataGridViewComboBoxItemWrapper(stochasticSoilProfile);
}
}
}
private static List> GetHydraulicBoundaryLocationsDataSource(IEnumerable hydraulicBoundaryLocations = null)
{
var dataGridViewComboBoxItemWrappers = new List>
{
new DataGridViewComboBoxItemWrapper(null)
};
if (hydraulicBoundaryLocations != null)
{
dataGridViewComboBoxItemWrappers.AddRange(hydraulicBoundaryLocations.Select(hbl => new DataGridViewComboBoxItemWrapper(hbl)));
}
return dataGridViewComboBoxItemWrappers;
}
#region Prefill combo box list items
private void PrefillComboBoxListItemsAtColumnLevel()
{
// Need to prefill for all possible data in order to guarantee 'combo box' columns
// do not generate errors when their cell value is not present in the list of available
// items.
using (new SuspendDataGridViewColumnResizes(stochasticSoilModelColumn))
{
var stochasticSoilModels = pipingFailureMechanism.StochasticSoilModels;
SetItemsOnObjectCollection(stochasticSoilModelColumn.Items, GetStochasticSoilModelColumnsDataSource(stochasticSoilModels).ToArray());
}
using (new SuspendDataGridViewColumnResizes(stochasticSoilProfileColumn))
{
var pipingSoilProfiles = GetPipingStochasticSoilProfilesFromStochasticSoilModels();
SetItemsOnObjectCollection(stochasticSoilProfileColumn.Items, GetSoilProfilesDataSource(pipingSoilProfiles).ToArray());
}
using (new SuspendDataGridViewColumnResizes(hydraulicBoundaryLocationColumn))
{
var hydraulicBoundaryLocations = assessmentSection != null && assessmentSection.HydraulicBoundaryDatabase != null
? assessmentSection.HydraulicBoundaryDatabase.Locations
: null;
SetItemsOnObjectCollection(
hydraulicBoundaryLocationColumn.Items,
GetHydraulicBoundaryLocationsDataSource(hydraulicBoundaryLocations).ToArray());
}
}
private StochasticSoilProfile[] GetPipingStochasticSoilProfilesFromStochasticSoilModels()
{
if (pipingFailureMechanism != null)
{
return pipingFailureMechanism.StochasticSoilModels
.SelectMany(ssm => ssm.StochasticSoilProfiles)
.Distinct()
.ToArray();
}
return null;
}
#endregion
#region Nested types
///
/// This class makes it easier to temporarily disable automatic resizing of a column,
/// for example when it's data is being changed or you are replacing the list items
/// available in a combo-box for that column.
///
private class SuspendDataGridViewColumnResizes : IDisposable
{
private readonly DataGridViewColumn column;
private readonly DataGridViewAutoSizeColumnMode originalValue;
public SuspendDataGridViewColumnResizes(DataGridViewColumn columnToSuspend)
{
column = columnToSuspend;
originalValue = columnToSuspend.AutoSizeMode;
columnToSuspend.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
}
public void Dispose()
{
column.AutoSizeMode = originalValue;
}
}
private class PipingCalculationRow
{
private readonly PipingCalculation pipingCalculation;
public PipingCalculationRow(PipingCalculation pipingCalculation)
{
this.pipingCalculation = pipingCalculation;
}
public PipingCalculation PipingCalculation
{
get
{
return pipingCalculation;
}
}
public string Name
{
get
{
return pipingCalculation.Name;
}
set
{
pipingCalculation.Name = value;
pipingCalculation.NotifyObservers();
}
}
public DataGridViewComboBoxItemWrapper StochasticSoilModel
{
get
{
return new DataGridViewComboBoxItemWrapper(pipingCalculation.InputParameters.StochasticSoilModel);
}
set
{
pipingCalculation.InputParameters.StochasticSoilModel = value != null
? value.WrappedObject
: null;
pipingCalculation.InputParameters.NotifyObservers();
}
}
public DataGridViewComboBoxItemWrapper StochasticSoilProfile
{
get
{
return new DataGridViewComboBoxItemWrapper(pipingCalculation.InputParameters.StochasticSoilProfile);
}
set
{
pipingCalculation.InputParameters.StochasticSoilProfile = value != null
? value.WrappedObject
: null;
pipingCalculation.InputParameters.NotifyObservers();
}
}
public DataGridViewComboBoxItemWrapper HydraulicBoundaryLocation
{
get
{
return new DataGridViewComboBoxItemWrapper(pipingCalculation.InputParameters.HydraulicBoundaryLocation);
}
set
{
pipingCalculation.InputParameters.HydraulicBoundaryLocation = value != null
? value.WrappedObject
: null;
pipingCalculation.InputParameters.NotifyObservers();
}
}
public RoundedDouble DampingFactorExitMean
{
get
{
return pipingCalculation.InputParameters.DampingFactorExit.Mean;
}
set
{
pipingCalculation.InputParameters.DampingFactorExit.Mean = value;
pipingCalculation.InputParameters.NotifyObservers();
}
}
public RoundedDouble PhreaticLevelExitMean
{
get
{
return pipingCalculation.InputParameters.PhreaticLevelExit.Mean;
}
set
{
pipingCalculation.InputParameters.PhreaticLevelExit.Mean = value;
pipingCalculation.InputParameters.NotifyObservers();
}
}
public RoundedDouble EntryPointL
{
get
{
return pipingCalculation.InputParameters.EntryPointL;
}
set
{
pipingCalculation.InputParameters.EntryPointL = value;
pipingCalculation.InputParameters.NotifyObservers();
}
}
public RoundedDouble ExitPointL
{
get
{
return pipingCalculation.InputParameters.ExitPointL;
}
set
{
pipingCalculation.InputParameters.ExitPointL = value;
pipingCalculation.InputParameters.NotifyObservers();
}
}
}
#endregion
# region Event handling
private void DataGridViewCurrentCellDirtyStateChanged(object sender, EventArgs e)
{
// Ensure combobox values are directly committed
DataGridViewColumn currentColumn = dataGridView.Columns[dataGridView.CurrentCell.ColumnIndex];
if (currentColumn is DataGridViewComboBoxColumn)
{
dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
dataGridView.EndEdit();
}
}
private void DataGridViewOnCellClick(object sender, DataGridViewCellEventArgs e)
{
if (updatingDataSource)
{
return;
}
UpdateApplicationSelection();
}
private void DataGridViewCellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
dataGridView.Rows[e.RowIndex].ErrorText = String.Empty;
var cellEditValue = e.FormattedValue.ToString();
if (string.IsNullOrWhiteSpace(cellEditValue))
{
dataGridView.Rows[e.RowIndex].ErrorText = RingtoetsCommonFormsResources.DataGridViewCellValidating_Text_may_not_be_empty;
}
}
private void DataGridViewDataError(object sender, DataGridViewDataErrorEventArgs e)
{
e.ThrowException = false;
e.Cancel = true;
if (string.IsNullOrWhiteSpace(dataGridView.Rows[e.RowIndex].ErrorText) && e.Exception != null)
{
dataGridView.Rows[e.RowIndex].ErrorText = e.Exception.Message;
}
}
private void ListBoxOnSelectedValueChanged(object sender, EventArgs e)
{
UpdateDataGridViewDataSource();
UpdateApplicationSelection();
}
private void OnGenerateScenariosButtonClick(object sender, EventArgs e)
{
var dialog = new PipingSurfaceLineSelectionDialog(Parent, pipingFailureMechanism.SurfaceLines);
dialog.ShowDialog();
var calculationsStructure = PipingCalculationConfigurationHelper.GenerateCalculationsStructure(
dialog.SelectedSurfaceLines,
pipingFailureMechanism.StochasticSoilModels,
pipingFailureMechanism.GeneralInput,
pipingFailureMechanism.SemiProbabilisticInput);
foreach (var item in calculationsStructure)
{
pipingCalculationGroup.Children.Add(item);
}
pipingCalculationGroup.NotifyObservers();
}
private void UpdateApplicationSelection()
{
if (ApplicationSelection == null)
{
return;
}
var pipingCalculationRow = dataGridView.CurrentRow != null
? (PipingCalculationRow) dataGridView.CurrentRow.DataBoundItem
: null;
PipingInputContext selection = null;
if (pipingCalculationRow != null)
{
selection = new PipingInputContext(
pipingCalculationRow.PipingCalculation.InputParameters,
pipingFailureMechanism.SurfaceLines,
pipingFailureMechanism.StochasticSoilModels,
assessmentSection);
}
if ((ApplicationSelection.Selection == null && selection != null)
|| (ApplicationSelection.Selection != null && !ApplicationSelection.Selection.Equals(selection)))
{
ApplicationSelection.Selection = selection;
}
}
# endregion
}
}