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