// 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.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 log4net;
using Ringtoets.Common.Data.AssessmentSection;
using Ringtoets.Piping.IO.SurfaceLines;
using Ringtoets.Piping.Primitives;
using Ringtoets.Piping.Plugin.Properties;
using RingtoetsCommonIOResources = Ringtoets.Common.IO.Properties.Resources;
namespace Ringtoets.Piping.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 PipingSurfaceLinesCsvImporter : FileImporterBase>
{
private enum ReferenceLineIntersectionsResult
{
NoIntersections,
OneIntersection,
MultipleIntersectionsOrOverlap
}
private const string characteristicPointsFileSubExtension = ".krp";
private const string csvFileExtension = ".csv";
private readonly ILog log = LogManager.GetLogger(typeof(PipingSurfaceLinesCsvImporter));
private readonly ReferenceLine referenceLine;
///
/// Initializes a new instance of the class.
///
/// The import target.
/// The reference line.
/// The path to the file to import from.
/// Thrown when
/// or is null.
public PipingSurfaceLinesCsvImporter(ObservableList importTarget, ReferenceLine referenceLine, string filePath) : base(filePath, importTarget)
{
if (referenceLine == null)
{
throw new ArgumentNullException(nameof(referenceLine));
}
this.referenceLine = referenceLine;
}
protected override bool OnImport()
{
var importSurfaceLinesResult = ReadPipingSurfaceLines();
if (importSurfaceLinesResult.CriticalErrorOccurred || Canceled)
{
return false;
}
var importCharacteristicPointsResult = ReadCharacteristicPoints();
if (importCharacteristicPointsResult.CriticalErrorOccurred || Canceled)
{
return false;
}
var importResults = ProcessImportedDataToModel(importSurfaceLinesResult.ImportedItems,
importCharacteristicPointsResult.ImportedItems).ToArray();
if (Canceled)
{
return false;
}
ImportTarget.AddRange(importResults);
return true;
}
protected override void LogImportCanceledMessage()
{
log.Info(Resources.PipingSurfaceLinesCsvImporter_Import_Import_canceled);
}
private ReadResult HandleCriticalReadError(Exception e)
{
log.ErrorFormat(Resources.PipingSurfaceLinesCsvImporter_CriticalErrorMessage_0_File_Skipped,
e.Message);
return new ReadResult(true);
}
private IEnumerable ProcessImportedDataToModel(ICollection readSurfaceLines,
ICollection readCharacteristicPointsLocations)
{
NotifyProgress(RingtoetsCommonIOResources.Importer_ProgressText_Adding_imported_data_to_DataModel, 0, readSurfaceLines.Count);
List readCharacteristicPointsLocationNames = readCharacteristicPointsLocations.Select(cpl => cpl.Name).ToList();
int surfaceLineNumber = 1;
foreach (var readSurfaceLine in readSurfaceLines)
{
NotifyProgress(RingtoetsCommonIOResources.Importer_ProgressText_Adding_imported_data_to_DataModel,
surfaceLineNumber++, readSurfaceLines.Count);
if (Canceled)
{
yield break;
}
ReferenceLineIntersectionResult result = CheckReferenceLineInterSections(readSurfaceLine);
if (result.TypeOfIntersection != ReferenceLineIntersectionsResult.OneIntersection)
{
continue;
}
readSurfaceLine.ReferenceLineIntersectionWorldPoint = result.IntersectionPoint;
CharacteristicPoints characteristicPoints = readCharacteristicPointsLocations.FirstOrDefault(cpl => cpl.Name == readSurfaceLine.Name);
if (characteristicPoints != null)
{
if (!CheckCharacteristicPoints(readSurfaceLine, characteristicPoints))
{
continue;
}
SetCharacteristicPointsOnSurfaceLine(readSurfaceLine, characteristicPoints);
readCharacteristicPointsLocationNames.Remove(characteristicPoints.Name);
}
else if (readCharacteristicPointsLocations.Count > 0)
{
log.WarnFormat(Resources.PipingSurfaceLinesCsvImporter_AddImportedDataToModel_No_characteristic_points_for_SurfaceLine_0_,
readSurfaceLine.Name);
}
yield return readSurfaceLine;
}
foreach (string name in readCharacteristicPointsLocationNames)
{
log.WarnFormat(Resources.PipingSurfaceLinesCsvImporter_AddImportedDataToModel_Characteristic_points_found_for_unknown_SurfaceLine_0_,
name);
}
}
private bool CheckCharacteristicPoints(RingtoetsPipingSurfaceLine readSurfaceLine, CharacteristicPoints characteristicPoints)
{
if (characteristicPoints.DikeToeAtRiver == null || characteristicPoints.DikeToeAtPolder == null)
{
return true;
}
var localDikeToeAtRiver = readSurfaceLine.GetLocalPointFromGeometry(characteristicPoints.DikeToeAtRiver);
var localDikeToeAtPolder = readSurfaceLine.GetLocalPointFromGeometry(characteristicPoints.DikeToeAtPolder);
if (localDikeToeAtPolder.X <= localDikeToeAtRiver.X)
{
log.WarnFormat(Resources.PipingSurfaceLinesCsvImporter_CheckCharacteristicPoints_EntryPointL_greater_or_equal_to_ExitPointL_for_0_, characteristicPoints.Name);
return false;
}
return true;
}
private ReferenceLineIntersectionResult CheckReferenceLineInterSections(RingtoetsPipingSurfaceLine readSurfaceLine)
{
ReferenceLineIntersectionResult result = GetReferenceLineIntersections(referenceLine, readSurfaceLine);
if (result.TypeOfIntersection == ReferenceLineIntersectionsResult.NoIntersections)
{
log.ErrorFormat(Resources.PipingSurfaceLinesCsvImporter_CheckReferenceLineInterSections_Surfaceline_0_does_not_correspond_to_current_referenceline_1_,
readSurfaceLine.Name,
Resources.PipingSurfaceLinesCsvImporter_CheckReferenceLineInterSections_This_could_be_caused_coordinates_being_local_coordinate_system);
}
else if (result.TypeOfIntersection == ReferenceLineIntersectionsResult.MultipleIntersectionsOrOverlap)
{
log.ErrorFormat(Resources.PipingSurfaceLinesCsvImporter_CheckReferenceLineInterSections_Surfaceline_0_does_not_correspond_to_current_referenceline, readSurfaceLine.Name);
}
return result;
}
private static ReferenceLineIntersectionResult GetReferenceLineIntersections(ReferenceLine referenceLine, RingtoetsPipingSurfaceLine surfaceLine)
{
var 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 static void SetCharacteristicPointsOnSurfaceLine(RingtoetsPipingSurfaceLine readSurfaceLine, CharacteristicPoints characteristicPointsLocation)
{
readSurfaceLine.TrySetDitchPolderSide(characteristicPointsLocation.DitchPolderSide);
readSurfaceLine.TrySetBottomDitchPolderSide(characteristicPointsLocation.BottomDitchPolderSide);
readSurfaceLine.TrySetBottomDitchDikeSide(characteristicPointsLocation.BottomDitchDikeSide);
readSurfaceLine.TrySetDitchDikeSide(characteristicPointsLocation.DitchDikeSide);
readSurfaceLine.TrySetDikeToeAtRiver(characteristicPointsLocation.DikeToeAtRiver);
readSurfaceLine.TrySetDikeToeAtPolder(characteristicPointsLocation.DikeToeAtPolder);
}
private class ReferenceLineIntersectionResult
{
private ReferenceLineIntersectionResult(ReferenceLineIntersectionsResult typeOfIntersection, Point2D intersectionPoint)
{
TypeOfIntersection = typeOfIntersection;
IntersectionPoint = intersectionPoint;
}
public ReferenceLineIntersectionsResult TypeOfIntersection { get; private set; }
public Point2D IntersectionPoint { get; private set; }
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 piping surface lines
private ReadResult ReadPipingSurfaceLines()
{
NotifyProgress(Resources.PipingSurfaceLinesCsvImporter_Reading_surface_line_file, 1, 1);
using (PipingSurfaceLinesCsvReader reader = CreateSurfaceLineReader())
{
if (reader == null)
{
return new ReadResult(true);
}
log.InfoFormat(Resources.PipingSurfaceLinesCsvImporter_ReadSurfaceLines_Start_reading_surface_lines_from_File_0_,
FilePath);
ReadResult readPipingSurfaceLines = ReadPipingSurfaceLines(reader);
log.InfoFormat(Resources.PipingSurfaceLinesCsvImporter_ReadSurfaceLines_Finished_reading_surface_lines_from_File_0_,
FilePath);
return readPipingSurfaceLines;
}
}
private ReadResult ReadPipingSurfaceLines(PipingSurfaceLinesCsvReader reader)
{
int itemCount = GetNumberOfSurfaceLines(reader);
if (itemCount == -1)
{
return new ReadResult(true);
}
var stepName = string.Format(Resources.PipingSurfaceLinesCsvImporter_Read_PipingSurfaceLines_0_,
Path.GetFileName(FilePath));
NotifyProgress(stepName, 0, itemCount);
var readSurfaceLines = new List(itemCount);
for (int 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)
{
ImportedItems = 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, PipingSurfaceLinesCsvReader reader)
{
try
{
var ringtoetsPipingSurfaceLine = reader.ReadSurfaceLine();
if (IsSurfaceLineAlreadyDefined(list, ringtoetsPipingSurfaceLine))
{
PruneConsecutiveDuplicateGeometryPoints(ringtoetsPipingSurfaceLine);
list.Add(ringtoetsPipingSurfaceLine);
}
}
catch (LineParseException e)
{
log.ErrorFormat(Resources.PipingSurfaceLinesCsvImporter_ReadPipingSurfaceLines_ParseErrorMessage_0_SurfaceLine_skipped,
e.Message);
}
}
private bool IsSurfaceLineAlreadyDefined(ICollection readSurfaceLineIdentifiers, RingtoetsPipingSurfaceLine ringtoetsPipingSurfaceLine)
{
if (readSurfaceLineIdentifiers.Any(i => i.Name == ringtoetsPipingSurfaceLine.Name))
{
log.WarnFormat(
Resources.PipingSurfaceLinesCsvImporter_AddImportedDataToModel_Duplicate_definitions_for_same_location_0_,
ringtoetsPipingSurfaceLine.Name);
return false;
}
return true;
}
private int GetNumberOfSurfaceLines(PipingSurfaceLinesCsvReader reader)
{
try
{
return reader.GetSurfaceLinesCount();
}
catch (CriticalFileReadException e)
{
log.ErrorFormat(Resources.PipingSurfaceLinesCsvImporter_CriticalErrorMessage_0_File_Skipped,
e.Message);
return -1;
}
}
private PipingSurfaceLinesCsvReader CreateSurfaceLineReader()
{
try
{
return new PipingSurfaceLinesCsvReader(FilePath);
}
catch (ArgumentException e)
{
log.ErrorFormat(Resources.PipingSurfaceLinesCsvImporter_CriticalErrorMessage_0_File_Skipped,
e.Message);
return null;
}
}
private void PruneConsecutiveDuplicateGeometryPoints(RingtoetsPipingSurfaceLine ringtoetsPipingSurfaceLine)
{
Point3D[] readPoints = ringtoetsPipingSurfaceLine.Points.ToArray();
var consecutiveDuplicatePointIndices = new List();
Point3D previousPoint = null;
for (int 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.PipingSurfaceLinesCsvImporter_SurfaceLine_0_has_multiple_duplicate_geometry_points_and_is_ignored,
ringtoetsPipingSurfaceLine.Name);
ringtoetsPipingSurfaceLine.SetGeometry(readPoints.Where((p, index) => !consecutiveDuplicatePointIndices.Contains(index)));
}
}
#endregion
#region read characteristic points
private ReadResult ReadCharacteristicPoints()
{
NotifyProgress(Resources.PipingSurfaceLinesCsvImporter_Reading_characteristic_points_file, 1, 1);
string characteristicPointsFilePath = GetCharacteristicPointsFilePath();
if (characteristicPointsFilePath == null)
{
return new ReadResult(false);
}
using (CharacteristicPointsCsvReader reader = CreateCharacteristicPointsReader(characteristicPointsFilePath))
{
if (reader == null)
{
return new ReadResult(true);
}
log.InfoFormat(Resources.PipingSurfaceLinesCsvImporter_ReadCharacteristicPoints_Start_reading_characteristic_points_from_File_0_,
characteristicPointsFilePath);
ReadResult readCharacteristicPoints = ReadCharacteristicPoints(characteristicPointsFilePath, reader);
log.InfoFormat(Resources.PipingSurfaceLinesCsvImporter_ReadCharacteristicPoints_Finished_reading_characteristic_points_from_File_0_,
characteristicPointsFilePath);
return readCharacteristicPoints;
}
}
private ReadResult ReadCharacteristicPoints(string path, CharacteristicPointsCsvReader reader)
{
int itemCount = GetNumberOfCharacteristicPointLocations(reader);
if (itemCount == -1)
{
return new ReadResult(true);
}
var stepName = string.Format(Resources.PipingSurfaceLinesCsvImporter_Read_PipingCharacteristicPoints_0_,
Path.GetFileName(path));
NotifyProgress(stepName, 0, itemCount);
var readCharacteristicPointsLocations = new List(itemCount);
for (int i = 0; i < itemCount && !Canceled; i++)
{
try
{
AddValidCharacteristicPointsLocationToCollection(readCharacteristicPointsLocations, reader);
}
catch (CriticalFileReadException e)
{
return HandleCriticalReadError(e);
}
NotifyProgress(stepName, i + 1, itemCount);
}
return new ReadResult(false)
{
ImportedItems = readCharacteristicPointsLocations
};
}
///
/// 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 AddValidCharacteristicPointsLocationToCollection(ICollection list, CharacteristicPointsCsvReader reader)
{
try
{
CharacteristicPoints location = reader.ReadCharacteristicPointsLocation();
if (IsCharacteristicPointsLocationsAlreadyDefined(list, location))
{
list.Add(location);
}
}
catch (LineParseException e)
{
log.ErrorFormat(Resources.PipingSurfaceLinesCsvImporter_ReadCharacteristicPoints_ParseErrorMessage_0_CharacteristicPoints_skipped,
e.Message);
}
}
private bool IsCharacteristicPointsLocationsAlreadyDefined(IEnumerable list, CharacteristicPoints location)
{
if (list.Any(i => i.Name == location.Name))
{
log.WarnFormat(Resources.PipingSurfaceLinesCsvImporter_AddImportedDataToModel_Duplicate_definitions_for_same_characteristic_point_location_0_,
location.Name);
return false;
}
return true;
}
private int GetNumberOfCharacteristicPointLocations(CharacteristicPointsCsvReader reader)
{
try
{
return reader.GetLocationsCount();
}
catch (CriticalFileReadException e)
{
log.ErrorFormat(Resources.PipingSurfaceLinesCsvImporter_CriticalErrorMessage_0_File_Skipped,
e.Message);
return -1;
}
}
private CharacteristicPointsCsvReader CreateCharacteristicPointsReader(string path)
{
try
{
return new CharacteristicPointsCsvReader(path);
}
catch (ArgumentException e)
{
log.ErrorFormat(Resources.PipingSurfaceLinesCsvImporter_CriticalErrorMessage_0_File_Skipped,
e.Message);
return null;
}
}
private string GetCharacteristicPointsFilePath()
{
var path = FilePath.Insert(FilePath.Length - csvFileExtension.Length, characteristicPointsFileSubExtension);
if (!File.Exists(path))
{
log.InfoFormat(Resources.PipingSurfaceLinesCsvImporter_Import_No_characteristic_points_file_for_surface_line_file_expecting_file_0_, path);
return null;
}
return path;
}
#endregion
}
}