// 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;
using Ringtoets.HydraRing.Data;
using Ringtoets.Piping.Data;
using Ringtoets.Piping.Forms.PresentationObjects;
using Ringtoets.Piping.Forms.Properties;
using Ringtoets.Piping.Primitives;
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 pipingSoilProfilesObserver;
private IAssessmentSection assessmentSection;
private DataGridViewComboBoxColumn hydraulicBoundaryLocationColumn;
private PipingCalculationGroup pipingCalculationGroup;
private PipingFailureMechanism pipingFailureMechanism;
private DataGridViewComboBoxColumn soilProfileColumn;
private bool updatingDataSource;
///
/// Creates a new instance of the class.
///
public PipingCalculationsView()
{
InitializeComponent();
InitializeDataGridView();
InitializeListBox();
pipingSoilProfilesObserver = new Observer(OnSoilProfilesUpdate);
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;
pipingSoilProfilesObserver.Observable = pipingFailureMechanism != null ? pipingFailureMechanism.StochasticSoilModels : null;
pipingFailureMechanismObserver.Observable = pipingFailureMechanism;
UpdateSoilProfileColumn();
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;
var nameColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "Name",
HeaderText = Resources.PipingCalculation_Name_DisplayName,
Name = "column_Name"
};
soilProfileColumn = new DataGridViewComboBoxColumn
{
DataPropertyName = "SoilProfile",
HeaderText = Resources.PipingInput_SoilProfile_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,
soilProfileColumn,
hydraulicBoundaryLocationColumn,
dampingFactorExitMeanColumn,
phreaticLevelExitMeanColumn,
entryPointLColumn,
exitPointLColumn);
foreach (var column in dataGridView.Columns.OfType())
{
column.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
column.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
UpdateHydraulicBoundaryLocationsColumn();
UpdateSoilProfileColumn();
}
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 OnSoilProfilesUpdate()
{
UpdateGenerateScenariosButtonState();
UpdateSoilProfileColumn();
}
private void UpdateSoilProfileColumn()
{
using (new SuspendDataGridViewColumnResizes(soilProfileColumn))
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)
{
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();
UpdateSoilProfileColumn();
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 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(soilProfileColumn))
{
var pipingSoilProfiles = GetPipingSoilProfilesFromStochasticSoilModels();
SetItemsOnObjectCollection(soilProfileColumn.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 PipingSoilProfile[] GetPipingSoilProfilesFromStochasticSoilModels()
{
if (pipingFailureMechanism != null)
{
return pipingFailureMechanism.StochasticSoilModels
.SelectMany(ssm => ssm.StochasticSoilProfiles.Select(ssp => ssp.SoilProfile))
.Distinct()
.ToArray();
}
return null;
}
private void FillAvailableSoilProfilesList(DataGridViewRow dataGridViewRow)
{
var rowData = (PipingCalculationRow) dataGridViewRow.DataBoundItem;
IEnumerable pipingSoilProfiles = GetSoilProfilesForCalculation(rowData.PipingCalculation);
var cell = (DataGridViewComboBoxCell) dataGridViewRow.Cells[soilProfileColumn.Index];
SetItemsOnObjectCollection(cell.Items, GetSoilProfilesDataSource(pipingSoilProfiles).ToArray());
}
private IEnumerable GetSoilProfilesForCalculation(PipingCalculation pipingCalculation)
{
if (pipingFailureMechanism == null)
{
return Enumerable.Empty();
}
return PipingCalculationConfigurationHelper.GetPipingSoilProfilesForSurfaceLine(
pipingCalculation.InputParameters.SurfaceLine,
pipingFailureMechanism.StochasticSoilModels);
}
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> GetSoilProfilesDataSource(IEnumerable soilProfiles = null)
{
yield return new DataGridViewComboBoxItemWrapper(null);
if (soilProfiles != null)
{
foreach (PipingSoilProfile profile in soilProfiles)
{
yield return new DataGridViewComboBoxItemWrapper(profile);
}
}
}
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 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 SoilProfile
{
get
{
return new DataGridViewComboBoxItemWrapper(pipingCalculation.InputParameters.SoilProfile);
}
set
{
pipingCalculation.InputParameters.SoilProfile = 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 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
}
}