// Copyright (C) Stichting Deltares 2017. 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.Data; using System.Data.SQLite; using Core.Common.Base.IO; using Core.Common.IO.Readers; using Core.Common.Utils.Builders; using Ringtoets.Common.IO.Exceptions; using Ringtoets.Common.IO.Properties; using Ringtoets.Common.IO.SoilProfile.Schema; namespace Ringtoets.Common.IO.SoilProfile { /// /// This class reads a D-Soil Model file and reads 2d profiles from this database. /// public class SoilProfile2DReader : SqLiteDatabaseReaderBase, IRowBasedDatabaseReader { private IDataReader dataReader; /// /// Creates a new instance of which will use the /// as its source. /// /// The path of the database file to open. /// Thrown when: /// /// The contains invalid characters; /// No file could be found at . /// /// public SoilProfile2DReader(string databaseFilePath) : base(databaseFilePath) {} /// /// Gets a value indicating whether or not more soil profiles can be read using /// the . /// public bool HasNext { get; private set; } /// /// Initializes the database reader. /// public void Initialize() { PrepareReader(); MoveNext(); } /// /// Reads the information for the next soil profile from the database and creates a /// instance of the information. /// /// The next from the database, or null /// if no more soil profile can be read. /// Thrown when reading properties of the profile failed. /// Thrown when the database returned incorrect /// values for required properties. public SoilProfile2D ReadSoilProfile() { try { return TryReadSoilProfile(); } catch (SystemException exception) when (exception is FormatException || exception is OverflowException || exception is InvalidCastException) { string message = new FileReaderErrorMessageBuilder(Path).Build(Resources.SoilProfileReader_Error_reading_soil_profile_from_database); throw new CriticalFileReadException(message, exception); } } public void MoveNext() { HasNext = MoveNext(dataReader); } public T Read(string columnName) { return (T) dataReader[columnName]; } public T ReadOrDefault(string columnName) { object valueObject = dataReader[columnName]; if (valueObject.Equals(DBNull.Value)) { return default(T); } return (T) valueObject; } protected override void Dispose(bool disposing) { if (dataReader != null) { dataReader.Close(); dataReader.Dispose(); dataReader = null; } base.Dispose(disposing); } /// /// Steps through the result rows until a row is read which' profile id differs from . /// /// The id of the profile to skip. private void MoveToNextProfile(long soilProfileId) { while (HasNext && Read(SoilProfileTableDefinitions.SoilProfileId).Equals(soilProfileId)) { MoveNext(); } } private void PrepareReader() { string soilProfile2DQuery = SoilDatabaseQueryBuilder.GetSoilProfile2DQuery(); try { dataReader = CreateDataReader(soilProfile2DQuery); } catch (SQLiteException exception) { string message = new FileReaderErrorMessageBuilder(Path).Build(Resources.SoilProfileReader_Error_reading_soil_profile_from_database); throw new CriticalFileReadException(message, exception); } } /// /// Tries to read and create a . /// /// The read . /// Thrown when reading properties of the profile failed. private SoilProfile2D TryReadSoilProfile() { var properties = new RequiredProfileProperties(this); var soilLayers = new List(); try { for (var i = 1; i <= properties.LayerCount; i++) { soilLayers.Add(ReadSoilLayerFrom(this, properties.ProfileName)); MoveNext(); } } catch (SoilProfileReadException) { MoveToNextProfile(properties.ProfileId); throw; } try { return new SoilProfile2D(properties.ProfileId, properties.ProfileName, soilLayers) { IntersectionX = properties.IntersectionX }; } catch (ArgumentException exception) { throw new SoilProfileReadException( Resources.SoilProfile1DReader_ReadSoilProfile_Failed_to_construct_profile_from_read_data, properties.ProfileName, exception); } } /// /// Reads a from the given . /// /// Thrown when reading properties of the layers failed. private static SoilLayer2D ReadSoilLayerFrom(IRowBasedDatabaseReader reader, string profileName) { var properties = new Layer2DProperties(reader, profileName); byte[] geometryValue = properties.GeometryValue; SoilLayer2D soilLayer; try { soilLayer = new SoilLayer2DGeometryReader().Read(geometryValue); } catch (SoilLayerConversionException e) { throw CreateSoilProfileReadException(reader.Path, profileName, e); } soilLayer.MaterialName = properties.MaterialName ?? string.Empty; if (properties.IsAquifer.HasValue) { soilLayer.IsAquifer = properties.IsAquifer.Value.Equals(1.0); } if (properties.Color.HasValue) { soilLayer.Color = SoilLayerColorConverter.Convert(properties.Color); } if (properties.BelowPhreaticLevelDistribution.HasValue) { soilLayer.BelowPhreaticLevelDistribution = properties.BelowPhreaticLevelDistribution.Value; } if (properties.BelowPhreaticLevelShift.HasValue) { soilLayer.BelowPhreaticLevelShift = properties.BelowPhreaticLevelShift.Value; } if (properties.BelowPhreaticLevelMean.HasValue) { soilLayer.BelowPhreaticLevelMean = properties.BelowPhreaticLevelMean.Value; } if (properties.BelowPhreaticLevelDeviation.HasValue) { soilLayer.BelowPhreaticLevelDeviation = properties.BelowPhreaticLevelDeviation.Value; } if (properties.DiameterD70Distribution.HasValue) { soilLayer.DiameterD70Distribution = properties.DiameterD70Distribution.Value; } if (properties.DiameterD70Shift.HasValue) { soilLayer.DiameterD70Shift = properties.DiameterD70Shift.Value; } if (properties.DiameterD70Mean.HasValue) { soilLayer.DiameterD70Mean = properties.DiameterD70Mean.Value; } if (properties.DiameterD70CoefficientOfVariation.HasValue) { soilLayer.DiameterD70CoefficientOfVariation = properties.DiameterD70CoefficientOfVariation.Value; } if (properties.PermeabilityDistribution.HasValue) { soilLayer.PermeabilityDistribution = properties.PermeabilityDistribution.Value; } if (properties.PermeabilityShift.HasValue) { soilLayer.PermeabilityShift = properties.PermeabilityShift.Value; } if (properties.PermeabilityMean.HasValue) { soilLayer.PermeabilityMean = properties.PermeabilityMean.Value; } if (properties.PermeabilityCoefficientOfVariation.HasValue) { soilLayer.PermeabilityCoefficientOfVariation = properties.PermeabilityCoefficientOfVariation.Value; } return soilLayer; } private static SoilProfileReadException CreateSoilProfileReadException(string filePath, string profileName, Exception innerException) { string message = new FileReaderErrorMessageBuilder(filePath) .WithSubject(string.Format(Resources.SoilProfileReader_SoilProfileName_0_, profileName)) .Build(innerException.Message); return new SoilProfileReadException(message, profileName, innerException); } private class Layer2DProperties : LayerProperties { /// /// Creates a new instance of which contains properties /// that are required to create a complete . If these properties /// cannot be read, then the reader can proceed to the next profile. /// /// The to read the required layer property values from. /// The profile name used in generating exceptions messages if casting failed. /// Thrown when the values in the database could not be /// casted to the expected column types. internal Layer2DProperties(IRowBasedDatabaseReader reader, string profileName) : base(reader, profileName) { const string readColumn = SoilProfileTableDefinitions.LayerGeometry; try { GeometryValue = reader.Read(readColumn); } catch (InvalidCastException e) { string message = new FileReaderErrorMessageBuilder(reader.Path) .WithSubject(string.Format(Resources.SoilProfileReader_SoilProfileName_0_, profileName)) .Build(string.Format(Resources.SoilProfileReader_Profile_has_invalid_value_on_Column_0_, readColumn)); throw new SoilProfileReadException(message, profileName, e); } } /// /// Gets the geometry for the layer. /// public byte[] GeometryValue { get; } } private class RequiredProfileProperties { /// /// Creates a new instance of which contains properties /// that are required to create a complete . If these properties /// cannot be read, then the reader can proceed to the next profile. /// /// The to read the required profile property values from. /// Thrown when the values in the database could not be /// casted to the expected column types. internal RequiredProfileProperties(IRowBasedDatabaseReader reader) { string readColumn = SoilProfileTableDefinitions.ProfileName; try { ProfileName = reader.Read(SoilProfileTableDefinitions.ProfileName); readColumn = SoilProfileTableDefinitions.IntersectionX; IntersectionX = reader.ReadOrDefault(readColumn) ?? double.NaN; readColumn = SoilProfileTableDefinitions.LayerCount; LayerCount = reader.Read(readColumn); readColumn = SoilProfileTableDefinitions.SoilProfileId; ProfileId = reader.Read(readColumn); } catch (InvalidCastException e) { string message = new FileReaderErrorMessageBuilder(reader.Path) .WithSubject(string.Format(Resources.SoilProfileReader_SoilProfileName_0_, ProfileName)) .Build(string.Format(Resources.SoilProfileReader_Profile_has_invalid_value_on_Column_0_, readColumn)); throw new SoilProfileReadException(message, ProfileName, e); } } /// /// The 1d intersection of the profile. /// public double IntersectionX { get; } /// /// The name of the profile to read. /// public string ProfileName { get; } /// /// The number of layers that the profile has to read. /// public long LayerCount { get; } /// /// Gets the database identifier of the profile. /// public long ProfileId { get; } } } }