// 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.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using Core.Common.Base.Data;
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.GrassCoverErosionInwards.Data;
using Ringtoets.GrassCoverErosionInwards.Forms.PresentationObjects;
using Ringtoets.GrassCoverErosionInwards.IO.DikeProfiles;
using Ringtoets.GrassCoverErosionInwards.Plugin.Properties;
using CoreCommonUtilsResources = Core.Common.Utils.Properties.Resources;
using RingtoetsFormsResources = Ringtoets.Common.Forms.Properties.Resources;
using RingtoetsCommonDataResources = Ringtoets.Common.Data.Properties.Resources;
using RingtoetsCommonIOResources = Ringtoets.Common.IO.Properties.Resources;
namespace Ringtoets.GrassCoverErosionInwards.Plugin.FileImporter
{
///
/// Imports point shapefiles containing dike profile locations and text file containing the foreland/dike schematizations.
///
public class DikeProfilesImporter : FileImporterBase
{
private readonly ILog log = LogManager.GetLogger(typeof(DikeProfilesImporter));
public override string Name
{
get
{
return Resources.DikeProfilesImporter_DisplayName;
}
}
public override string Category
{
get
{
return RingtoetsFormsResources.Ringtoets_Category;
}
}
public override Bitmap Image
{
get
{
return Resources.DikeProfile;
}
}
public override string FileFilter
{
get
{
return string.Format(RingtoetsCommonIOResources.DikeProfilesImport_FileFilter_0_1_shapefile_extension,
Resources.DikeProfilesImporter_DisplayName, RingtoetsCommonDataResources.DikeProfilesImporter_FileFilter_Shapefile);
}
}
public override ProgressChangedDelegate ProgressChanged { protected get; set; }
public override bool CanImportOn(object targetItem)
{
return base.CanImportOn(targetItem) && IsReferenceLineAvailable(targetItem);
}
public override bool Import(object targetItem, string filePath)
{
var dikeProfilesContext = (DikeProfilesContext) targetItem;
if (!IsReferenceLineAvailable(dikeProfilesContext))
{
log.Error(Resources.DikeProfilesImporter_Import_no_referenceline_import_aborted);
return false;
}
ReferenceLine referenceLine = dikeProfilesContext.ParentAssessmentSection.ReferenceLine;
ReadResult importDikeProfilesResult = ReadDikeProfileLocations(filePath, referenceLine);
if (importDikeProfilesResult.CriticalErrorOccurred)
{
return false;
}
if (ImportIsCancelled)
{
HandleUserCancellingImport();
return false;
}
string folderPath = Path.GetDirectoryName(filePath);
ReadResult importDikeProfileDataResult = ReadDikeProfileData(folderPath);
if (importDikeProfileDataResult.CriticalErrorOccurred)
{
return false;
}
if (ImportIsCancelled)
{
HandleUserCancellingImport();
return false;
}
IEnumerable dikeProfiles = CreateDikeProfiles(importDikeProfilesResult.ImportedItems, importDikeProfileDataResult.ImportedItems);
foreach (DikeProfile dikeProfile in dikeProfiles)
{
dikeProfilesContext.WrappedData.Add(dikeProfile);
}
return true;
}
private ReadResult ReadDikeProfileLocations(string filePath, ReferenceLine referenceLine)
{
NotifyProgress(Resources.DikeProfilesImporter_ReadDikeProfileLocations_reading_dikeprofilelocations, 1, 1);
try
{
using (var dikeProfileLocationReader = new DikeProfileLocationReader(filePath))
{
return GetDikeProfileLocationReadResult(dikeProfileLocationReader, referenceLine);
}
}
catch (CriticalFileReadException exception)
{
log.Error(exception.Message);
}
catch (ArgumentException exception)
{
log.Error(exception.Message);
}
return new ReadResult(true);
}
private ReadResult GetDikeProfileLocationReadResult(DikeProfileLocationReader dikeProfileLocationReader, ReferenceLine referenceLine)
{
var dikeProfileLocations = new Collection();
int totalNumberOfSteps = dikeProfileLocationReader.GetLocationCount;
for (int i = 0; i < totalNumberOfSteps; i++)
{
if (ImportIsCancelled)
{
return new ReadResult(false);
}
try
{
NotifyProgress(Resources.DikeProfilesImporter_GetDikeProfileLocationReadResult_reading_dikeprofilelocation, i + 1, totalNumberOfSteps);
AddNextDikeProfileLocation(dikeProfileLocationReader, referenceLine, dikeProfileLocations);
}
catch (CriticalFileReadException exception)
{
log.Error(exception.Message);
return new ReadResult(true);
}
}
return new ReadResult(false)
{
ImportedItems = dikeProfileLocations
};
}
///
/// Get the next DikeProfileLocation from and add to in case it is close enough to .
///
/// Reader reading DikeProfileLocations for a shapefile.
/// The reference line.
/// Collection of DikeProfileLocations to which a new DikeProfileLocation is to be added.
///
/// - The shapefile misses a value for a required attribute.
/// - The shapefile has an attribute whose type is incorrect.
///
private void AddNextDikeProfileLocation(DikeProfileLocationReader dikeProfileLocationReader, ReferenceLine referenceLine, Collection dikeProfileLocations)
{
DikeProfileLocation dikeProfileLocation = dikeProfileLocationReader.GetNextDikeProfileLocation();
double distanceToReferenceLine = GetDistanceToReferenceLine(dikeProfileLocation.Point, referenceLine);
if (distanceToReferenceLine > 1.0)
{
log.Error(string.Format(Resources.DikeProfilesImporter_AddNextDikeProfileLocation_0_skipping_location_outside_referenceline, dikeProfileLocation.Id));
return;
}
dikeProfileLocations.Add(dikeProfileLocation);
}
private ReadResult ReadDikeProfileData(string folderPath)
{
NotifyProgress(Resources.DikeProfilesImporter_ReadDikeProfileData_reading_dikeprofile_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();
Dictionary> duplicates = new Dictionary>();
var dikeProfileDataReader = new DikeProfileDataReader();
for (int i = 0; i < totalNumberOfSteps; i++)
{
if (ImportIsCancelled)
{
return new ReadResult(false);
}
string prflFilePath = prflFilePaths[i];
try
{
NotifyProgress(Resources.DikeProfilesImporter_ReadDikeProfileData_reading_dikeprofiledata, i + 1, totalNumberOfSteps);
DikeProfileData data = dikeProfileDataReader.ReadDikeProfileData(prflFilePath);
if (data.ProfileType != ProfileType.Coordinates)
{
log.Error(string.Format(Resources.DikeProfilesImporter_ReadDikeProfileData_sheet_piling_not_zero_skipping_0_, prflFilePath));
continue;
}
if (dikeProfileData.Any(d => d.Id.Equals(data.Id)))
{
UpdateDuplicates(duplicates, data, prflFilePath);
}
else
{
dikeProfileData.Add(data);
}
}
// No need to catch ArgumentException, as prflFilePaths are valid by construction.
catch (CriticalFileReadException exception)
{
log.Error(exception.Message);
return new ReadResult(true);
}
}
LogDuplicateDikeProfileData(duplicates);
return new ReadResult(false)
{
ImportedItems = dikeProfileData
};
}
private static void UpdateDuplicates(Dictionary> duplicates, DikeProfileData data, string prflFilePath)
{
if (!duplicates.ContainsKey(data.Id))
{
duplicates.Add(data.Id, new List());
}
duplicates[data.Id].Add(prflFilePath);
}
private void LogDuplicateDikeProfileData(Dictionary> duplicates)
{
foreach (KeyValuePair> keyValuePair in duplicates)
{
StringBuilder builder = new StringBuilder(
string.Format(Resources.DikeProfilesImporter_LogDuplicateDikeProfileData_dikeprofiledata_file_with_id_0_used_files_1_are_skipped,
keyValuePair.Key, keyValuePair.Value.Count));
foreach (string filePath in keyValuePair.Value)
{
if (BuilderNotAtMaxCapacity(builder, filePath))
{
builder.AppendLine(filePath);
}
}
string message = builder.ToString();
log.Error(message);
}
}
private static bool BuilderNotAtMaxCapacity(StringBuilder builder, string filePath)
{
return builder.Length + filePath.Length + Environment.NewLine.Length < builder.MaxCapacity;
}
private IEnumerable CreateDikeProfiles(ICollection dikeProfileLocationCollection, ICollection dikeProfileDataCollection)
{
List dikeProfiles = new List();
foreach (DikeProfileLocation dikeProfileLocation in dikeProfileLocationCollection)
{
string id = dikeProfileLocation.Id;
var dikeProfileData = GetMatchingDikeProfileData(dikeProfileDataCollection, id);
if (dikeProfileData == null)
{
log.Error(string.Format(Resources.DikeProfilesImporter_GetMatchingDikeProfileData_no_dikeprofiledata_for_location_0_, id));
}
else
{
DikeProfile dikeProfile = CreateDikeProfile(dikeProfileLocation, dikeProfileData);
dikeProfiles.Add(dikeProfile);
}
}
return dikeProfiles;
}
private DikeProfileData GetMatchingDikeProfileData(ICollection dikeProfileDataCollection, string id)
{
DikeProfileData matchingDikeProfileData = dikeProfileDataCollection.FirstOrDefault(d => d.Id.Equals(id));
return matchingDikeProfileData;
}
private static DikeProfile CreateDikeProfile(DikeProfileLocation dikeProfileLocation, DikeProfileData dikeProfileData)
{
var dikeProfile = new DikeProfile(dikeProfileLocation.Point, dikeProfileData.DikeGeometry, dikeProfileData.ForeshoreGeometry.Select(fg => fg.Point).ToArray())
{
Name = dikeProfileData.Id,
Memo = dikeProfileData.Memo,
X0 = dikeProfileLocation.Offset,
Orientation = (RoundedDouble) dikeProfileData.Orientation,
DikeHeight = (RoundedDouble) dikeProfileData.DikeHeight
};
switch (dikeProfileData.DamType)
{
case DamType.None:
dikeProfile.BreakWater = null;
break;
case DamType.Caisson:
dikeProfile.BreakWater = new BreakWater(BreakWaterType.Caisson, dikeProfileData.DamHeight);
break;
case DamType.HarborDam:
dikeProfile.BreakWater = new BreakWater(BreakWaterType.Dam, dikeProfileData.DamHeight);
break;
case DamType.Vertical:
dikeProfile.BreakWater = new BreakWater(BreakWaterType.Wall, dikeProfileData.DamHeight);
break;
default:
// Invalid values are caught as exceptions from DikeProfileDataReader.
break;
}
return dikeProfile;
}
private void HandleUserCancellingImport()
{
log.Info(Resources.DikeProfilesImporter_HandleUserCancellingImport_dikeprofile_import_aborted);
ImportIsCancelled = false;
}
private static bool IsReferenceLineAvailable(object targetItem)
{
return ((DikeProfilesContext)targetItem).ParentAssessmentSection.ReferenceLine != null;
}
private double GetDistanceToReferenceLine(Point2D point, ReferenceLine referenceLine)
{
return GetLineSegments(referenceLine.Points).Min(segment => segment.GetEuclideanDistanceToPoint(point));
}
private IEnumerable GetLineSegments(IEnumerable linePoints)
{
return Math2D.ConvertLinePointsToLineSegments(linePoints);
}
}
}