using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using Core.Common.Base.Geometry; using Core.Common.Base.IO; using Core.Common.IO.Exceptions; using Core.Common.IO.Readers; using log4net; using Ringtoets.Common.Data; using Ringtoets.Common.Forms.PresentationObjects; using Ringtoets.Common.IO; using Ringtoets.Integration.Plugin.Properties; using RingtoetsCommonDataResources = Ringtoets.Common.Data.Properties.Resources; using RingtoetsCommonFormsResources = Ringtoets.Common.Forms.Properties.Resources; using CoreCommonBaseResources = Core.Common.Base.Properties.Resources; namespace Ringtoets.Integration.Plugin.FileImporters { /// /// Imports instances from a shapefile that contains /// one or more polylines and stores them in a . /// public class FailureMechanismSectionsImporter : FileImporterBase { /// /// The snapping tolerance in meters. /// private const double snappingTolerance = 1; /// /// The length tolerance between the reference line and the imported FailureMechanismSections in meters. /// private const double lengthDifferenceTolerance = 1; private static readonly ILog log = LogManager.GetLogger(typeof(FailureMechanismSectionsImporter)); public override string Name { get { return RingtoetsCommonDataResources.FailureMechanism_Sections_DisplayName; } } public override string Category { get { return RingtoetsCommonFormsResources.Ringtoets_Category; } } public override Bitmap Image { get { return RingtoetsCommonFormsResources.Sections; } } public override Type SupportedItemType { get { return typeof(FailureMechanismSectionsContext); } } public override string FileFilter { get { return string.Format("{0} shapefile (*.shp)|*.shp", Name); } } public override ProgressChangedDelegate ProgressChanged { protected get; set; } public override bool CanImportOn(object targetItem) { return base.CanImportOn(targetItem) && ReferenceLineAvailable(targetItem); } public override bool Import(object targetItem, string filePath) { var context = (FailureMechanismSectionsContext)targetItem; if (context.ParentAssessmentSection.ReferenceLine == null) { LogCriticalFileReadError(Resources.FailureMechanismSectionsImporter_Import_Required_referenceline_missing); return false; } if (ImportIsCancelled) { HandleUserCancellingImport(); return false; } NotifyProgress(Resources.FailureMechanismSectionsImporter_ProgressText_Reading_file, 1, 3); ReadResult readResults = ReadFailureMechanismSections(filePath); if (readResults.CriticalErrorOccurred) { return false; } if (ImportIsCancelled) { HandleUserCancellingImport(); return false; } NotifyProgress(Resources.FailureMechanismSectionsImporter_ProgressText_Validating_imported_sections, 2, 3); if (!SectionsCorrespondToReferenceLine(context, readResults)) { LogCriticalFileReadError(Resources.FailureMechanismSectionsImporter_Import_Imported_sections_do_not_correspond_to_current_referenceline); return false; } if (ImportIsCancelled) { HandleUserCancellingImport(); return false; } NotifyProgress(Resources.FailureMechanismSectionsImporter_ProgressText_Adding_imported_data_to_failureMechanism, 3, 3); AddImportedDataToModel(context, readResults); return true; } private static bool ReferenceLineAvailable(object targetItem) { return ((FailureMechanismSectionsContext)targetItem).ParentAssessmentSection.ReferenceLine != null; } private void HandleUserCancellingImport() { log.Info(Resources.FailureMechanismSectionsImporter_Import_cancelled_no_data_read); ImportIsCancelled = false; } private ReadResult ReadFailureMechanismSections(string filePath) { using (FailureMechanismSectionReader reader = CreateFileReader(filePath)) { if (reader == null) { return new ReadResult(true); } return ReadFile(reader); } } private FailureMechanismSectionReader CreateFileReader(string filePath) { try { return new FailureMechanismSectionReader(filePath); } catch (ArgumentException e) { LogCriticalFileReadError(e); } catch (CriticalFileReadException e) { LogCriticalFileReadError(e); } return null; } private ReadResult ReadFile(FailureMechanismSectionReader reader) { try { var count = reader.GetFailureMechanismSectionCount(); if (count == 0) { LogCriticalFileReadError(Resources.FailureMechanismSectionsImporter_ReadFile_File_is_empty); return new ReadResult(true); } var importedSections = new FailureMechanismSection[count]; for (int i = 0; i < count; i++) { importedSections[i] = reader.ReadFailureMechanismSection(); } return new ReadResult(false) { ImportedItems = importedSections }; } catch (CriticalFileReadException e) { LogCriticalFileReadError(e); return new ReadResult(true); } } private void LogCriticalFileReadError(Exception exception) { LogCriticalFileReadError(exception.Message); } private void LogCriticalFileReadError(string message) { var errorMessage = String.Format(Resources.FailureMechanismSectionsImporter_CriticalErrorMessage_0_No_sections_imported, message); log.Error(errorMessage); } private bool SectionsCorrespondToReferenceLine(FailureMechanismSectionsContext context, ReadResult readResults) { ICollection failureMechanismSections = readResults.ImportedItems; ReferenceLine referenceLine = context.ParentAssessmentSection.ReferenceLine; IEnumerable allStartAndEndPoints = failureMechanismSections.Select(s => s.GetStart()).Concat(failureMechanismSections.Select(s => s.GetLast())); if (allStartAndEndPoints.Any(point => GetDistanceToReferenceLine(point, referenceLine) > snappingTolerance)) { return false; } var totalSectionsLength = failureMechanismSections.Sum(s => GetSectionLength(s)); var referenceLineLength = GetLengthOfLine(referenceLine.Points); if (Math.Abs(totalSectionsLength - referenceLineLength) > lengthDifferenceTolerance) { return false; } return true; } private double GetDistanceToReferenceLine(Point2D point, ReferenceLine referenceLine) { return GetLineSegments(referenceLine.Points) .Select(segment => segment.GetEuclideanDistanceToPoint(point)) .Min(); } private void AddImportedDataToModel(FailureMechanismSectionsContext context, ReadResult readResults) { IEnumerable snappedSections = SnapReadSectionsToReferenceLine(readResults.ImportedItems, context.ParentAssessmentSection.ReferenceLine); context.ParentFailureMechanism.ClearAllSections(); foreach (FailureMechanismSection section in snappedSections) { context.ParentFailureMechanism.AddSection(section); } } private IEnumerable SnapReadSectionsToReferenceLine(IEnumerable readSections, ReferenceLine referenceLine) { IList orderedReadSections = OrderSections(readSections); double[] orderedSectionLengths = GetReferenceLineCutoffLengths(referenceLine, orderedReadSections); Point2D[][] splitResults = Math2D.SplitLineAtLengths(referenceLine.Points, orderedSectionLengths); return CreateFailureMechanismSectionsSnappedOnReferenceLine(orderedReadSections, splitResults); } private IList OrderSections(IEnumerable unorderedSections) { List sourceList = unorderedSections.ToList(); var resultList = new List(sourceList.Count) { sourceList[0] }; GrowTowardsEnd(resultList, sourceList); GrowTowardsStart(resultList, sourceList); return resultList; } private void GrowTowardsEnd(List resultList, List sourceList) { bool doneGrowingToEnd = false; while (!doneGrowingToEnd) { Point2D endPointToConnect = resultList[resultList.Count - 1].GetLast(); var shortestDistance = double.MaxValue; FailureMechanismSection closestSectionToConnectWith = null; Dictionary sectionConnectionDistances = sourceList.ToDictionary(s => endPointToConnect.GetEuclideanDistanceTo(s.GetStart()), s => s); foreach (var sectionConnectionDistance in sectionConnectionDistances) { double distance = sectionConnectionDistance.Key; if (distance < shortestDistance && distance <= snappingTolerance) { shortestDistance = sectionConnectionDistance.Key; closestSectionToConnectWith = sectionConnectionDistance.Value; } } if (closestSectionToConnectWith == null) { doneGrowingToEnd = true; } else { resultList.Add(closestSectionToConnectWith); sourceList.Remove(closestSectionToConnectWith); } } } private void GrowTowardsStart(List resultList, List sourceList) { bool doneGrowingToStart = false; while (!doneGrowingToStart) { Point2D startPointToConnect = resultList[0].GetStart(); var shortestDistance = double.MaxValue; FailureMechanismSection closestSectionToConnectWith = null; Dictionary sectionConnectionDistances = sourceList.ToDictionary(s => startPointToConnect.GetEuclideanDistanceTo(s.GetLast()), s => s); foreach (var sectionConnectionDistance in sectionConnectionDistances) { double distance = sectionConnectionDistance.Key; if (distance < shortestDistance && distance <= snappingTolerance) { shortestDistance = sectionConnectionDistance.Key; closestSectionToConnectWith = sectionConnectionDistance.Value; } } if (closestSectionToConnectWith == null) { doneGrowingToStart = true; } else { resultList.Insert(0, closestSectionToConnectWith); sourceList.Remove(closestSectionToConnectWith); } } } private double[] GetReferenceLineCutoffLengths(ReferenceLine referenceLine, IList orderedReadSections) { double[] orderedSectionLengths = orderedReadSections.Select(GetSectionLength).ToArray(); // Correct last section to fully match referenceLine length: double difference = GetLengthOfLine(referenceLine.Points) - orderedSectionLengths.Sum(l => l); orderedSectionLengths[orderedSectionLengths.Length - 1] += difference; return orderedSectionLengths; } private static List CreateFailureMechanismSectionsSnappedOnReferenceLine(IList orderedReadSections, Point2D[][] splitResults) { var snappedSections = new List(orderedReadSections.Count); for (int i = 0; i < orderedReadSections.Count; i++) { snappedSections.Add(new FailureMechanismSection(orderedReadSections[i].Name, splitResults[i])); } return snappedSections; } private double GetSectionLength(FailureMechanismSection section) { return GetLengthOfLine(section.Points); } private double GetLengthOfLine(IEnumerable linePoints) { return GetLineSegments(linePoints).Sum(segment => segment.Length); } private IEnumerable GetLineSegments(IEnumerable linePoints) { return Math2D.ConvertLinePointsToLineSegments(linePoints); } } }