// 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.IO; using System.Linq; using Core.Common.Base; using Core.Common.Base.Geometry; using Core.Common.Base.IO; using Core.Common.IO.Exceptions; using Core.Common.IO.Readers; using Ringtoets.Common.Data.AssessmentSection; using Ringtoets.Common.Data.Exceptions; using Ringtoets.Common.IO.FileImporters.MessageProviders; using Ringtoets.MacroStabilityInwards.Data; using Ringtoets.MacroStabilityInwards.IO.Importers; using Ringtoets.MacroStabilityInwards.IO.SurfaceLines; using Ringtoets.MacroStabilityInwards.Plugin.Properties; using Ringtoets.MacroStabilityInwards.Primitives; using RingtoetsCommonIOResources = Ringtoets.Common.IO.Properties.Resources; using RingtoetsMacroStabilityInwardsDataResources = Ringtoets.MacroStabilityInwards.Data.Properties.Resources; namespace Ringtoets.MacroStabilityInwards.Plugin.FileImporter { /// /// Imports *.csv files having the following header pattern: /// Id;X1;Y1;Z1;...(Xn;Yn;Zn) /// Where Xn;Yn;Zn form the n-th 3D point describing the geometry of the surface line. /// public class MacroStabilityInwardsSurfaceLinesCsvImporter : FileImporterBase { private enum ReferenceLineIntersectionsResult { NoIntersections, OneIntersection, MultipleIntersectionsOrOverlap } private readonly IImporterMessageProvider messageProvider; private readonly ISurfaceLineUpdateDataStrategy surfaceLineUpdateStrategy; private readonly ReferenceLine referenceLine; private IEnumerable updatedInstances; /// /// Initializes a new instance of the class. /// /// The import target. /// The reference line. /// The path to the file to import from. /// The message provider to provide messages during importer actions. /// The strategy to update the surface lines with imported data. /// Thrown when any of the input parameters is null. public MacroStabilityInwardsSurfaceLinesCsvImporter(RingtoetsMacroStabilityInwardsSurfaceLineCollection importTarget, ReferenceLine referenceLine, string filePath, IImporterMessageProvider messageProvider, ISurfaceLineUpdateDataStrategy surfaceLineUpdateStrategy) : base(filePath, importTarget) { if (referenceLine == null) { throw new ArgumentNullException(nameof(referenceLine)); } if (messageProvider == null) { throw new ArgumentNullException(nameof(messageProvider)); } if (surfaceLineUpdateStrategy == null) { throw new ArgumentNullException(nameof(surfaceLineUpdateStrategy)); } this.messageProvider = messageProvider; this.surfaceLineUpdateStrategy = surfaceLineUpdateStrategy; this.referenceLine = referenceLine; updatedInstances = Enumerable.Empty(); } protected override bool OnImport() { ReadResult importSurfaceLinesResult = ReadMacroStabilityInwardsSurfaceLines(); if (importSurfaceLinesResult.CriticalErrorOccurred || Canceled) { return false; } RingtoetsMacroStabilityInwardsSurfaceLine[] importResults = GetProcessedImportedData(importSurfaceLinesResult.Items).ToArray(); if (Canceled) { return false; } NotifyProgress(messageProvider.GetAddDataToModelProgressText(), 1, 1); try { updatedInstances = surfaceLineUpdateStrategy.UpdateSurfaceLinesWithImportedData(ImportTarget, importResults, FilePath); } catch (UpdateDataException e) { string message = string.Format(messageProvider.GetUpdateDataFailedLogMessageText( RingtoetsMacroStabilityInwardsDataResources.MacroStabilityInwardsSurfaceLineCollection_TypeDescriptor), e.Message); Log.Error(message, e); return false; } return true; } protected override void LogImportCanceledMessage() { string message = messageProvider.GetCancelledLogMessageText(RingtoetsMacroStabilityInwardsDataResources.MacroStabilityInwardsSurfaceLineCollection_TypeDescriptor); Log.Info(message); } protected override void DoPostImportUpdates() { foreach (IObservable observable in updatedInstances) { observable.NotifyObservers(); } } private ReadResult HandleCriticalReadError(Exception e) { Log.ErrorFormat(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_CriticalErrorMessage_0_File_Skipped, e.Message); return new ReadResult(true); } private IEnumerable GetProcessedImportedData(ICollection readSurfaceLines) { string progressText = RingtoetsCommonIOResources.Importer_ProgressText_Validating_imported_data; NotifyProgress(progressText, 0, readSurfaceLines.Count); var surfaceLineNumber = 1; foreach (RingtoetsMacroStabilityInwardsSurfaceLine readSurfaceLine in readSurfaceLines) { NotifyProgress(progressText, surfaceLineNumber++, readSurfaceLines.Count); if (Canceled) { yield break; } ReferenceLineIntersectionResult result = CheckReferenceLineInterSections(readSurfaceLine); if (result.TypeOfIntersection != ReferenceLineIntersectionsResult.OneIntersection) { continue; } readSurfaceLine.ReferenceLineIntersectionWorldPoint = result.IntersectionPoint; yield return readSurfaceLine; } } private ReferenceLineIntersectionResult CheckReferenceLineInterSections(RingtoetsMacroStabilityInwardsSurfaceLine readSurfaceLine) { ReferenceLineIntersectionResult result = GetReferenceLineIntersections(referenceLine, readSurfaceLine); if (result.TypeOfIntersection == ReferenceLineIntersectionsResult.NoIntersections) { Log.ErrorFormat(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_CheckReferenceLineInterSections_Surfaceline_0_does_not_correspond_to_current_referenceline_1_, readSurfaceLine.Name, Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_CheckReferenceLineInterSections_This_could_be_caused_coordinates_being_local_coordinate_system); } else if (result.TypeOfIntersection == ReferenceLineIntersectionsResult.MultipleIntersectionsOrOverlap) { Log.ErrorFormat(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_CheckReferenceLineInterSections_Surfaceline_0_does_not_correspond_to_current_referenceline, readSurfaceLine.Name); } return result; } private static ReferenceLineIntersectionResult GetReferenceLineIntersections(ReferenceLine referenceLine, RingtoetsMacroStabilityInwardsSurfaceLine surfaceLine) { IEnumerable surfaceLineSegments = Math2D.ConvertLinePointsToLineSegments(surfaceLine.Points.Select(p => new Point2D(p.X, p.Y))); Segment2D[] referenceLineSegments = Math2D.ConvertLinePointsToLineSegments(referenceLine.Points).ToArray(); return GetReferenceLineIntersectionsResult(surfaceLineSegments, referenceLineSegments); } private static ReferenceLineIntersectionResult GetReferenceLineIntersectionsResult(IEnumerable surfaceLineSegments, Segment2D[] referenceLineSegments) { Point2D intersectionPoint = null; foreach (Segment2D surfaceLineSegment in surfaceLineSegments) { foreach (Segment2D referenceLineSegment in referenceLineSegments) { Segment2DIntersectSegment2DResult result = Math2D.GetIntersectionBetweenSegments(surfaceLineSegment, referenceLineSegment); if (result.IntersectionType == Intersection2DType.Intersects) { if (intersectionPoint != null) { // Early exit as multiple intersections is a return result: return ReferenceLineIntersectionResult.CreateMultipleIntersectionsOrOverlapResult(); } intersectionPoint = result.IntersectionPoints[0]; } if (result.IntersectionType == Intersection2DType.Overlaps) { // Early exit as overlap is a return result: return ReferenceLineIntersectionResult.CreateMultipleIntersectionsOrOverlapResult(); } } } return intersectionPoint != null ? ReferenceLineIntersectionResult.CreateIntersectionResult(intersectionPoint) : ReferenceLineIntersectionResult.CreateNoSingleIntersectionResult(); } private class ReferenceLineIntersectionResult { private ReferenceLineIntersectionResult(ReferenceLineIntersectionsResult typeOfIntersection, Point2D intersectionPoint) { TypeOfIntersection = typeOfIntersection; IntersectionPoint = intersectionPoint; } public ReferenceLineIntersectionsResult TypeOfIntersection { get; } public Point2D IntersectionPoint { get; } public static ReferenceLineIntersectionResult CreateNoSingleIntersectionResult() { return new ReferenceLineIntersectionResult(ReferenceLineIntersectionsResult.NoIntersections, null); } public static ReferenceLineIntersectionResult CreateIntersectionResult(Point2D point) { return new ReferenceLineIntersectionResult(ReferenceLineIntersectionsResult.OneIntersection, point); } public static ReferenceLineIntersectionResult CreateMultipleIntersectionsOrOverlapResult() { return new ReferenceLineIntersectionResult(ReferenceLineIntersectionsResult.MultipleIntersectionsOrOverlap, null); } } #region read macro stability inwards surface lines private ReadResult ReadMacroStabilityInwardsSurfaceLines() { NotifyProgress(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_Reading_surface_line_file, 1, 1); using (MacroStabilityInwardsSurfaceLinesCsvReader reader = CreateSurfaceLineReader()) { if (reader == null) { return new ReadResult(true); } Log.InfoFormat(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_ReadSurfaceLines_Start_reading_surface_lines_from_File_0_, FilePath); ReadResult readMacroStabilityInwardsSurfaceLines = ReadMacroStabilityInwardsSurfaceLines(reader); Log.InfoFormat(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_ReadSurfaceLines_Finished_reading_surface_lines_from_File_0_, FilePath); return readMacroStabilityInwardsSurfaceLines; } } private ReadResult ReadMacroStabilityInwardsSurfaceLines(MacroStabilityInwardsSurfaceLinesCsvReader reader) { int itemCount = GetNumberOfSurfaceLines(reader); if (itemCount == -1) { return new ReadResult(true); } string stepName = string.Format(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_Read_MacroStabilityInwardsSurfaceLines_0_, Path.GetFileName(FilePath)); NotifyProgress(stepName, 0, itemCount); var readSurfaceLines = new List(itemCount); for (var i = 0; i < itemCount && !Canceled; i++) { try { AddValidSurfaceLineToCollection(readSurfaceLines, reader); } catch (CriticalFileReadException e) { return HandleCriticalReadError(e); } NotifyProgress(stepName, i + 1, itemCount); } return new ReadResult(false) { Items = readSurfaceLines }; } /// /// Adds a valid read from to the . /// /// The list to add the valid to. /// The reader to read the from. /// already contains a /// with the same name as the new . private void AddValidSurfaceLineToCollection(List list, MacroStabilityInwardsSurfaceLinesCsvReader reader) { try { RingtoetsMacroStabilityInwardsSurfaceLine ringtoetsMacroStabilityInwardsSurfaceLine = reader.ReadSurfaceLine(); if (IsSurfaceLineAlreadyDefined(list, ringtoetsMacroStabilityInwardsSurfaceLine)) { PruneConsecutiveDuplicateGeometryPoints(ringtoetsMacroStabilityInwardsSurfaceLine); list.Add(ringtoetsMacroStabilityInwardsSurfaceLine); } } catch (LineParseException e) { Log.ErrorFormat(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_AddValidSurfaceLineToCollection_ParseErrorMessage_0_SurfaceLine_skipped, e.Message); } } private bool IsSurfaceLineAlreadyDefined(ICollection readSurfaceLineIdentifiers, RingtoetsMacroStabilityInwardsSurfaceLine ringtoetsMacroStabilityInwardsSurfaceLine) { if (readSurfaceLineIdentifiers.Any(i => i.Name == ringtoetsMacroStabilityInwardsSurfaceLine.Name)) { Log.WarnFormat( Resources.AddValidSurfaceLineToCollectionSurfaceLinesCsvImporter_AddImportedDataToModel_Duplicate_definitions_for_same_location_0_, ringtoetsMacroStabilityInwardsSurfaceLine.Name); return false; } return true; } private int GetNumberOfSurfaceLines(MacroStabilityInwardsSurfaceLinesCsvReader reader) { try { return reader.GetSurfaceLinesCount(); } catch (CriticalFileReadException e) { Log.ErrorFormat(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_CriticalErrorMessage_0_File_Skipped, e.Message); return -1; } } private MacroStabilityInwardsSurfaceLinesCsvReader CreateSurfaceLineReader() { try { return new MacroStabilityInwardsSurfaceLinesCsvReader(FilePath); } catch (ArgumentException e) { Log.ErrorFormat(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_CriticalErrorMessage_0_File_Skipped, e.Message); return null; } } private void PruneConsecutiveDuplicateGeometryPoints(RingtoetsMacroStabilityInwardsSurfaceLine ringtoetsMacroStabilityInwardsSurfaceLine) { Point3D[] readPoints = ringtoetsMacroStabilityInwardsSurfaceLine.Points.ToArray(); var consecutiveDuplicatePointIndices = new List(); Point3D previousPoint = null; for (var j = 0; j < readPoints.Length; j++) { if (j != 0 && readPoints[j].Equals(previousPoint)) { consecutiveDuplicatePointIndices.Add(j); previousPoint = readPoints[j]; } else { previousPoint = readPoints[j]; } } if (consecutiveDuplicatePointIndices.Any()) { Log.WarnFormat(Resources.MacroStabilityInwardsSurfaceLinesCsvImporter_SurfaceLine_0_has_multiple_duplicate_geometry_points_and_is_ignored, ringtoetsMacroStabilityInwardsSurfaceLine.Name); ringtoetsMacroStabilityInwardsSurfaceLine.SetGeometry(readPoints.Where((p, index) => !consecutiveDuplicatePointIndices.Contains(index))); } } #endregion } }