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