From bf96a0e8a02bc04aa80e24d51b8f139af380b9e2 Mon Sep 17 00:00:00 2001 From: ArneD Date: Thu, 30 May 2024 12:25:22 +0200 Subject: [PATCH] fix: use a buffer to calculate if building overlaps --- .../BuildingGeometryContext.cs | 34 +++++++++++++++++-- .../OverlappingBuildingGeometryTests.cs | 23 +++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/BuildingRegistry.Api.BackOffice.Handlers.Lambda/BuildingGeometryContext.cs b/src/BuildingRegistry.Api.BackOffice.Handlers.Lambda/BuildingGeometryContext.cs index 8181bfdcd..e6c159d83 100644 --- a/src/BuildingRegistry.Api.BackOffice.Handlers.Lambda/BuildingGeometryContext.cs +++ b/src/BuildingRegistry.Api.BackOffice.Handlers.Lambda/BuildingGeometryContext.cs @@ -3,11 +3,14 @@ using Building; using Infrastructure; using Microsoft.EntityFrameworkCore; + using NetTopologySuite.Geometries; + using NetTopologySuite.Operation.Buffer; public class BuildingGeometryContext : DbContext, IBuildingGeometries { - public DbSet BuildingGeometries => Set(); + private const double AllowedOverlapPercentage = 0.05; + public DbSet BuildingGeometries => Set(); public BuildingGeometryContext() { } // This needs to be DbContextOptions for Autofac! @@ -63,10 +66,37 @@ public ICollection GetOverlappingBuildings( && !building.IsRemoved && boundingBox.Intersects(building.SysGeometry)) .ToList() - .Where(building => geometry.Intersects(building.SysGeometry)) + .Where(building => HasTooMuchOverlap(geometry, building.SysGeometry)) .ToList(); return overlappingBuildings; } + + private static bool HasTooMuchOverlap(Geometry newBuildingGeometry, Geometry? existingBuildingGeometry) + { + if (existingBuildingGeometry is null) + { + return false; + } + + try + { + var overlapArea = newBuildingGeometry.Intersection(existingBuildingGeometry).Area; + var newBuildingGeometryOverlapPercentage = overlapArea / newBuildingGeometry.Area; + var existingBuildingGeometryOverlapPercentage = overlapArea / existingBuildingGeometry.Area; + + return newBuildingGeometryOverlapPercentage > AllowedOverlapPercentage + || existingBuildingGeometryOverlapPercentage > AllowedOverlapPercentage; + } + catch (TopologyException topologyException) + { + // Consider buildings that Intersect, but fail with "found non-noded intersection" on calculating, to have an overlap value of 0 + if (topologyException.Message.Contains("found non-noded intersection", StringComparison.InvariantCultureIgnoreCase)) + return false; + + // any other TopologyException should be treated normally + throw; + } + } } } diff --git a/test/BuildingRegistry.Tests/BackOffice/Lambda/OverlappingBuildingGeometryTests.cs b/test/BuildingRegistry.Tests/BackOffice/Lambda/OverlappingBuildingGeometryTests.cs index 5ff13622f..51648a8c5 100644 --- a/test/BuildingRegistry.Tests/BackOffice/Lambda/OverlappingBuildingGeometryTests.cs +++ b/test/BuildingRegistry.Tests/BackOffice/Lambda/OverlappingBuildingGeometryTests.cs @@ -134,6 +134,29 @@ public void WithBuildingOverlappingGeometryAndIdenticalId_ShouldReturnNothing() result.Should().BeEmpty(); } + [Fact] + public void WithOverlappingBuilding_ShouldReturnNothing() + { + var polygon1 = CreateGeometry("150515.51954216015 193984.87173669986 150514.00391716015 193976.65298669986 150510.30950881125 193977.33427493152 150509.72481561470 193974.16367058750 150500.24419151392 193975.91199860617 150501.16168397170 193980.88726698520 150506.17659092674 193979.96246475200 150507.35941666557 193986.37655071693 150515.51954216015 193984.87173669986"); + var polygon2 = CreateGeometry("150527.43725961447 193983.04583268613 150515.52737160772 193984.85690468922 150513.92218761146 193976.26362468302 150513.68020360917 193974.55764068291 150519.21812361479 193973.77197667956 150518.91988360882 193971.80333667994 150522.89082761109 193971.20173668116 150526.00993161649 193973.88960868120 150527.43725961447 193983.04583268613"); + + _buildingGeometryContext.BuildingGeometries + .Add(new BuildingGeometryData( + 2, + BuildingStatus.Realized, + BuildingGeometryMethod.MeasuredByGrb, + polygon2, + false)); + + _buildingGeometryContext.SaveChanges(); + + var result = _buildingGeometryContext.GetOverlappingBuildings( + new BuildingPersistentLocalId(1), + ExtendedWkbGeometry.CreateEWkb(polygon1.AsBinary())!); + + result.Should().BeEmpty(); + } + private static Geometry CreateGeometry(string coordinates) => GmlHelpers.CreateGmlReader().Read( "" +