using System; using System.Collections.Generic; using System.Data; using System.Data.OleDb; using System.Drawing; using System.IO; using System.Linq; using System.Text; using Deltares.DeltaModel; using Deltares.Geotechnics; using Deltares.Geotechnics.ConePenetrationTest; using Deltares.Geotechnics.IO.Importers; using Deltares.Probabilistic; using Deltares.Standard; using Deltares.Standard.EventPublisher; using Deltares.Standard.IO; using FirebirdSql.Data.FirebirdClient; namespace Deltares.Soilbase { public enum GeoDatabaseType { Access, Firebird } /// /// Class for reading data from old style GeoDataBases in either access or firebird format. /// public class GeoDatabase : Importer, IDisposable { private enum SoilBaseSoilType { Gravel = 1, Sand = 2, Loam = 3, Clay = 4, Peat = 5, TertClay = 6, SandyLoam = 7, ClayeySand = 8, } private Dictionary aquiferDictionary = null; private IDbConnection connection = null; private Dictionary originalSoilNamesDictionary = null; private bool reUseSoils; private Dictionary segmentDictionary = null; private string server = "localhost"; private Dictionary soilDictionary = null; public GeoDatabase() { Factory = new DefaultClassFactory(); } public GeoDatabase(string path) { Factory = new DefaultClassFactory(); FileName = path; } public GeoDatabase(string path, string server) { Factory = new DefaultClassFactory(); this.server = server; FileName = path; } public string Path { get { return FileName; } } public string Server { get { return server; } } /// /// Tells whether to reuse soils or not (when true, an existing soil gets the paramters read here, /// else a new (unique) soil is added with the parameters). /// public bool ReUseSoils { get { return reUseSoils; } set { reUseSoils = value; } } /// /// Open connection /// public void Open() { switch (DatabaseType) { case GeoDatabaseType.Firebird: connection = new FbConnection(ConnectionString); break; case GeoDatabaseType.Access: connection = new OleDbConnection(ConnectionString); break; } connection.Open(); } /// /// Close connection /// [Obsolete("GeoDatabase is IDisposable, use a using statement instead.")] public void Close() { if (connection != null) { connection.Close(); connection = null; } } /// /// Read all 1D soilprofiles /// /// /// // TODO according to the test using this method it can throw an exception, but this is not documented here. So is it really thrown? public List ReadSoilProfiles(List soilList) { if (connection == null) { Open(); } if (soilDictionary == null) { ReadSoilDictionary(soilList); } var soilProfiles = new List(); IDataReader reader = GetDataReader("select OD_NR, OD_NAME, OD_RDX, OD_RDY, OD_GEOL_ZONE, OD_CHANCE_OCCUR from ODGEOMETRIES"); while (reader.Read()) { var soilProfile = new SoilProfile1D(); int soilProfileNr = reader.GetInt32(0); soilProfile.Name = reader.GetString(1).Trim(); soilProfile.X = reader.GetDouble(2); soilProfile.Y = reader.GetDouble(3); soilProfile.ProbabilityOfOccurrence = reader.GetDouble(5); ReadSoilProfileLayers(soilProfile, soilProfileNr, soilList); soilProfiles.Add(soilProfile); } reader.Close(); return soilProfiles; } /// /// Read all soilsegments /// /// public List ReadSegments() { var segments = new List(); if (segmentDictionary != null) { segments.AddRange(segmentDictionary.Values); } return segments; } /// /// Read all 2D soilprofiles /// /// /// public List ReadSoilProfiles2D(List soilList) { if (connection == null) { Open(); } var soilProfiles = new List(); IDataReader reader = GetDataReader("select SP_NR, SP_NAME, SP_DESCR, SP_RDX1, SP_RDY1, SP_RDX2, SP_RDY2, SP_GEO from SUBPROJECTS"); while (reader.Read()) { var soilProfile = new SoilProfile2D(); int soilProfileNr = reader.GetInt32(0); soilProfile.Name = reader.GetString(1).Trim(); soilProfile.XBegin = reader.GetDouble(3); soilProfile.YBegin = reader.GetDouble(4); soilProfile.XEnd = reader.GetDouble(5); soilProfile.YEnd = reader.GetDouble(6); var buffer = new byte[reader.GetBytes(7, 0, null, 0, int.MaxValue) - 1]; reader.GetBytes(7, 0, buffer, 0, buffer.Length); var rows = GetStringListFromBuffer(buffer); //Parse of geo file to Soilprofile2D var oldGeoImporter = new OldGeometryFileImporter(); oldGeoImporter.Soils = soilList; oldGeoImporter.OriginalSoilNamesDictionary = originalSoilNamesDictionary; oldGeoImporter.FillFromOldGeoFileAsStrings(rows); var lSoilProfile = oldGeoImporter.SoilProfile2D; if (lSoilProfile != null) { foreach (var soilLayer2D in lSoilProfile.Surfaces) { soilLayer2D.IsAquifer = aquiferDictionary[soilLayer2D.Soil]; } lSoilProfile.Name = soilProfile.Name; lSoilProfile.XBegin = soilProfile.XBegin; lSoilProfile.YBegin = soilProfile.YBegin; lSoilProfile.XEnd = soilProfile.XEnd; lSoilProfile.YEnd = soilProfile.YEnd; soilProfiles.Add(lSoilProfile); } } reader.Close(); return soilProfiles; } /// /// Read all CPTs /// /// public List ReadCPTs() { if (connection == null) { Open(); } var cpts = new List(); IDataReader reader = GetDataReader("select CP_NR, CP_NAME, CP_DESCR, CP_XRD, CP_YRD, CP_FILE from CPTS"); while (reader.Read()) { var cpt = new ConePenetrationTestData(); cpt.Name = !reader.IsDBNull(1) ? reader.GetString(1).Trim() : ""; cpt.Description = !reader.IsDBNull(2) ? reader.GetString(2).Trim() : ""; cpt.X = reader.GetDouble(3); cpt.Y = reader.GetDouble(4); var buffer = new byte[reader.GetBytes(5, 0, null, 0, int.MaxValue) - 1]; reader.GetBytes(5, 0, buffer, 0, buffer.Length); var rows = GetStringListFromBuffer(buffer); var input = ""; foreach (var row in rows) { input = input + row + Environment.NewLine; } var importer = new GefCptFileImporter(); if (importer.FillFromGefString(cpt.Name, input)) { var imCpt = importer.ConePenetrationTestData; // Make sure meta data prevails. imCpt.Name = cpt.Name; imCpt.Description = cpt.Description; imCpt.X = cpt.X; imCpt.Y = cpt.Y; cpts.Add(imCpt); } } reader.Close(); return cpts; } /// /// Read all borings /// /// public List ReadBorings(SoilList soilList) { if (connection == null) { Open(); } var borings = new List(); IDataReader reader = GetDataReader("select BO_NR, BO_NAME, BO_DESCR, BO_XRD, BO_YRD, BO_FILE from BORINGS"); while (reader.Read()) { var boring = new Boring(soilList); boring.Name = !reader.IsDBNull(1) ? reader.GetString(1).Trim() : ""; boring.Description = !reader.IsDBNull(2) ? reader.GetString(2).Trim() : ""; boring.X = reader.GetDouble(3); boring.Y = reader.GetDouble(4); var buffer = new byte[reader.GetBytes(5, 0, null, 0, int.MaxValue) - 1]; reader.GetBytes(5, 0, buffer, 0, buffer.Length); var rows = GetStringListFromBuffer(buffer); var input = ""; foreach (var row in rows) { input = input + row + Environment.NewLine; } var importer = new GefBoringFileImporter(); importer.OriginalSoilNamesDictionary = originalSoilNamesDictionary; if (importer.FillFromGefString(boring.Name, input, soilList)) { var imBoring = importer.Boring; // Make sure meta data prevails. imBoring.Name = boring.Name; imBoring.Description = boring.Description; imBoring.X = boring.X; imBoring.Y = boring.Y; borings.Add(imBoring); } } reader.Close(); return borings; } /// /// Transfer soils from soil dictionary to list /// /// public SoilList ReadSoils(List soilList) { if (soilDictionary == null) { ReadSoilDictionary(soilList); } var soils = new List(); if (soilDictionary != null) { soils = soilDictionary.Values.ToList(); } var newSoilList = new SoilList(); newSoilList.Soils.AddRange(soils); newSoilList.AquiferDictionary = aquiferDictionary; return newSoilList; } /// /// Read parameter values for a soil /// /// /// /// public void ReadSoilParameters(Soil soil, int materialNr, out bool isAquifer) { IDataReader reader = GetDataReader( "select Parametervalues.PV_NR, " + " Parametervalues.PN_NR, " + " Parametervalues.PV_VALUE, " + " ParameterNames.PN_NAME " + "from ParameterNames, Parametervalues " + "where ParameterNames.PN_NR = Parametervalues.PN_NR and " + " Parametervalues.MA_NR = " + materialNr); isAquifer = false; while (reader.Read()) { int parameterNameNr = reader.GetInt32(1); string parameterName = reader.GetString(3).Trim(); double parameterValue = Convert.ToDouble(reader.GetValue(2)); if (parameterName == SoilParameterNames.IsAquifer) { isAquifer = DoubleToBool(parameterValue); } else { SetSoilParameter(soil, parameterName, parameterValue); } } reader.Close(); } /// /// Import the data using the Factory. So only use when you use a Factory. /// public override void Import() { DataEventPublisher.InvokeWithoutPublishingEvents(() => { var soilList = new List(); ReadSoils(soilList); }); } public void Dispose() { Close(); } private GeoDatabaseType DatabaseType { get { string extension = System.IO.Path.GetExtension(FileName).ToLower().Trim('.'); switch (extension) { case "fdb": case "gdb": return GeoDatabaseType.Firebird; case "mdb": return GeoDatabaseType.Access; default: throw new Exception("Unknown database type: " + FileName); } } } /// /// Provides the proper connection string for the database depending on its type (access or firebird) /// private string ConnectionString { get { switch (DatabaseType) { case GeoDatabaseType.Firebird: // Set the ServerType to 0 to connect to the client-server client // Set the ServerType to 1 to connect to the embedded server return "User=SYSDBA;" + "Password=masterkey;" + "Database=" + FileName + ";" + "DataSource=" + server + ";" + "Port=3050;" + "Dialect=3;" + "Charset=NONE;" + "Role=;" + "Connection lifetime=15;" + "Pooling=true;" + "MinPoolSize=0;" + "MaxPoolSize=50;" + "Packet Size=8192;" + "ServerType=1"; case GeoDatabaseType.Access: return "Provider=Microsoft.JET.OLEDB.4.0;" + "data source=" + FileName; default: return null; } } } /// /// Generic request for datareader /// /// /// private IDataReader GetDataReader(string query) { if (DatabaseType == GeoDatabaseType.Firebird) { var command = new FbCommand(query, (FbConnection) connection); FbDataReader reader = command.ExecuteReader(); return reader; } else // Access { var command = new OleDbCommand(query, (OleDbConnection) connection); OleDbDataReader reader = command.ExecuteReader(); return reader; } } /// /// Returns the content of the buffer as a list of strings /// /// /// private List GetStringListFromBuffer(byte[] buffer) { var stream = new MemoryStream(buffer); // Make sure to rewind the stream! stream.Position = 0; var rows = new List(); // This works for ASCII files using (var streader = new StreamReader(stream, Encoding.ASCII)) { string line; while ((line = streader.ReadLine()) != null) { rows.Add(line); } } return rows; } /// /// Read layers into soilprofile /// /// /// /// private void ReadSoilProfileLayers(SoilProfile1D soilProfile, int soilProfileNr, List soilList) { IDataReader reader = GetDataReader("select MA_NR, OL_LAYERNR, OL_LEVEL from ODGEOLAYERS where OD_NR = " + soilProfileNr.ToString() + " ORDER BY OL_LAYERNR"); while (reader.Read()) { var layer = new SoilLayer1D(); int index = reader.GetInt32(1); layer.Name = index.ToString(); layer.Id = "L" + soilProfile.Layers.Count.ToString(); int materialNr = reader.GetInt32(0); var lSoil = soilDictionary[materialNr]; var soil = soilList.FirstOrDefault(s => s.Name.Equals(lSoil.Name, StringComparison.InvariantCultureIgnoreCase)); if (soil == null) { // If soil was not yet part of the list, create and add it. soil = new Soil { Name = lSoil.Name }; soil.GenerateRandomColor(); soilList.Add(soil); } layer.Soil = soil; layer.TopLevel = Convert.ToDouble(reader.GetValue(2)); layer.IsAquifer = aquiferDictionary[soil]; soilProfile.Layers.Add(layer); } reader.Close(); } /// /// Read soil dictionary (used for e.g. lookup of soils in soilprofiles) /// private void ReadSoilDictionary(List soilList) { if (connection == null) { Open(); } soilDictionary = new Dictionary(); aquiferDictionary = new Dictionary(); originalSoilNamesDictionary = new Dictionary(); IDataReader reader = GetDataReader("select MA_NR, MA_NAME, MA_DESCR, MA_INDEX from MATERIALS"); while (reader.Read()) { var soil = (Soil) Factory.GetObject(reader.GetString(1).Trim(), typeof(Soil), null); soil.Name = reader.GetString(1).Trim(); if (reUseSoils) { soil.Description = !reader.IsDBNull(2) ? reader.GetString(2).Trim() : ""; int materialNr = reader.GetInt32(0); bool isAquifer = false; ReadSoilParameters(soil, materialNr, out isAquifer); soilDictionary.Add(materialNr, soil); aquiferDictionary.Add(soil, isAquifer); } else { // Make sure that the soil read here is or becomes unique to the existing list of soils // This makes sure that the dictionary contains the new unique names so when reading 1D soilprofiles // the soils there automatically refer to the correct soils. // This however does generate a problem for the 2D geometries and Borings as they do (2d) or might (boring) refer // to the original names. So these are to be kept too! var originalName = soil.Name; UniqueNameProvider.ProvideUniqueName(soilList, soil); bool isRenamed = soil.Name != originalName; soil.Description = !reader.IsDBNull(2) ? reader.GetString(2).Trim() : ""; int materialNr = reader.GetInt32(0); bool isAquifer = false; ReadSoilParameters(soil, materialNr, out isAquifer); if (!(isRenamed && originalName == "Undetermined")) { soilDictionary.Add(materialNr, soil); aquiferDictionary.Add(soil, isAquifer); } else { var soilUndetermined = soilList.Find(x => x.Name == "Undetermined"); soilDictionary.Add(materialNr, soilUndetermined); aquiferDictionary.Add(soil, isAquifer); } if (isRenamed && originalName != "Undetermined") { originalSoilNamesDictionary.Add(originalName, soil); } } } reader.Close(); if (Factory is DeltaModelFactory) { var df = (DeltaModelFactory) Factory; df.SetAquiferDictionary(aquiferDictionary); } } /// /// Set soil property to value /// /// /// /// /// private void SetSoilParameter(Soil soil, string parameterName, double parameterValue) { //if (parameterName.Equals(SoilParameterNames.Color)) //{ //} switch (parameterName) { case SoilParameterNames.Color: Color colorBGR = Color.FromArgb((int) parameterValue); soil.Color = Color.FromArgb(255, colorBGR.B, colorBGR.G, colorBGR.R); break; case SoilParameterNames.SoilType: var soilBaseSoilType = (SoilBaseSoilType) Math.Round(parameterValue); switch (soilBaseSoilType) { case SoilBaseSoilType.Gravel: soil.SoilType = SoilType.Gravel; break; case SoilBaseSoilType.Sand: soil.SoilType = SoilType.Sand; break; case SoilBaseSoilType.Loam: soil.SoilType = SoilType.Loam; break; case SoilBaseSoilType.Clay: soil.SoilType = SoilType.Clay; break; case SoilBaseSoilType.Peat: soil.SoilType = SoilType.Peat; break; case SoilBaseSoilType.TertClay: soil.SoilType = SoilType.Clay; break; case SoilBaseSoilType.SandyLoam: soil.SoilType = SoilType.Loam; break; case SoilBaseSoilType.ClayeySand: soil.SoilType = SoilType.Sand; break; default: throw new SoilTypeNotFoundException(); } break; case SoilParameterNames.DiameterD70: soil.DiameterD70 = parameterValue*1e-6; break; case SoilParameterNames.WhitesConstant: soil.WhitesConstant = parameterValue; break; case SoilParameterNames.BeddingAngle: soil.BeddingAngle = parameterValue; break; case SoilParameterNames.PermeabKx: soil.PermeabKx = parameterValue; break; case SoilParameterNames.StdDiameterD70: soil.DiameterD70Stochast.Deviation = parameterValue*1e-6; break; case SoilParameterNames.DistDiameterD70: soil.DiameterD70Stochast.DistributionType = DoubleToDistribution(parameterValue); break; case SoilParameterNames.StdPermeabKx: soil.PermeabKxStochast.Deviation = parameterValue; break; case SoilParameterNames.DistPermeabKx: soil.PermeabKxStochast.DistributionType = DoubleToDistribution(parameterValue); break; case SoilParameterNames.StdWhitesConstant: soil.WhitesConstantStochast.Deviation = parameterValue; break; case SoilParameterNames.DistWhitesConstant: soil.WhitesConstantStochast.DistributionType = DoubleToDistribution(parameterValue); break; case SoilParameterNames.StdBeddingAngle: soil.BeddingAngleStochast.Deviation = parameterValue; break; case SoilParameterNames.DistBeddingAngle: soil.BeddingAngleStochast.DistributionType = DoubleToDistribution(parameterValue); break; case SoilParameterNames.AbovePhreaticLevel: soil.AbovePhreaticLevel = parameterValue; break; case SoilParameterNames.BelowPhreaticLevel: soil.BelowPhreaticLevel = parameterValue; break; case SoilParameterNames.OvenDry: soil.DryUnitWeight = parameterValue; break; case SoilParameterNames.ExcessPorePressure: soil.ExcessPorePressure = parameterValue; break; case SoilParameterNames.PorePressureFactor: soil.PorePressureFactor = parameterValue; break; case SoilParameterNames.Cohesion: soil.Cohesion = parameterValue; break; case SoilParameterNames.Phi: soil.FrictionAngle = parameterValue; break; case SoilParameterNames.RatioCuPc: soil.RatioCuPc = parameterValue; break; case SoilParameterNames.RatioCuPcActive: soil.RatioCuPcActive = parameterValue; break; case SoilParameterNames.RatioCuPcPassive: soil.RatioCuPcPassive = parameterValue; break; case SoilParameterNames.StrengthIncreaseExponent: soil.StrengthIncreaseExponent = parameterValue; break; case SoilParameterNames.ShearStrengthModel: // in the MSoilbase the identifier for ShearStrengthModel was MatStrengthType, so also check on this identifier case SoilParameterNames.MatStrengthType: var shearStrengthModel = DoubleToMatStrengthType(parameterValue); // When shearStrengthModel == ShearStrengthModel.None in the original DGeoStability it meant that the default value should be used // In the current soil class we have a boolean that indicates that the default (projectwide) value should be used) soil.UseDefaultShearStrengthModel = (shearStrengthModel == ShearStrengthModel.None); soil.ShearStrengthModel = shearStrengthModel; break; case SoilParameterNames.UseProbDefaults: soil.UseDefaultProbabilisticValues = DoubleToBool(parameterValue); break; case SoilParameterNames.StdCohesion: soil.CohesionStochast.Deviation = parameterValue; break; case SoilParameterNames.StdPhi: soil.FrictionAngleStochast.Deviation = parameterValue; break; case SoilParameterNames.StdPn: soil.PnStochast.Deviation = parameterValue; break; case SoilParameterNames.StdPOP: soil.POPStochast.Deviation = parameterValue; break; case SoilParameterNames.DistCohesion: soil.CohesionStochast.DistributionType = DoubleToDistribution(parameterValue); break; case SoilParameterNames.DistPhi: soil.FrictionAngleStochast.DistributionType = DoubleToDistribution(parameterValue); break; case SoilParameterNames.DistStressTable: soil.StressTableStochast.DistributionType = DoubleToDistribution(parameterValue); break; case SoilParameterNames.DistPn: soil.PnStochast.DistributionType = DoubleToDistribution(parameterValue); break; case SoilParameterNames.DistPOP: soil.POPStochast.DistributionType = DoubleToDistribution(parameterValue); break; case SoilParameterNames.CorrelationCPhi: soil.CorrelationCPhi = parameterValue; break; case SoilParameterNames.POP: soil.POP = parameterValue; break; case SoilParameterNames.DesignPartialCohesion: soil.CohesionStochast.DesignFactor = parameterValue; break; case SoilParameterNames.DesignStdCohesion: soil.CohesionStochast.DesignDeviation = parameterValue; break; case SoilParameterNames.DesignPartialPhi: soil.FrictionAngleStochast.DesignFactor = parameterValue; break; case SoilParameterNames.DesignStdPhi: soil.FrictionAngleStochast.DesignDeviation = parameterValue; break; case SoilParameterNames.DesignPartialStressTable: soil.StressTableStochast.DesignFactor = parameterValue; break; case SoilParameterNames.DesignStdStressTable: soil.StressTableStochast.DesignDeviation = parameterValue; break; case SoilParameterNames.DesignPartialPOP: soil.POPStochast.DesignFactor = parameterValue; break; case SoilParameterNames.DesignStdPOP: soil.POPStochast.DesignDeviation = parameterValue; break; case SoilParameterNames.DesignPartialRRatio: soil.RRatioStochast.DesignFactor = parameterValue; break; case SoilParameterNames.DesignStdRRatio: soil.RRatioStochast.DesignDeviation = parameterValue; break; case SoilParameterNames.SoilGroup: soil.SoilGroup = DoubleToInt(parameterValue); break; case SoilParameterNames.UseSoilType: soil.UseSoilType = DoubleToBool(parameterValue); break; case SoilParameterNames.RRatio: soil.RRatio = parameterValue; break; // Use following (debug) code to see which parameters are not handled yet //default: // Debug.WriteLine(@"WARNING: Unknown Soil Parameter: " + parameterName); // break; } } /// /// Convert a double value to Distribution type /// /// /// private DistributionType DoubleToDistribution(double value) { return (DistributionType) Math.Round(value) - 1; } /// /// Convert a double value to ShearStrengthModel type /// /// /// private ShearStrengthModel DoubleToMatStrengthType(double value) { return (ShearStrengthModel) Math.Round(value); } /// /// Convert double to int /// /// /// private int DoubleToInt(double value) { return (int) Math.Round(value); } /// /// Convert double to int /// /// /// private bool DoubleToBool(double value) { return ((int) Math.Round(value) != 0); } } }