// 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. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using Deltares.Maps; using DotSpatial.Data; using DotSpatial.Topology; using NUnit.Framework; using IFeature = DotSpatial.Data.IFeature; namespace Deltares.Dam.Tests { [TestFixture] public class IntersectionOnShapesTest { [SetUp] public void Setup() { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); } [Test] [Category("Slow")] public void AllIntersectionsWithTrafficLoad() { string crossSectionFile = Path.Combine(Directory.GetCurrentDirectory(), @"TestData\HHNKShapeFiles\Crosssection.shp"); string trafficFile = Path.Combine(Directory.GetCurrentDirectory(), @"TestData\HHNKShapeFiles\TrafficLoad.shp"); FindAllIntersectionsDotSpatial(crossSectionFile, trafficFile, 5543, 8329, 5602); } [Test] [Category("Slow")] public void AllIntersectionsWithPolderLevel() { string crossSectionFile = Path.Combine(Directory.GetCurrentDirectory(), @"TestData\HHNKShapeFiles\Crosssection.shp"); string polderLevelFile = Path.Combine(Directory.GetCurrentDirectory(), @"TestData\HHNKShapeFiles\PolderLevel.shp"); FindAllIntersectionsDotSpatial(crossSectionFile, polderLevelFile, 5543, 4393, 12469); } [Test] public void OneIntersectionWithTrafficLoad() { string crossSectionFile = Path.Combine(Directory.GetCurrentDirectory(), @"TestData\HHNKShapeFiles\Crosssection.shp"); string trafficFile = Path.Combine(Directory.GetCurrentDirectory(), @"TestData\HHNKShapeFiles\TrafficLoad.shp"); FindFirstIntersectionDotSpatial(crossSectionFile, trafficFile, 5543, 8329, 1); } [Test] public void OneIntersectionWithPolderLevel() { string crossSectionFile = Path.Combine(Directory.GetCurrentDirectory(), @"TestData\HHNKShapeFiles\Crosssection.shp"); string polderLevelFile = Path.Combine(Directory.GetCurrentDirectory(), @"TestData\HHNKShapeFiles\PolderLevel.shp"); Debug.WriteLine("--- DotSpatial ---"); FindFirstIntersectionDotSpatial(crossSectionFile, polderLevelFile, 5543, 4393, 2); Debug.WriteLine("--- NetTopologySuite ---"); FindFirstIntersectionNetTopology(crossSectionFile, polderLevelFile, 5543, 4393, 2); } private void FindAllIntersectionsDotSpatial(string sourceFile, string targetFile, int expectedNumberOfSourceSections, int expectedNumberOfTargets, int expectedIntersections) { IFeatureSet crossSectionFeatureSet = FeatureSet.Open(sourceFile); IFeatureSet trafficLoadFeatureSet = FeatureSet.Open(targetFile); DateTime start = DateTime.Now; var intersections = 0; foreach (IFeature crossSectionFeature in crossSectionFeatureSet.Features) { foreach (IFeature trafficLoadFeature in trafficLoadFeatureSet.Features) { if (crossSectionFeature.Intersects(trafficLoadFeature)) { intersections++; } } } DateTime finish = DateTime.Now; TimeSpan duration = finish - start; Assert.Multiple(() => { Assert.That(crossSectionFeatureSet.Features, Has.Count.EqualTo(expectedNumberOfSourceSections)); Assert.That(trafficLoadFeatureSet.Features, Has.Count.EqualTo(expectedNumberOfTargets)); Assert.That(intersections, Is.EqualTo(expectedIntersections)); }); Debug.WriteLine("Seconds: " + duration.TotalSeconds); Debug.WriteLine("Seconds per intersection: " + duration.TotalSeconds / (trafficLoadFeatureSet.Features.Count * crossSectionFeatureSet.Features.Count)); } private void FindFirstIntersectionDotSpatial(string sourceFile, string targetFile, int expectedNumberOfSourceSections, int expectedNumberOfTargets, int expectedIntersections) { IFeatureSet crossSectionFeatureSet = FeatureSet.Open(sourceFile); IFeatureSet trafficLoadFeatureSet = FeatureSet.Open(targetFile); DateTime start = DateTime.Now; var intersections = 0; IFeature crossSectionFeature = crossSectionFeatureSet.Features[0]; foreach (IFeature trafficLoadFeature in trafficLoadFeatureSet.Features) { if (crossSectionFeature.Intersects(trafficLoadFeature)) { intersections++; } } DateTime finish = DateTime.Now; TimeSpan duration = finish - start; Assert.That(crossSectionFeatureSet.Features.Count, Is.EqualTo(expectedNumberOfSourceSections)); Assert.That(trafficLoadFeatureSet.Features.Count, Is.EqualTo(expectedNumberOfTargets)); Assert.That(intersections, Is.EqualTo(expectedIntersections)); Debug.WriteLine("Number of intersections: " + intersections); Debug.WriteLine("Seconds: " + duration.TotalSeconds); Debug.WriteLine("Seconds per intersection: " + duration.TotalSeconds / trafficLoadFeatureSet.Features.Count); } private void FindFirstIntersectionNetTopology(string sourceFile, string targetFile, int expectedNumberOfSourceSections, int expectedNumberOfTargets, int expectedIntersections) { IEnumerable crossSectionFeatures = ShapeFileReader.ReadAllFeatures(sourceFile); IEnumerable trafficLoadFeatures = ShapeFileReader.ReadAllFeatures(targetFile); DateTime start = DateTime.Now; var intersections = 0; Maps.IFeature crossSectionFeature = crossSectionFeatures.First(); foreach (Maps.IFeature trafficLoadFeature in trafficLoadFeatures) { if (crossSectionFeature.Geometry.Intersects(trafficLoadFeature.Geometry)) { intersections++; } } DateTime finish = DateTime.Now; TimeSpan duration = finish - start; Assert.That(crossSectionFeatures.Count(), Is.EqualTo(expectedNumberOfSourceSections)); Assert.That(trafficLoadFeatures.Count(), Is.EqualTo(expectedNumberOfTargets)); Assert.That(intersections, Is.EqualTo(expectedIntersections)); Debug.WriteLine("Seconds: " + duration.TotalSeconds); Debug.WriteLine("Seconds per intersection: " + duration.TotalSeconds / trafficLoadFeatures.Count()); } } abstract class AbstractFeature { protected AbstractFeature() { Attributes = new Dictionary(); } public T Geometry { get; set; } public IDictionary Attributes { get; set; } public abstract bool IsIntersecting(AbstractFeature otherFeature); public abstract bool IsWithin(AbstractFeature otherFeature); public abstract bool IsTouching(AbstractFeature otherFeature); } class NtsFeature : AbstractFeature { public override bool IsIntersecting(AbstractFeature otherFeature) { return Geometry.Intersects(otherFeature.Geometry); } public override bool IsWithin(AbstractFeature otherFeature) { return Geometry.Within(otherFeature.Geometry); } public override bool IsTouching(AbstractFeature otherFeature) { return Geometry.Touches(otherFeature.Geometry); } } }