//-----------------------------------------------------------------------
//
// Copyright (c) 2011 Deltares. All rights reserved.
//
// J. Bokma
// j.bokma@deltares.nl
// 02-02-2011
// n.a.
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Deltares.Dam.Data.DataPlugins.Configuration;
using Deltares.Geotechnics;
using Deltares.Soilbase;
using Deltares.Standard.EventPublisher;
using Deltares.Standard.IO.DtoAssembler;
using Deltares.Standard.Reflection;
using System.IO;
using Deltares.Geotechnics.Soils;
using Deltares.Standard;
using Deltares.Standard.IO;
using Deltares.Standard.IO.Xml;
using Deltares.Standard.Logging;
namespace Deltares.Dam.Data
{
public class DamProjectException : ApplicationException
{
public DamProjectException(string message)
: base(message)
{
}
}
public class DamProject : IDisposable
{
private string projectFileName;
private DamProjectData damProjectData;
private DamProjectSettings damProjectSettings;
public static ProjectPathLocation ProjectWorkingPathLocation = ProjectPathLocation.InApplicationMap;
public static string UserWorkingPath = DamProject.GetNewTempDirectory();
public static string ProjectMapWorkingPath = DamProject.GetNewTempDirectory();
public static string ProjectMap = "";
public DamProject()
{
this.damProjectData = new DamProjectData();
this.damProjectSettings = new DamProjectSettings();
}
public static string GetNewTempDirectory()
{
int counter = 1;
while (true)
{
string dir = Path.GetTempPath() + Path.DirectorySeparatorChar + "DAM" + counter.ToString();
if (!Directory.Exists(dir) && !File.Exists(dir))
{
return dir;
}
else
{
counter++;
}
}
}
public string ProjectFileName
{
get { return this.projectFileName; }
set
{
DataEventPublisher.BeforeChange(this, "ProjectFileName");
this.projectFileName = value;
ProjectMap = Path.GetDirectoryName(Path.GetFullPath(value));
ProjectMapWorkingPath = Path.GetFullPath(Path.ChangeExtension(value, "Calc"));
//damProjectFolder = Path.GetDirectoryName(value);
DataEventPublisher.AfterChange(this, "ProjectFileName");
}
}
//[XmlIgnore]
public DamProjectData DamProjectData
{
get { return damProjectData; }
set
{
var propertyName = this.GetMemberName(x => x.DamProjectData);
DataEventPublisher.BeforeChange(this, propertyName);
damProjectData = value;
DataEventPublisher.AfterChange(this, propertyName);
}
}
//[XmlIgnore]
public DamProjectSettings DamProjectSettings
{
get { return this.damProjectSettings; }
set { this.damProjectSettings = value; }
}
public object ImportWithDefinitionFile(string fileName, DamType damType, DamProjectType damProjectType, ProgressDelegate progress)
{
var dataSourceContainer = DataSourceContainer.Deserialize(fileName);
ProjectFileName = fileName;
var definitionFileProjectFolder = Path.GetDirectoryName(fileName);
List logMessages;
// import
var waterBoard = WaterBoardImporter.ImportAllData(
definitionFileProjectFolder, dataSourceContainer, damType, damProjectType,
progress, out logMessages);
//var importer = new WaterBoardImporter();
//var waterBoard = importer.ImportAllData(
// definitionFileProjectFolder, dataSourceContainer, damType, progress);
//logMessages = importer.importLogMessages.ToList();
// create data
DamProjectData = new DamProjectData { WaterBoard = waterBoard };
// handle log messages
if (logMessages.Count > 0)
{
LogManager.Messages.AddRange(logMessages);
}
// Add stability as standard mechanism when not yet available.
if (damProjectData.DamProjectCalculationSpecification.DamCalculationSpecifications.Count == 0)
{
damProjectData.DamProjectCalculationSpecification.DamCalculationSpecifications
.Add(new DamFailureMechanismeCalculationSpecification());
}
return DamProjectData;
}
///
/// Imports all the data
///
/// The data folder.
/// The data source container.
/// The dike ring ids.
/// Type of the dam.
/// Type of the dam project.
/// The progress.
///
public object Import(string dataFolder, DataSourceContainer dataSourceContainer,
IEnumerable dikeRingIds, DamType damType, DamProjectType damProjectType, ProgressDelegate progress)
{
var damProjectFolder = Path.GetDirectoryName(projectFileName);
DataSourceManager.DataSources = DamProjectData.DataSources;
List logMessages;
// import
var waterBoard = WaterBoardImporter.ImportDataForDikeRings(
damProjectFolder, dataFolder, dataSourceContainer,
dikeRingIds, damType, damProjectType, progress, out logMessages);
// create data
DamProjectData = new DamProjectData { WaterBoard = waterBoard, DataSourceEsriProjection = dataSourceContainer.DataSourceEsriProjection };
// Make sure Soil properties are same as in database, also build IsAquifer dictionary
UpdateSoilPropertiesToSoilDatabase();
// handle logs
if (logMessages.Any())
{
LogManager.Messages.Clear();
LogManager.Messages.AddRange(logMessages);
}
// Add stability as standard mechanism when not yet available.
if (damProjectData.DamProjectCalculationSpecification.DamCalculationSpecifications.Count == 0)
{
this.damProjectData.DamProjectCalculationSpecification.DamCalculationSpecifications.
Add(new DamFailureMechanismeCalculationSpecification());
}
damProjectData.EnsureProperZoneSafetyFactors();
return this.DamProjectData;
}
///
/// Loads DAM project data from the given .damx file.
///
/// Name of the file.
/// The project data
///
///
public static DamProjectData LoadData(string fileName)
{
if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("fileName");
if (!File.Exists(fileName)) throw new FileNotFoundException(fileName);
if (!fileName.EndsWith("damx")) throw new ArgumentException("No damx file", "fileName");
using (var damProject = new DamProject())
{
return damProject.OpenXMLProject(fileName) as DamProjectData;
}
}
///
/// Saves DAM project data to the given .damx file.
///
/// If the file already exists it will be overriden
/// Name of the file.
/// The DAM project data to be saved.
public static void SaveData(string fileName, DamProjectData data)
{
if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("fileName");
var project = new DamProject() { DamProjectData = data };
project.SaveXMLProject(fileName, null);
}
public object OpenXMLProject(string fileName)
{
ClearProject();
this.ProjectFileName = fileName;
DataEventPublisher.InvokeWithoutPublishingEvents(() =>
{
string damProjectFolder = Path.GetDirectoryName(fileName);
var xmlSerializer = new XmlDeserializer();
object project = xmlSerializer.XmlDeserialize(fileName, typeof(DamProjectData), new DefaultClassFactory());
this.damProjectData = (DamProjectData)project;
// Project still needs a reference to soilmaterials database; to be resolved later
UpdateSoilDatabaseReferences(damProjectFolder);
UpdateSoilPropertiesToSoilDatabase();
});
// Add stability as standard mechanism when not yet available.
if (damProjectData.DamProjectCalculationSpecification.DamCalculationSpecifications.Count == 0)
{
damProjectData.DamProjectCalculationSpecification.DamCalculationSpecifications.Add(new DamFailureMechanismeCalculationSpecification());
}
UpdateForOlderProjects();
return damProjectData;
}
///
/// Updates older projects for missing data or changed data model.
///
private void UpdateForOlderProjects()
{
UpdateMStabParametersSlipCircleDefinition();
damProjectData.EnsureProperZoneSafetyFactors();
UpdateScenariosForLocations();
}
///
/// Updates the scenarios for locations.
/// If they are not available in Design mode, the values should be taken from location
///
private void UpdateScenariosForLocations()
{
if (this.DamProjectData.DamProjectType == DamProjectType.Design)
{
foreach (Location location in damProjectData.Locations)
{
foreach (Scenario scenario in location.Scenarios)
{
if (scenario.UsePlLineOffsetBelowDikeCrestMiddle == null)
{
scenario.UsePlLineOffsetBelowDikeCrestMiddle = location.UsePlLineOffsetBelowDikeCrestMiddle;
}
if (scenario.PlLineOffsetBelowDikeCrestMiddle == null)
{
scenario.PlLineOffsetBelowDikeCrestMiddle = location.PlLineOffsetBelowDikeCrestMiddle;
}
if (scenario.PlLineOffsetBelowDikeToeAtPolder == null)
{
scenario.PlLineOffsetBelowDikeToeAtPolder = location.PlLineOffsetBelowDikeToeAtPolder;
}
if (scenario.PlLineOffsetBelowDikeTopAtPolder == null)
{
scenario.PlLineOffsetBelowDikeTopAtPolder = location.PlLineOffsetBelowDikeTopAtPolder;
}
if (scenario.PlLineOffsetBelowDikeTopAtRiver == null)
{
scenario.PlLineOffsetBelowDikeTopAtRiver = location.PlLineOffsetBelowDikeTopAtRiver;
}
if (scenario.PlLineOffsetBelowShoulderBaseInside == null)
{
scenario.PlLineOffsetBelowShoulderBaseInside = location.PlLineOffsetBelowShoulderBaseInside;
}
if (scenario.UsePlLineOffsetFactorBelowShoulderCrest == null)
{
scenario.UsePlLineOffsetFactorBelowShoulderCrest = location.UsePlLineOffsetFactorBelowShoulderCrest;
}
if (scenario.PlLineOffsetFactorBelowShoulderCrest == null)
{
scenario.PlLineOffsetFactorBelowShoulderCrest = location.PlLineOffsetFactorBelowShoulderCrest;
}
if (scenario.HeadPl3 == null)
{
scenario.HeadPl3 = location.HeadPl3;
}
if (scenario.HeadPl4 == null)
{
scenario.HeadPl4 = location.HeadPl4;
}
}
}
}
}
///
/// Updates the soil properties to the values in the soil database, to avoid discrepancies.
/// If the use of the soil database is not necessary anymore (and the soil parameters can be edited in the UI)
/// then this method is not needed anymore.
/// Also all references of soil in the layers are forced to point to a soil in the SoilList.
///
/// After this method every soil has only 1 instance, and is present in the soillist
///
private void UpdateSoilPropertiesToSoilDatabase()
{
foreach (Dike dike in DamProjectData.WaterBoard.Dikes)
{
UpdateSoilListFromDatabase(dike);
Dictionary aquiferDictionary = dike.SoilList.AquiferDictionary;
foreach (Location location in dike.Locations)
{
location.SoilList = dike.SoilList;
if (location.Segment != null)
{
foreach (SoilGeometryProbability soilGeometryProbability in location.Segment.SoilProfileProbabilities)
{
if (soilGeometryProbability.SoilGeometryType == SoilGeometryType.SoilGeometry1D)
{
foreach (SoilLayer1D layer in soilGeometryProbability.SoilProfile.Layers)
{
bool isAquifer;
// Assign correct soil to layer
Soil newSoil;
var index = dike.SoilList.GetSoilIndexByName(layer.Soil.Name);
if (index < 0)
{
Soil databaseSoil = dike.SoilBaseDB.GetSoil(layer.Soil.Name, out isAquifer);
newSoil = databaseSoil;
dike.SoilList.Soils.Add(newSoil);
}
else
{
newSoil = dike.SoilList.Soils[index];
isAquifer = aquiferDictionary[newSoil];
}
// Set correct IsAquifer property in layer
layer.IsAquifer = isAquifer;
layer.Soil = newSoil;
}
}
}
}
}
}
}
///
/// Update all soil parameters to the data read from database
///
///
private static void UpdateSoilListFromDatabase(Dike dike)
{
using (var geoDatabase = new GeoDatabase(dike.SoilDatabaseName))
{
geoDatabase.ReUseSoils = true;
// read list from old DB and put them in a dictonary by name
var soilList = geoDatabase.ReadSoils(dike.SoilList.Soils);
Dictionary soils = soilList.Soils.ToDictionary(s => s.Name);
foreach (Soil soil in dike.SoilList.Soils)
{
var newSoil = soils[soil.Name];
// if the current soil was found in the db list, update the params for the current soil
if (newSoil != null)
{
soil.Assign(newSoil);
}
// update aquifer dictionary too
if (soilList.AquiferDictionary.ContainsKey(newSoil))
{
if (dike.SoilList.AquiferDictionary.ContainsKey(soil))
{
dike.SoilList.AquiferDictionary[soil] = soilList.AquiferDictionary[newSoil];
}
else
{
dike.SoilList.AquiferDictionary.Add(soil, soilList.AquiferDictionary[newSoil]);
}
}
}
}
}
///
/// Update soil database references:
/// Soildatabase should always be in the project path
/// Make sure the file reference is adjusted to this
///
///
private void UpdateSoilDatabaseReferences(string damProjectFolder)
{
if (!string.IsNullOrWhiteSpace(damProjectFolder))
{
foreach (Dike dike in damProjectData.WaterBoard.Dikes)
{
if (!string.IsNullOrWhiteSpace(dike.SoilDatabaseName))
{
dike.SoilDatabaseName = Path.Combine(damProjectFolder, Path.GetFileName(dike.SoilDatabaseName));
}
foreach (Location location in dike.Locations)
{
location.SoildatabaseName = dike.SoilDatabaseName;
}
}
}
}
///
/// Updates the MStab parameters slip circle definition.
/// For older projects, when slip circle definition was not yet defined we have to provide the default values
///
private void UpdateMStabParametersSlipCircleDefinition()
{
foreach (DamFailureMechanismeCalculationSpecification damCalculationSpecification in this.damProjectData.DamProjectCalculationSpecification.DamCalculationSpecifications)
{
if (damCalculationSpecification.FailureMechanismSystemType == FailureMechanismSystemType.StabilityInside)
{
if (damCalculationSpecification.IsSlipCircleDefinitionUndefined())
{
damCalculationSpecification.ReadUserSettingsSlipCircleDefinition();
}
}
}
}
///
/// Assigns the specified 2D-geometry mapname for all dikes where it is not assigned.
///
/// Name of the map.
public void AssignGeometry2DMapnameIfNotAssigned(string mapName)
{
foreach (var dike in damProjectData.WaterBoard.Dikes)
{
if (String.IsNullOrEmpty(dike.MapForSoilGeometries2D))
{
dike.MapForSoilGeometries2D = mapName;
foreach (Location location in dike.Locations)
{
location.MapForSoilGeometries2D = mapName;
}
}
}
}
///
/// Create a destination filename for new location of soilmaterials database
///
///
///
///
///
public static string CreateNewGeometry2DMapname(string projectFilename, string orgMapName, int dikeIndex)
{
string destMapname = Path.GetFileNameWithoutExtension(projectFilename) + ".geometries2D." + dikeIndex.ToString(CultureInfo.InvariantCulture) + @"\";
return destMapname;
}
///
/// Copies the 2D-geometries.
///
/// The original mapname.
/// The destination mapname.
private void CopyGeometries(string orgMapname, string destMapname)
{
if (Directory.Exists(orgMapname))
{
if (!Directory.Exists(destMapname))
{
Directory.CreateDirectory(destMapname);
}
foreach (var file in Directory.GetFiles(orgMapname))
{
string fileName = Path.GetFileName(file);
if (fileName != null)
{
var destFile = Path.Combine(destMapname, fileName);
if (File.Exists(destFile))
{
File.Delete(destFile);
}
File.Copy(file, destFile);
}
}
}
}
///
/// Make sure the map with 2d_geometries is copied besides the project file
/// and reference to the map is set to it
///
/// the filename of the project
/// The org project map in case of file save as; used to determine original location for 2d-geometries
public void EnsureGeometries2DMapWithProject(string fileName, string orgProjectMap)
{
for (int dikeIndex = 0; dikeIndex < damProjectData.WaterBoard.Dikes.Count; dikeIndex++)
{
string sourceMapName = damProjectData.WaterBoard.Dikes[dikeIndex].MapForSoilGeometries2D;
if (!Directory.Exists(sourceMapName))
{
sourceMapName = Path.Combine(orgProjectMap, sourceMapName);
}
// Only copy files if map of geometries is assigned and exists
if (!String.IsNullOrEmpty(sourceMapName) && Directory.Exists(sourceMapName))
{
string destMapname = CreateNewGeometry2DMapname(fileName, sourceMapName, dikeIndex);
string fullFilename = Path.GetFullPath(fileName);
string destFullMapname = Path.Combine(Path.GetDirectoryName(fullFilename), destMapname);
string orgFullMapname = Path.GetFullPath(sourceMapName);
if (!orgFullMapname.Equals(destFullMapname))
{
CopyGeometries(orgFullMapname, destFullMapname);
}
// Store the relative path to the 2d-geometries
damProjectData.WaterBoard.Dikes[dikeIndex].MapForSoilGeometries2D = destMapname;
foreach (Location location in damProjectData.WaterBoard.Dikes[dikeIndex].Locations)
{
location.MapForSoilGeometries2D = destMapname;
}
}
}
}
///
/// Create a destination filename for new location of soilmaterials database
///
///
///
///
public static string CreateNewSoilMaterialsFilename(string projectFilename, string soilmaterialsFilename, int dikeIndex)
{
string destsoilmaterialsFilename = Path.GetFileNameWithoutExtension(projectFilename) + dikeIndex.ToString(CultureInfo.InvariantCulture) + ".soilmaterials.mdb";
return destsoilmaterialsFilename;
}
///
/// Make sure soilamterials file is copied besides the project file
/// and reference to the file is set to it
///
/// the filename of the project
private void EnsureSoilmaterialsFileWithProject(string fileName)
{
for (int dikeIndex = 0; dikeIndex < damProjectData.WaterBoard.Dikes.Count; dikeIndex++)
{
string path = Path.GetFullPath(damProjectData.WaterBoard.Dikes[dikeIndex].SoilDatabaseName);
// ThrowIfMoreThanOneDikeInProject();
Dike dike = this.DamProjectData.WaterBoard.Dikes[dikeIndex];
string sourceSoilmaterialsFilename = damProjectData.WaterBoard.Dikes[dikeIndex].SoilDatabaseName;
string destSoilmaterialsFilename = CreateNewSoilMaterialsFilename(fileName, sourceSoilmaterialsFilename, dikeIndex);
string fullFilename = Path.GetFullPath(fileName);
string destSoilmaterialsFullFilename = Path.Combine(Path.GetDirectoryName(fullFilename), destSoilmaterialsFilename);
string orgSoilmaterialsFullFilename = Path.GetFullPath(sourceSoilmaterialsFilename);
if (!orgSoilmaterialsFullFilename.Equals(destSoilmaterialsFullFilename))
{
if (File.Exists(destSoilmaterialsFullFilename))
{
File.Delete(destSoilmaterialsFullFilename);
}
if (File.Exists(orgSoilmaterialsFullFilename))
{
File.Copy(orgSoilmaterialsFullFilename, destSoilmaterialsFullFilename);
}
}
damProjectData.WaterBoard.Dikes[dikeIndex].SoilDatabaseName = destSoilmaterialsFullFilename;
foreach (var location in damProjectData.WaterBoard.Dikes[dikeIndex].Locations)
{
location.SoildatabaseName = destSoilmaterialsFullFilename;
}
}
}
///
/// Save the project in XML format
///
///
///
public void SaveXMLProject(string fileName, object project)
{
fileName = Path.ChangeExtension(fileName, "damx");
string orgProjectMap = DamProject.ProjectMap;
ProjectFileName = fileName;
DataEventPublisher.InvokeWithoutPublishingEvents(() =>
{
EnsureSoilmaterialsFileWithProject(fileName);
EnsureGeometries2DMapWithProject(fileName, orgProjectMap);
var xmlSerializer = new XmlSerializer();
xmlSerializer.Serialize(damProjectData, fileName);
});
}
public void ImportWaterLevelTimeSeries()
{
if (DamProjectData.DamProjectCalculationSpecification == null) return;
var file = DamProjectData.WaterBoard.WaterLevelTimeSeriesFileName;
// check if file exists
if (!file.HasValidStringValue()) return;
if (!File.Exists(file)) return;
// load file
var fewsInputTimeSerieCollection = TimeSerieCollection.LoadFromFile(file);
var waterBoardJob = DamProjectData.WaterBoardJob as WaterBoardJob;
WaterBoardJobImporter.ImportWaterlevels(waterBoardJob, fewsInputTimeSerieCollection);
}
public static string ProjectWorkingPath
{
get
{
switch (ProjectWorkingPathLocation)
{
case ProjectPathLocation.InUserMap:
return UserWorkingPath;
case ProjectPathLocation.InProjectMap:
return ProjectMapWorkingPath;
default:
return Path.GetDirectoryName(typeof(DamProject).Assembly.Location) + @"\DamCalculation";
}
}
}
private void DeleteResultData()
{
damProjectData.DeleteResults();
}
public void DeleteResults()
{
DeleteResultData();
RemoveCalculationFolder();
}
public void ClearProject()
{
// DeleteResultData();
damProjectData = null;
damProjectData = new DamProjectData();
damProjectSettings = null;
damProjectSettings = new DamProjectSettings();
LogManager.Clear();
}
private static void RemoveCalculationFolder()
{
if (!Directory.Exists(ProjectWorkingPath)) return;
try
{
Directory.Delete(ProjectWorkingPath, true);
}
catch (IOException e)
{
throw new DamProjectException(e.Message);
}
}
public void Dispose()
{
DamProjectData.Dispose();
}
}
}