// 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.Collections.ObjectModel;
using System.IO;
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.AssessmentSection;
using Ringtoets.Common.Data.DikeProfiles;
using Ringtoets.Common.IO.DikeProfiles;
using Ringtoets.Common.IO.Exceptions;
using Ringtoets.Common.IO.Properties;
namespace Ringtoets.Common.IO.FileImporters
{
///
/// Abstract class for profile importers, providing an implementation of importing point shapefiles
/// containing dike profile locations and text files containing dike schematizations.
///
///
public abstract class ProfilesImporter : FileImporterBase
{
protected readonly ILog Log = LogManager.GetLogger(typeof(ProfilesImporter));
private readonly ReferenceLine referenceLine;
///
/// Initializes a new instance of .
///
/// The reference line used to check if the imported profiles are intersecting it.
/// The path to the file to import from.
/// The import target.
/// Thrown when ,
/// or is null.
protected ProfilesImporter(ReferenceLine referenceLine, string filePath, T importTarget) : base(filePath, importTarget)
{
if (referenceLine == null)
{
throw new ArgumentNullException(nameof(referenceLine));
}
this.referenceLine = referenceLine;
}
protected override bool OnImport()
{
ReadResult importDikeProfilesResult = ReadProfileLocations();
if (importDikeProfilesResult.CriticalErrorOccurred || Canceled)
{
return false;
}
string folderPath = Path.GetDirectoryName(FilePath);
string[] acceptedIds = importDikeProfilesResult.Items.Select(dp => dp.Id).ToArray();
ReadResult importDikeProfileDataResult = ReadDikeProfileData(folderPath, acceptedIds);
if (importDikeProfileDataResult.CriticalErrorOccurred || Canceled)
{
return false;
}
NotifyProgress(Resources.Importer_ProgressText_Adding_imported_data_to_DataModel, 1, 1);
CreateProfiles(importDikeProfilesResult, importDikeProfileDataResult);
return true;
}
///
/// Create profile objects from location and geometry data.
///
/// The read profile locations.
/// The read dike profile geometries.
protected abstract void CreateProfiles(ReadResult importProfileLocationResult,
ReadResult importDikeProfileDataResult);
///
/// Construct a from a dike profile geometry.
///
/// The dike profile geometry.
/// A new .
protected static BreakWater CreateBreakWater(DikeProfileData dikeProfileData)
{
switch (dikeProfileData.DamType)
{
case DamType.Caisson:
return new BreakWater(BreakWaterType.Caisson, dikeProfileData.DamHeight);
case DamType.HarborDam:
return new BreakWater(BreakWaterType.Dam, dikeProfileData.DamHeight);
case DamType.Vertical:
return new BreakWater(BreakWaterType.Wall, dikeProfileData.DamHeight);
}
return null;
}
///
/// Obtain the dike profile geometry object matching a given .
///
/// The available dike profile geometry objects.
/// The id on which to match.
/// The matching .
protected static DikeProfileData GetMatchingDikeProfileData(IEnumerable dikeProfileDataCollection, string id)
{
return dikeProfileDataCollection.FirstOrDefault(d => d.Id.Equals(id));
}
///
/// Validate the consistency of a object.
///
/// The to validate.
/// Filepath of the profile data file.
/// Value indicating whether the is valid.
protected abstract bool DikeProfileDataIsValid(DikeProfileData data, string prflFilePath);
private ReadResult ReadProfileLocations()
{
NotifyProgress(Resources.ProfilesImporter_ReadProfileLocations_reading_profilelocations, 1, 1);
try
{
using (var profileLocationReader = new ProfileLocationReader(FilePath))
{
return GetProfileLocationReadResult(profileLocationReader);
}
}
catch (CriticalFileReadException exception)
{
Log.Error(exception.Message);
}
catch (ArgumentException exception)
{
Log.Error(exception.Message);
}
return new ReadResult(true);
}
private ReadResult GetProfileLocationReadResult(ProfileLocationReader profileLocationReader)
{
var profileLocations = new Collection();
int totalNumberOfSteps = profileLocationReader.GetLocationCount;
for (int i = 0; i < totalNumberOfSteps; i++)
{
if (Canceled)
{
return new ReadResult(false);
}
try
{
NotifyProgress(Resources.ProfilesImporter_GetProfileLocationReadResult_reading_profilelocation, i + 1, totalNumberOfSteps);
AddNextProfileLocation(profileLocationReader, profileLocations);
}
catch (LineParseException exception)
{
var message = string.Format(
Resources.ProfilesImporter_GetProfileLocationReadResult_Error_reading_Profile_LineNumber_0_Error_1_The_Profile_is_skipped,
i + 1,
exception.Message);
Log.Warn(message);
}
catch (CriticalFileReadException exception)
{
Log.Error(exception.Message);
return new ReadResult(true);
}
}
return new ReadResult(false)
{
Items = profileLocations
};
}
///
/// Get the next from
/// and add to in case it is close enough to the .
///
/// Reader reading objects from a shapefile.
/// Collection of objects
/// to which the new is to be added.
///
/// - The shapefile misses a value for a required attribute.
/// - The shapefile has an attribute whose type is incorrect.
/// - The read is outside the reference line.
///
private void AddNextProfileLocation(ProfileLocationReader profileLocationReader, ICollection profileLocations)
{
ProfileLocation profileLocation = profileLocationReader.GetNextProfileLocation();
double distanceToReferenceLine = GetDistanceToReferenceLine(profileLocation.Point);
if (distanceToReferenceLine > 1.0)
{
throw new LineParseException(string.Format(Resources.ProfilesImporter_AddNextProfileLocation_Location_with_id_0_outside_referenceline, profileLocation.Id));
}
if (profileLocations.Any(dpl => dpl.Id.Equals(profileLocation.Id)))
{
Log.WarnFormat(Resources.ProfilesImporter_AddNextProfileLocation_Location_with_id_0_already_read, profileLocation.Id);
}
profileLocations.Add(profileLocation);
}
private ReadResult ReadDikeProfileData(string folderPath, string[] acceptedIds)
{
NotifyProgress(Resources.ProfilesImporter_ReadDikeProfileData_reading_profile_data, 1, 1);
// No exception handling for GetFiles, as folderPath is derived from an existing, read file.
string[] prflFilePaths = Directory.GetFiles(folderPath, "*.prfl");
int totalNumberOfSteps = prflFilePaths.Length;
var dikeProfileData = new Collection();
var dikeProfileDataReader = new DikeProfileDataReader(acceptedIds);
var errorOccured = false;
for (int i = 0; i < totalNumberOfSteps; i++)
{
if (Canceled)
{
return new ReadResult(false);
}
string prflFilePath = prflFilePaths[i];
try
{
NotifyProgress(Resources.ProfilesImporter_ReadDikeProfileData_reading_profiledata, i + 1, totalNumberOfSteps);
DikeProfileData data = dikeProfileDataReader.ReadDikeProfileData(prflFilePath);
if (!DikeProfileDataIsValid(data, prflFilePath))
{
continue;
}
if (data.SheetPileType != SheetPileType.Coordinates)
{
Log.Error(string.Format(Resources.ProfilesImporter_ReadDikeProfileData_sheet_piling_not_zero_skipping_0_, prflFilePath));
continue;
}
if (dikeProfileData.Any(d => d.Id.Equals(data.Id)))
{
LogDuplicate(data, prflFilePath);
}
else
{
dikeProfileData.Add(data);
}
}
// No need to catch ArgumentException, as prflFilePaths are valid by construction.
catch (CriticalFileReadException exception)
{
Log.Error(exception.Message);
errorOccured = true;
}
catch (CriticalFileValidationException)
{
// Ignore file
}
}
return errorOccured && !dikeProfileData.Any()
? new ReadResult(true)
: new ReadResult(false)
{
Items = dikeProfileData
};
}
private void LogDuplicate(DikeProfileData data, string prflFilePath)
{
var message = String.Format(
Resources.ProfilesImporter_LogDuplicateDikeProfileData_Multiple_DikeProfileData_found_for_DikeProfile_0_File_1_skipped,
data.Id,
prflFilePath);
Log.Error(message);
}
private double GetDistanceToReferenceLine(Point2D point)
{
return GetLineSegments(referenceLine.Points).Min(segment => segment.GetEuclideanDistanceToPoint(point));
}
private static IEnumerable GetLineSegments(IEnumerable linePoints)
{
return Math2D.ConvertLinePointsToLineSegments(linePoints);
}
}
}