//----------------------------------------------------------------------- // // 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(); } } }