using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using Deltares.Geographic; using Deltares.Geometry; using Deltares.Geotechnics; using Deltares.Geotechnics.ConePenetrationTest; using Deltares.Geotechnics.IO; using Deltares.Geotechnics.IO.Importers; using Deltares.Geotechnics.Mechanisms; using Deltares.Geotechnics.Xsd; using Deltares.MStab.IO.Classic; using Deltares.Soilbase; using Deltares.Stability; using Deltares.Standard; using Deltares.Standard.EventPublisher; using Deltares.Standard.Forms; using Deltares.Standard.Language; using Deltares.Standard.Logging; using Deltares.Standard.Maps; using Deltares.Standard.Project; using Deltares.Standard.Validation; namespace Deltares.DSoilModel.Data { /// /// D-Soil Model Project /// public class DSoilModelProject : Project, IDisposable, IDomain { private readonly List bondStressCurves = new List(); private readonly List boringLookup1Ds = new List(); private readonly List boringLookup2Ds = new List(); private readonly List borings = new List(); private readonly List cptLookup1Ds = new List(); private readonly List cptLookup2Ds = new List(); private readonly List cpts = new List(); private readonly List currentSoilSegments = new List(); private readonly List soilProfiles1D = new List(); private readonly List soilProfiles2D = new List(); private readonly List soilSegments = new List(); private readonly List soilprofile1DLookup2Ds = new List(); private readonly List specificMechanismPointLocations = new List(); private readonly List stressCurves = new List(); private readonly List surfaceLines = new List(); private List dataSources = new List(); private SoilbaseDB soilbaseDB = new SoilbaseDB(); private SoilList soils = new SoilList(); private ParameterViewSettings parameterView = ParameterViewSettings.AllParameters; private Mechanism currentFailureMechanism; private const double noPoint = -9999123456789.0; private const double geoTolerance = 0.001; public const bool IsAutoValidationEnabled = true; public DSoilModelProject() { // allow the project to provide lists of soils, stresscurves Soil.DomainProvider = this; SoilLayer.DomainProvider = this; SoilSegment.DomainProvider = this; TransformerManager.Transformer = new DSoilModelTransformer(); DataEventPublisher.OnAfterChange += DataEventPublisher_OnAfterChange; DataEventPublisher.OnDataListModified += DataEventPublisher_OnDataListModified; } /// /// Currently selected (in the UI) parameter filter /// public ParameterViewSettings ParameterView { get { return parameterView; } set { DataEventPublisher.BeforeChange(this, x => x.ParameterView); parameterView = value; DataEventPublisher.AfterChange(this, x => x.ParameterView); } } /// /// Currently selected (in the UI) failure mechanism /// public Mechanism CurrentFailureMechanism { get { return currentFailureMechanism; } set { DataEventPublisher.BeforeChange(this, "CurrentFailureMechanism"); currentFailureMechanism = value; // Also set proper mechanism(s) for soil to get the proper filtering based on mechanism. // Note: do NOT alter the MechanismeSupport.Mechanisms list, that may only be done in the plugin // The MechanismeSupport.Mechanisms list controls the content of all available mechanisms for the product. if (value != Mechanism.None) { Soil.Mechanisms = new[] { value }; } else { Soil.Mechanisms = MechanismSupport.Mechanisms; } FilterSegments(); DataEventPublisher.AfterChange(this, "CurrentFailureMechanism"); DataEventPublisher.DataListModified(Soils.Soils); DataEventPublisher.DataListModified(CurrentSoilSegments); DataEventPublisher.DoRefreshView(null, null); } } /// /// SoilSegments for the currently selected Failure mechanism /// public List CurrentSoilSegments { get { return currentSoilSegments; } } /// /// Gets the CPTs (Cone Penetration Tests). /// /// /// The CPTs. /// [Validate] public List CPTs { get { return cpts; } } /// /// Gets the borings. /// /// /// The borings. /// [Validate] public List Borings { get { return borings; } } /// /// Gets or sets the soilbase database. /// /// /// The soilbase database. /// public SoilbaseDB SoilbaseDb { get { return soilbaseDB; } set { soilbaseDB = value; } } /// /// Gets or sets the soils. /// /// /// The soils. /// [Validate] public SoilList Soils { get { return soils; } set { soils = value; } } /// /// Gets the 1D soil profiles. /// /// /// The 1D soil profiles. /// [Validate] public List SoilProfiles1D { get { return soilProfiles1D; } } /// /// Gets the 2D soil profiles. /// /// /// The 2D soil profiles. /// [Validate] public List SoilProfiles2D { get { return soilProfiles2D; } } /// /// Gets the 1D CPT lookups. /// /// /// The 1D CPT lookups. /// [Validate] public List CptLookup1Ds { get { return cptLookup1Ds; } } /// /// Gets the 2D CPT lookups. /// /// /// The 2D CPT lookups. /// [Validate] public List CptLookup2Ds { get { return cptLookup2Ds; } } /// /// Gets the 1D boring lookups. /// /// /// The 1D boring lookups. /// [Validate] public List BoringLookup1Ds { get { return boringLookup1Ds; } } /// /// Gets the 2D boring lookups. /// /// /// The 2D boring lookups. /// [Validate] public List BoringLookup2Ds { get { return boringLookup2Ds; } } /// /// Gets the specific mechanism point locations. /// These locations define a local position where a 1D soilprofile is to be extracted from a 2D profile for calculations related to a specific failure mechanism. /// [Validate] public List SpecificMechanismPointLocations { get { return specificMechanismPointLocations; } } /// /// Gets the 1D to 2D lookup list. /// These loookups define a relative position for a 1D soil profile in a 2D soil profile, /// [Validate] public List Soilprofile1DLookup2Ds { get { return soilprofile1DLookup2Ds; } } /// /// Gets the soil segments. /// /// /// The soil segments. /// [Validate] public List SoilSegments { get { return soilSegments; } } /// /// Gets the surface lines. /// /// /// The surface lines. /// [Validate] public List SurfaceLines { get { return surfaceLines; } } /// /// Gets the stress curves. /// /// /// The stress curves. /// [Validate] public List StressCurves { get { return stressCurves; } } /// /// Gets the bond stress curves. /// /// /// The bond stress curves. /// [Validate] public List BondStressCurves { get { return bondStressCurves; } } /// /// Gets the list of data sources /// [Browsable(false)] public List DataSources { get { return dataSources; } set { dataSources = value; } } /// /// Reads a collection of SoilProfile1D from the given file (csv) and adds it to the project ensuring a unique name /// /// public void ReadSoilProfiles1DFromFile(string fileName) { var soilsCount = Soils.Soils.Count; var importer = new SosSoilProfilesImporter(); List profiles = importer.ReadSoilProfiles(fileName, Soils); foreach (var profile in profiles) { AddSoilProfile1DToProject(profile); } if (importer.ErrorMessages.Count > 0) { // log the messages DataEventPublisher.InvokeAndPublish(() => { foreach (var msg in importer.ErrorMessages) { LogManager.Add(new LogMessage(LogMessageType.Warning, this, fileName + " : " + msg)); } }); } } /// /// Reads a SoilProfile2d (geometry) from the given file (sti or geo) and adds it to the project ensuring a unique name /// /// file name (*.sti, *.geo) /// Option to ignore soils with duplicate names, drequired for DAM defx import public SoilProfile2D ReadGeometryFromFile(string fileName, bool skipSoilsWithSameName = false) { if (fileName.ToLower().EndsWith(".sti")) { SoilProfile2D sp2 = null; StabilityModel stabilityModel = null; try { var oldTransformer = TransformerManager.Transformer; try { TransformerManager.Transformer = new DSoilModelStiFileTransformer(); var converter = new Converter(); stabilityModel = converter.ConvertClassicMStab(fileName); } finally { TransformerManager.Transformer = oldTransformer; } } catch (Exception ex) { LogManager.Add(new LogMessage(LogMessageType.Error, typeof(Converter), ex.Message)); } if (stabilityModel != null) { foreach (var soil in stabilityModel.SoilModel.Soils) { soil.UseDefaultShearStrengthModel = false; } AddSoilDataToProject(new SoilList { Soils = stabilityModel.SoilModel.Soils.ToList() }, skipSoilsWithSameName); sp2 = AddSoilProfile2DToProject(fileName, stabilityModel); } return sp2; } if (fileName.ToLower().EndsWith(".geo")) { var oldGeoImporter = new SoilProfile2DFromDSerieGeoFileImporter { Soils = soils.Soils }; oldGeoImporter.ConvertToSoilProfile2D(fileName); var sp2 = oldGeoImporter.SoilProfile2D; if (sp2 != null) { // AddSoilDataToProject(oldGeoImporter.Soils); soils are handled in importer itself! AddSoilProfile2DToProject(fileName, sp2); } return sp2; } Soil.DomainProvider = this; SoilLayer.DomainProvider = this; SoilSegment.DomainProvider = this; TransformerManager.Transformer = new DSoilModelTransformer(); return null; } /// /// Reads a cpt from the given GEF file and adds it to the project ensuring a unique name /// /// public bool ReadGefCptFromFile(string fileName) { ConePenetrationTestData conePenetrationTestData = null; string lcFileName = fileName.ToLower(); if (lcFileName.EndsWith(".gef")) { var gefCptImporter = new GefCptFileImporter(); gefCptImporter.FillFromGefFile(fileName); var messages = gefCptImporter.ErrorMessages; if (messages.Count == 0) { conePenetrationTestData = gefCptImporter.ConePenetrationTestData; } } if (conePenetrationTestData != null) { if (conePenetrationTestData.LevelType != CPTLevelType.NAP || conePenetrationTestData.XYCoordinateSystem != CPTXYCoordinateSystem.RD) { var msg = String.Format(LocalizationManager.GetTranslatedText(this, "gefCoordinateSystemError"), fileName); LogManager.Add(new LogMessage(LogMessageType.Error, null, msg)); return false; } if (!IsValidRdCoordinate(conePenetrationTestData.X, conePenetrationTestData.Y)) { var msg = String.Format(LocalizationManager.GetTranslatedText(this, "gefInvalidRdCoordinates"), fileName, conePenetrationTestData.X, conePenetrationTestData.Y); LogManager.Add(new LogMessage(LogMessageType.Warning, null, msg)); conePenetrationTestData.X = 0; conePenetrationTestData.Y = 0; } UniqueNameProvider.ProvideUniqueName(cpts, conePenetrationTestData); cpts.Add(conePenetrationTestData); return true; } return false; } /// /// Reads a boring from the given GEF file and adds it to the project ensuring a unique name /// /// public bool ReadGefBoringFromFile(string fileName) { Boring boring = null; if (fileName.ToLower().EndsWith(".gef")) { var gefBoringImporter = new GefBoringFileImporter(); gefBoringImporter.FillFromGefFile(fileName, soils); boring = gefBoringImporter.Boring; } if (boring != null) { if (boring.ZcoordinateSystem != "NAP" || boring.XyCoordinateSystem != "RD") { var msg = String.Format(LocalizationManager.GetTranslatedText(this, "gefCoordinateSystemError"), fileName); LogManager.Add(new LogMessage(LogMessageType.Error, null, msg)); return false; } if (!IsValidRdCoordinate(boring.X, boring.Y)) { var msg = String.Format(LocalizationManager.GetTranslatedText(this, "gefInvalidRdCoordinates"), fileName, boring.X, boring.Y); LogManager.Add(new LogMessage(LogMessageType.Warning, null, msg)); boring.X = 0; boring.Y = 0; } // Ensure unique name UniqueNameProvider.ProvideUniqueName(borings, boring); borings.Add(boring); return true; } return false; } /// /// Reads soils from the given old style database and adds them to the project ensuring unique names /// /// public void ReadSoilsFromDatabase(string fileName) { DSoilModelIO.AddOldSoilDBToProject(fileName, this); DataEventPublisher.DataListModified(soils.Soils); } /// /// Reads data from the given old style database (*.MDB, *.GDB) and adds it to the project ensuring unique names /// /// public void ReadOldProjectFromDatabase(string fileName) { DSoilModelIO.AddOldProjectDataBaseToProject(fileName, this); } /// /// Gets the profiles. /// /// The profile. /// List of Soil Profiles public IEnumerable GetProfiles(StochasticSoilProfile profile) { var list = new List(); list.AddRange(soilProfiles1D); list.AddRange(soilProfiles2D); return list; } /// /// Gets the CPTS. /// /// The profile. /// public IEnumerable GetCpts(ConePenetrationTestPerSegment conePenetrationTest) { var list = new List(); list.AddRange(cpts); return list; } /// /// Gets the CPTS. /// /// The cpt. /// public IEnumerable GetCpts(SoilSegment cpt) { var list = new List(); list.AddRange(cpts); return list; } /// /// Gets the borings. /// /// The profile. /// public IEnumerable GetBorings(SoilSegment boring) { var list = new List(); list.AddRange(borings); return list; } /// /// Gets the borings. /// /// The profile. /// public IEnumerable GetBorings(BoringPerSegment boring) { var list = new List(); list.AddRange(borings); return list; } /// /// Gets the surfaceLines. /// /// The surfaceLine. /// public IEnumerable GetSurfaceLines(SoilSegment surfaceLine) { var list = new List(); list.AddRange(surfaceLines); return list; } /// /// Validation for the specific mechanism point locations on soil profile 2D. /// [Validate] public ValidationResult[] ValidateSpecificMechanismPointLocations() { var results = new List(); foreach (var soilProfile2D in soilProfiles2D) { SoilProfile2D thisSoilProfile2D = soilProfile2D; List specificMechanismPointLocationsForThisSoilProfile = specificMechanismPointLocations.Where(l => l.SoilProfile2D == thisSoilProfile2D).ToList(); foreach (var mechanism in Enum.GetValues(typeof(Mechanism)).Cast()) { Mechanism thisMechanism = mechanism; List locations = specificMechanismPointLocationsForThisSoilProfile.Where(l => l.Mechanism == thisMechanism).ToList(); if (thisMechanism != Mechanism.None) { if (locations.Count > 1) { string messageForProfile = String.Format(this.Translate("MultipleSpecificMechanismPointLocationsOnProfile"), locations.First().ToString().ToLower(), locations.Count); results.Add(new ValidationResult(ValidationResultType.Error, messageForProfile, thisSoilProfile2D)); foreach (var location in locations) { string messageforLocation = String.Format(this.Translate("MultipleSpecificMechanismPointLocations"), locations.First().ToString().ToLower(), thisSoilProfile2D.Name); results.Add(new ValidationResult(ValidationResultType.Error, messageforLocation, location)); } } } } } return results.ToArray(); } /// /// Add soil data to the project, resolving duplicate names /// /// soil data to add /// determines how to handle duplicate names public void AddSoilDataToProject(SoilList newSoilData, bool skipSoilsWithSameName = false) { if (newSoilData != null) { foreach (var newSoil in newSoilData.Soils) { var isNewSoil = true; CheckStrengthModelSoil(newSoil); foreach (var soilAlreadyKnown in soils.Soils) { if (soilAlreadyKnown.Name.Equals(newSoil.Name, StringComparison.InvariantCultureIgnoreCase)) { if (skipSoilsWithSameName) // required for DAM defx import where soil properties from MDB files are leading { isNewSoil = false; } else { // Make sure the names are equal! Otherwise the comparison of the properties may fail newSoil.Name = soilAlreadyKnown.Name; if (soilAlreadyKnown.HasTheSameDeterministicValues(newSoil)) { isNewSoil = false; // check isAquifer dictionary ? if (newSoilData.AquiferDictionary != null && newSoilData.AquiferDictionary.ContainsKey(newSoil)) { if (Soils.AquiferDictionary != null && Soils.AquiferDictionary.ContainsKey(soilAlreadyKnown)) { isNewSoil = (Soils.AquiferDictionary[soilAlreadyKnown] != newSoilData.AquiferDictionary[newSoil]); } } } } break; } } if (isNewSoil) { // Is a different soil with different properties that should be added under a new name UniqueNameProvider.ProvideUniqueName(soils.Soils, newSoil); soils.Add(newSoil); if (newSoilData.AquiferDictionary != null && newSoilData.AquiferDictionary.ContainsKey(newSoil)) { soils.AquiferDictionary.Add(newSoil, newSoilData.AquiferDictionary[newSoil]); } } } } } /// /// Repair all validation results for which a repair option is available /// public void RepairAllValidationResults() { RealTimeBackgroundValidator.Instance.Stop(); foreach (var result in RealTimeBackgroundValidator.ValidationResults) { if (result.Repairer != null) { result.Repair(); } } if (IsAutoValidationEnabled) { RealTimeBackgroundValidator.Instance.Start(); } } /// /// Make sure that the separate lists of Soils, SoilProfiles etc. do represent all referenced objects in the datamodel. /// public void UpdateLists() { foreach (var segment in soilSegments) { if (segment.StochasticSoilModel != null) { foreach (var profile in segment.StochasticSoilModel.StochasticSoilProfiles) { if (profile.Profile is SoilProfile1D) { if (!soilProfiles1D.Contains(profile.Profile)) { soilProfiles1D.Add((SoilProfile1D) profile.Profile); } } else if (profile.Profile is SoilProfile2D) { if (!soilProfiles2D.Contains(profile.Profile)) { soilProfiles2D.Add((SoilProfile2D) profile.Profile); } } } } if (segment.Cpts != null) { foreach (var cpt in segment.Cpts) { if (!cpts.Contains(cpt.ConePenetrationTestData)) { cpts.Add(cpt.ConePenetrationTestData); } } } if (segment.Borings != null) { foreach (var boring in segment.Borings) { if (!borings.Contains(boring.Boring)) { borings.Add(boring.Boring); } } } } foreach (var profile in soilProfiles1D) { foreach (var layer in profile.Layers) { if (layer.Soil != null && !soils.Soils.Contains(layer.Soil)) { soils.Add(layer.Soil); } } } foreach (var profile in soilProfiles2D) { foreach (var surface in profile.Surfaces) { if (surface.Soil != null && !soils.Soils.Contains(surface.Soil)) { soils.Add(surface.Soil); } } } foreach (var soil in soils.Soils) { if (soil != null) { if (soil.StressTable != null && soil.StressTable.SigmaTaus.Count > 0) { if (!stressCurves.Contains(soil.StressTable)) { stressCurves.Add(soil.StressTable); } } if (soil.BondStressTable != null && soil.BondStressTable.BondStresses.Count > 0) { if (!bondStressCurves.Contains(soil.BondStressTable)) { bondStressCurves.Add(soil.BondStressTable); } } } } } /// /// Dispose of unmanaged resources and delegates /// public void Dispose() { foreach (var surfaceLine in SurfaceLines) { surfaceLine.Dispose(); } surfaceLines.Clear(); DataEventPublisher.OnDataListModified -= DataEventPublisher_OnDataListModified; DataEventPublisher.OnAfterChange -= DataEventPublisher_OnAfterChange; } /// /// Activates the first data. I.e. it provides a selectionchanged for the first soil or if no soil available, the first CPT. /// This ensures the proper selection of the data line in the table and the belonging property window. /// Trying to find other data than soil or CPT is useless as all other data objects should have soils. /// Note: it does not set the proper table! That is handled in the plugin. /// public void ActivateFirstData() { // Find a data field for which a selectionchanged can be send. var firstSoil = soils.Soils.FirstOrDefault(); if (firstSoil != null) { DataEventPublisher.SelectionChanged(firstSoil); } else { var firstCPT = cpts.FirstOrDefault(); if (firstCPT != null) { DataEventPublisher.SelectionChanged(firstCPT); } } } /// /// Gets the domain. /// /// The property. /// the domain public ICollection GetDomain(string property) { switch (property) { case "Soil": return Soils.Soils; case "CPT": return CPTs; case "Boring": return Borings; case "SurfaceLine": return SurfaceLines; case "SoilSegment": return SoilSegments; case "SoilProfile": return SoilProfiles1D; case "SoilProfile2D": return SoilProfiles2D; case "StressTable": return StressCurves; case "BondStressTable": return BondStressCurves; case "CurrentFailureMechanism": return MechanismSupport.Mechanisms; default: return null; } } private void DataEventPublisher_OnAfterChange(object sender, PublishEventArgs e) { // update CurrentSoilSegments subset on user edit (mechanism change) if (sender is SoilSegment && e.Property == "Mechanism") { if (CurrentFailureMechanism != Mechanism.None) { FilterSegments(); } } } private void DataEventPublisher_OnDataListModified(object sender, PublishEventArgs e) { if (sender == SoilSegments) { FilterSegments(); } else if (sender == CurrentSoilSegments) { var args = e as DataListModifiedArgs; if (args == null) { return; } switch (args.Action) { case ListModifyAction.Delete: foreach (var segment in e.Objects.Cast().ToList()) { soilSegments.Remove(segment); } break; case ListModifyAction.Add: case ListModifyAction.Insert: foreach (var segment in e.Objects.Cast().ToList()) { soilSegments.Add(segment); } break; } } } private void FilterSegments() { if (CurrentFailureMechanism != Mechanism.None) { currentSoilSegments.Clear(); currentSoilSegments.AddRange(soilSegments.Where(s => s.Mechanism == CurrentFailureMechanism)); } else { currentSoilSegments.Clear(); currentSoilSegments.AddRange(soilSegments); } DataEventPublisher.DataListModified(CurrentSoilSegments); } // update current segments after adding/deleting a segment in the table private void UpdateSegments() {} private void AddSoilProfile1DToProject(SoilProfile1D profile) { UniqueNameProvider.ProvideUniqueName(soilProfiles1D, profile); soilProfiles1D.Add(profile); } private SoilProfile2D AddSoilProfile2DToProject(string fileName, SoilProfile2D soilProfile2D) { soilProfile2D.Name = Path.GetFileName(fileName); // Ensure unique name UniqueNameProvider.ProvideUniqueName(soilProfiles2D, soilProfile2D); soilProfile2D.XBegin = 0; soilProfile2D.XEnd = soilProfile2D.Geometry.Right; soilProfile2D.YBegin = 0; soilProfile2D.YEnd = 0; var loc = new GeographicPoint { X = soilProfile2D.Geometry.Right*0.5, Y = 0 }; soilProfile2D.Location = loc; soilProfiles2D.Add(soilProfile2D); return soilProfile2D; } private SoilProfile2D AddSoilProfile2DToProject(string fileName, StabilityModel stabilityModel) { if (stabilityModel.Geometry != null) { SoilProfile2D sp2D = stabilityModel.SoilProfile; sp2D.Geometry.Rebox(); sp2D.Length = stabilityModel.Geometry.Right; sp2D.Name = Path.GetFileName(fileName); // Ensure unique name UniqueNameProvider.ProvideUniqueName(soilProfiles2D, sp2D); soilProfiles2D.Add(sp2D); return sp2D; } return null; } /// /// Split a soil segment at given relative X coordinate /// /// The segment to split /// Local X coordinate public void SplitSoilSegment(SoilSegment selectedSegment, double xSplit) { // Split the current selected segment into two "halves" as defined by the split location if (selectedSegment != null) { var newSegment = new SoilSegment(); DataEventPublisher.BeforeChange(SoilSegments); DataEventPublisher.BeforeChange(selectedSegment); DataEventPublisher.BeforeChange(selectedSegment.Cpts); DataEventPublisher.BeforeChange(selectedSegment.Points); DataEventPublisher.InvokeWithoutPublishingEvents(() => { newSegment.Assign(selectedSegment); var oldName = selectedSegment.Name; newSegment.Name = UniqueNameProvider.ProvideNewUniqueName(SoilSegments, oldName); RearrangeSegmentCpts(selectedSegment, newSegment, xSplit); RearrangeSegmentBorings(selectedSegment, newSegment, xSplit); RearrangeSegmentPoints(selectedSegment, newSegment, xSplit); var index = SoilSegments.IndexOf(selectedSegment); if (index < SoilSegments.Count - 1) { SoilSegments.Insert(index + 1, newSegment); } else { SoilSegments.Add(newSegment); } var newName = UniqueNameProvider.ProvideNewUniqueName(SoilSegments, oldName); selectedSegment.Name = newSegment.Name; newSegment.Name = newName; }); DataEventPublisher.AfterChange(selectedSegment.Points); DataEventPublisher.DataListModified(SoilSegments); DataEventPublisher.DataListModified(selectedSegment.Cpts); DataEventPublisher.DataListModified(selectedSegment.Borings); DataEventPublisher.DataListModified(selectedSegment.Points); DataEventPublisher.SelectionChanged(selectedSegment, new SelectionEventArgs { Objects = new object[] { selectedSegment, newSegment }}); } } /// /// Link all selected CPT's and or Borings to the nearest (selected) segment(s) /// public void LinkCptsAndBoringsToNearestSegment(object[] selectedObjects) { // objects to link var cpts = selectedObjects.Where(o => o is ConePenetrationTestData).Cast().ToList(); var borings = selectedObjects.Where(o => o is Boring).Cast().ToList(); var segments = selectedObjects.Where(o => o is SoilSegment).Cast().ToList(); if (segments.Count == 0) { segments = SoilSegments; } foreach (var cpt in cpts) { LinkItemToSegments(cpt, segments); } foreach (var boring in borings) { LinkItemToSegments(boring, segments); } } private void LinkItemToSegments(IGeographic item, List segments) { double nearestDistance = double.MaxValue; IGeographic nearestItem = null; SoilSegment nearestSegment = null; foreach (var segment in segments) { double distance = DotSpatialGeographicAlgorithms.Distance(item, segment); if (distance < nearestDistance) { nearestDistance = distance; nearestItem = item; nearestSegment = segment; } } if (nearestItem != null) { // link segment to item SetSegmentLinkAndLocalCoordinate(nearestSegment, nearestItem); // check for other mechanism segment(s) with the same geographic definition and link to these as well foreach (var segment in segments) { double distance = DotSpatialGeographicAlgorithms.Distance(item, segment); if (Math.Abs(distance - nearestDistance) < GeometryConstants.Accuracy) { if (segment.Mechanism != nearestSegment.Mechanism && segment.Points.Count == nearestSegment.Points.Count) { if (CompareGeographicStrings(segment, nearestSegment)) { SetSegmentLinkAndLocalCoordinate(segment, nearestItem); } } } } } } private void SetSegmentLinkAndLocalCoordinate(SoilSegment segment, object item) { var itemPoint = item as IGeographicPoint; if (itemPoint != null) { var segmentPoint = DotSpatialGeographicAlgorithms.NearestPointOnGeographicString(segment, itemPoint); var xSegment = GeographicHelper.Instance.GetOffsetOnGeographicLineString(segmentPoint, segment); // link to cpt ? var cpt = item as ConePenetrationTestData; if (cpt != null) { if (segment.Cpts.All(x => x.ConePenetrationTestData != cpt)) { segment.Cpts.Add(new ConePenetrationTestPerSegment { ConePenetrationTestData = cpt, Xlocal = xSegment }); } } // link to boring ? var boring = item as Boring; if (boring != null) { if (segment.Borings.All(x => x.Boring != boring)) { segment.Borings.Add(new BoringPerSegment { Boring = boring, Xlocal = xSegment }); } } } } private bool CompareGeographicStrings(IGeographicString geographicString1, IGeographicString geographicString2) { if (geographicString1.Points.Count != geographicString2.Points.Count) { return false; } for (int i = 0; i < geographicString1.Points.Count; i++) { if (Math.Abs(geographicString1.Points[i].X - geographicString2.Points[i].X) > GeometryConstants.Accuracy) { return false; } if (Math.Abs(geographicString1.Points[i].Y - geographicString2.Points[i].Y) > GeometryConstants.Accuracy) { return false; } } return true; } private void RearrangeSegmentCpts(SoilSegment selectedSegment, SoilSegment newSegment, double xSplit) { foreach (var cptPerSegment in selectedSegment.Cpts) { if (cptPerSegment.Xlocal > xSplit) { var newCptPerSegment = new ConePenetrationTestPerSegment { Xlocal = cptPerSegment.Xlocal - xSplit, ConePenetrationTestData = cptPerSegment.ConePenetrationTestData }; newSegment.Cpts.Add(newCptPerSegment); cptPerSegment.Xlocal = noPoint; } } for (int i = selectedSegment.Cpts.Count - 1; i >= 0; i--) { if (Math.Abs(selectedSegment.Cpts[i].Xlocal - (noPoint)) < double.Epsilon) { selectedSegment.Cpts.Remove(selectedSegment.Cpts[i]); } } } private void RearrangeSegmentBorings(SoilSegment selectedSegment, SoilSegment newSegment, double xSplit) { foreach (var boringPerSegment in selectedSegment.Borings) { if (boringPerSegment.Xlocal > xSplit) { var newCptPerSegment = new BoringPerSegment { Xlocal = boringPerSegment.Xlocal, Boring = boringPerSegment.Boring }; newSegment.Borings.Add(newCptPerSegment); boringPerSegment.Xlocal = noPoint; } } for (int i = selectedSegment.Borings.Count - 1; i >= 0; i--) { if (Math.Abs(selectedSegment.Borings[i].Xlocal - (noPoint)) < double.Epsilon) { selectedSegment.Borings.Remove(selectedSegment.Borings[i]); } } } private void RearrangeSegmentPoints(SoilSegment selectedSegment, SoilSegment newSegment, double xSplit) { var addPoint = GeographicHelper.Instance.FindGeographicPointByOffsetOnLine(xSplit, selectedSegment); var points = selectedSegment.Points.ToArray(); // find the index for split double distance = 0; int index = 0; while (distance < xSplit) { index++; distance += Distance(points[index], points[index - 1]); } // copy points per segment selectedSegment.Points.Clear(); newSegment.Points.Clear(); newSegment.Points.Add(addPoint); for (var i = 0; i < points.Length; i++) { if (i < index) { selectedSegment.Points.Add(points[i]); } else { newSegment.Points.Add(points[i]); } } selectedSegment.Points.Add(addPoint); // check for (almost) duplicate points var n = selectedSegment.Points.Count - 1; if (Distance(selectedSegment.Points[n], selectedSegment.Points[n-1]) < geoTolerance) selectedSegment.Points.RemoveAt(n-1); if (Distance(newSegment.Points[0], newSegment.Points[1]) < geoTolerance) newSegment.Points.RemoveAt(1); } private double Distance(IGeographicPoint point1, IGeographicPoint point2) { var dx = point2.X - point1.X; var dy = point2.Y - point1.Y; return Math.Sqrt(dx * dx + dy * dy); } // Check the stress model for the given - set to None if not C-Phi or Su berekend private void CheckStrengthModelSoil(Soil soil) { if (soil.ShearStrengthModel != ShearStrengthModel.CPhi && soil.ShearStrengthModel != ShearStrengthModel.CuCalculated && soil.ShearStrengthModel != ShearStrengthModel.CuCalculatedYield) { soil.ShearStrengthModel = ShearStrengthModel.None; LogManager.Add(new LogMessage(LogMessageType.Warning, this, string.Format(LocalizationManager.GetTranslatedText(this, "UnknownStrengthModel"), soil.Name))); } if (soil.ShearStrengthModel == ShearStrengthModel.CuCalculatedYield) { soil.ShearStrengthModel = ShearStrengthModel.CuCalculated; soil.UsePop = false; } } /// /// Determines whether provided x,y coordinates are valid RD coordinates (Rijks Driehoekstelsel). /// /// The x coordinate. /// The y coordinate. /// /// true if [is valid rd coordinate] [the specified x]; otherwise, false. /// private bool IsValidRdCoordinate(double x, double y) { // valid coordinate range taken from wikipedia: https://nl.wikipedia.org/wiki/Rijksdriehoeksco%C3%B6rdinaten#Geldigheidsgebied const double xRdMin = -7000; const double xRdMax = 300000; const double yRdMin = 289000; const double yRdMax = 629000; return (x >= xRdMin && x <= xRdMax && y >= yRdMin && y <= yRdMax); } } }