// Copyright (C) Stichting Deltares 2025. All rights reserved. // // This file is part of the application DAM - UI. // // DAM - UI 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. //#define INCLUDE_ALL_LINE_TYPES using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using Deltares.Dam.Data.CsvImporters; using Deltares.Maps; namespace Deltares.Dam.Data.Importers; /// /// Exception for the class LineAttributeImporter /// public class LineAttributeImporterException : Exception { public LineAttributeImporterException() {} public LineAttributeImporterException(string message) : base(message) {} public LineAttributeImporterException(string message, Exception inner) : base(message, inner) {} } public class LineAttributeImporter : IAttributeImporter where T : CsvImporterLocations.LocationRecord { private readonly IList errors; private IDictionary targetLookup; private IFeatureRepository crossSectionRepository; public LineAttributeImporter() { errors = new List(); targetLookup = new Dictionary(); } /// /// /// public string LocationIDAttributeName { private get; set; } /// /// Gets or sets the cross section repository /// public IFeatureRepository CrossSectionRepository { get { return crossSectionRepository; } set { crossSectionRepository = value; if (targetLookup != null && targetLookup.Count > 0 && value != null && IsValidString(LocationIDAttributeName)) { // Filter out all locations which are not subject for the intersection query.. CrossSectionRepository.Remove(f => !targetLookup.ContainsKey(GetID(f))); } } } /// /// Gets or sets the target objects /// public IEnumerable Targets { private get { return targetLookup.Values; } set { if (value != null) { targetLookup = value.ToDictionary(k => k.LocationId.ToLowerInvariant(), v => v); if (CrossSectionRepository != null) { // Filter out all locations which are not subject for the intersection query.. CrossSectionRepository.Remove(f => !targetLookup.ContainsKey(GetID(f))); } } } } /// /// public IFeatureRepository AttributeRepository { internal get; set; } /// /// /// public IEnumerable Errors { get { return errors; } } /// /// Gets or sets the name of the file. /// /// /// The name of the file. /// public string FileName { private get; set; } /// /// Sets the attribute mappings /// public IEnumerable> AttributeMappings { private get; set; } public void AddError(Exception exception) { errors.Add(exception); } /// /// /// /// Preconditions: /// - A valid data folder needs to given /// - A set of "targets" with their characteristic points table and initialized id's available from the dike /// - The required files need to be in the data folder (one for the characteristics points, /// - A set of (characteristic) lines wich have to be retrieved from the files /// /// Postconditions: /// - Some targets (the result of a join from the Location shape file and the existing location list from the dike) have new characteristic point values /// /// /// /// /// /// public void Import() { if (AttributeRepository == null) { throw new InvalidOperationException("No repository set"); } if (!AttributeRepository.SupportedAttributes.Any()) { throw new InvalidOperationException("The repository doesn't contain attributes"); } if (!IsValidString(LocationIDAttributeName)) { throw new InvalidOperationException("The location ID attribute name is not valid"); } if (CrossSectionRepository == null) { throw new InvalidOperationException("No cross section repository set"); } if (AttributeMappings == null) { throw new InvalidOperationException("No attribute mappings set"); } // Find the intersection points between the geometries IEnumerable> results = CrossSectionRepository.GetIntersectionPoints(AttributeRepository); var count = 0; // <- using count to omit the Linq.Count() for perfomance reasons IEnumerable work = results.GetResultsHavingCount(1); foreach (IntersectionResult intersectionResult in work) { foreach (AttributeMapping mapping in AttributeMappings) { IFeature crossSection, attributeLine; if (intersectionResult.Source.Attributes.Exists(mapping.Name)) { crossSection = intersectionResult.Target; attributeLine = intersectionResult.Source; } else { if (intersectionResult.Target.Attributes.Exists(mapping.Name)) { crossSection = intersectionResult.Source; attributeLine = intersectionResult.Target; } else { AddError(new AttributeMissingException(mapping.Name)); continue; } } // Get the surface line for the location having the location ID string locationId = GetID(crossSection); if (!targetLookup.ContainsKey(locationId)) { errors.Add(new ImportException($"Location {LocationIDAttributeName} not found")); continue; } T target = targetLookup[locationId]; object value = attributeLine.Attributes[mapping.Name]; try { // set the value on target property mapping.Action(target, value); if (mapping.Name.ToUpper() == "DAMPINGPL3") { if (target.DampingFactorPl3 < 0 || target.DampingFactorPl3 > 1) { AddError(new AttributeWrongValueException(mapping.Name, target.DampingFactorPl3.Value, 0.0, 1.0)); target.DampingFactorPl3 = 0; } } if (String.Compare(mapping.Name, "DampingPL4", true) == 0) { if (target.DampingFactorPl4 < 0 || target.DampingFactorPl4 > 1) { AddError(new AttributeWrongValueException(mapping.Name, target.DampingFactorPl4.Value, 0.0, 1.0)); target.DampingFactorPl4 = 0; } } count++; } catch (Exception e) { throw new AttributeParseException( $"Error parsing the value '{value.ToString().Replace("\0", "")}' of '{mapping.Name}' for target '{target.LocationId}' ", e); } } } if (count == 0) { errors.Add(new NotSupportedException("There are none or multiple intersections found. There should be exactly one intersection for each location in the target repository")); } } private string GetID(IFeature feature) { bool attributeExists = feature.Attributes.Exists(LocationIDAttributeName); if (!attributeExists) { throw new LineAttributeImporterException($"Cannot find attribute '{LocationIDAttributeName}'"); } return feature.Attributes[LocationIDAttributeName].ToString().ToLowerInvariant(); } private static bool IsValidString(string value) { return !(string.IsNullOrEmpty(value) || value.Trim() == string.Empty); } } [Serializable] public class AttributeParseException : Exception { // // For guidelines regarding the creation of new exception types, see // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp // and // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp // public AttributeParseException() {} public AttributeParseException(string message) : base(message) {} public AttributeParseException(string message, Exception inner) : base(message, inner) {} }