Skip to content

Commit cf88c33

Browse files
committed
feat: add overlapping outline validation GAWR-7047
1 parent 110c79b commit cf88c33

File tree

13 files changed

+125
-48
lines changed

13 files changed

+125
-48
lines changed

src/BuildingRegistry.Api.BackOffice.Handlers.Lambda/BuildingGeometryContext.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,35 @@ public ICollection<BuildingGeometryData> GetOverlappingBuildings(
6565
|| building.StatusAsString == BuildingStatus.Realized.Value)
6666
&& !building.IsRemoved
6767
&& boundingBox.Intersects(building.SysGeometry))
68-
.ToList()
68+
.AsEnumerable()
69+
.Where(building => HasTooMuchOverlap(geometry, building.SysGeometry))
70+
.ToList();
71+
72+
return overlappingBuildings;
73+
}
74+
75+
public ICollection<BuildingGeometryData> GetOverlappingBuildingOutlines(
76+
BuildingPersistentLocalId buildingPersistentLocalId,
77+
ExtendedWkbGeometry extendedWkbGeometry)
78+
{
79+
var wkbReader = WKBReaderFactory.Create();
80+
var geometry = wkbReader.Read(extendedWkbGeometry);
81+
var fixedGeometry = NetTopologySuite.Geometries.Utilities.GeometryFixer.Fix(geometry);
82+
83+
var boundingBox = fixedGeometry.Factory.ToGeometry(fixedGeometry.EnvelopeInternal);
84+
//check if bounding box is not clockwise otherwise reverse => must be counter clockwise oriented
85+
if (boundingBox.Coordinates[0].X > boundingBox.Coordinates[1].X)
86+
{
87+
boundingBox = boundingBox.Reverse();
88+
}
89+
90+
var overlappingBuildings = BuildingGeometries
91+
.Where(building =>
92+
building.BuildingPersistentLocalId != buildingPersistentLocalId
93+
&& building.GeometryMethod == BuildingGeometryMethod.Outlined
94+
&& !building.IsRemoved
95+
&& boundingBox.Intersects(building.SysGeometry))
96+
.AsEnumerable()
6997
.Where(building => HasTooMuchOverlap(geometry, building.SysGeometry))
7098
.ToList();
7199

src/BuildingRegistry.Api.BackOffice.Handlers.Lambda/Handlers/Building/ChangeBuildingOutlineLambdaHandler.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ await IdempotentCommandHandler.Dispatch(
5757
BuildingHasBuildingUnitsOutsideBuildingGeometryException => ValidationErrors.ChangeBuildingOutline.BuildingHasBuildingUnitsOutsideChangedGeometry.ToTicketError(),
5858
PolygonIsInvalidException => ValidationErrors.Common.InvalidBuildingPolygonGeometry.ToTicketError(),
5959
BuildingOutlineIsTooSmallException => ValidationErrors.Common.BuildingTooSmallGeometry.ToTicketError(),
60+
BuildingGeometryOverlapsWithOutlinedBuildingException => ValidationErrors.RealizeBuilding.OverlappingOutlinedBuilding.ToTicketError(),
6061
_ => null
6162
};
6263
}

src/BuildingRegistry.Api.BackOffice.Handlers.Lambda/Handlers/Building/PlanBuildingLambdaHandler.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ await IdempotentCommandHandler.Dispatch(
5454
{
5555
PolygonIsInvalidException => ValidationErrors.Common.InvalidBuildingPolygonGeometry.ToTicketError(),
5656
BuildingOutlineIsTooSmallException => ValidationErrors.Common.BuildingTooSmallGeometry.ToTicketError(),
57+
BuildingGeometryOverlapsWithOutlinedBuildingException => ValidationErrors.RealizeBuilding.OverlappingOutlinedBuilding.ToTicketError(),
5758
_ => null
5859
};
5960
}

src/BuildingRegistry.Api.BackOffice.Handlers.Lambda/NoOverlappingBuildingGeometries.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,10 @@ public ICollection<BuildingGeometryData> GetOverlappingBuildings(
1010
{
1111
return new List<BuildingGeometryData>();
1212
}
13+
14+
public ICollection<BuildingGeometryData> GetOverlappingBuildingOutlines(BuildingPersistentLocalId buildingPersistentLocalId, ExtendedWkbGeometry extendedWkbGeometry)
15+
{
16+
return new List<BuildingGeometryData>();
17+
}
1318
}
1419
}

src/BuildingRegistry/Building/Building.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ public static Building MigrateBuilding(
3838
public static Building Plan(
3939
IBuildingFactory buildingFactory,
4040
BuildingPersistentLocalId buildingPersistentLocalId,
41-
ExtendedWkbGeometry extendedWkbGeometry)
41+
ExtendedWkbGeometry extendedWkbGeometry,
42+
IBuildingGeometries buildingGeometries)
4243
{
4344
var geometry = WKBReaderFactory.Create().Read(extendedWkbGeometry);
4445

4546
GuardOutline(geometry);
47+
if (buildingGeometries.GetOverlappingBuildingOutlines(buildingPersistentLocalId, extendedWkbGeometry).Any())
48+
throw new BuildingGeometryOverlapsWithOutlinedBuildingException();
4649

4750
var newBuilding = buildingFactory.Create();
4851
newBuilding.ApplyChange(
@@ -226,7 +229,9 @@ public void RemoveMeasuredBuilding()
226229
ApplyChange(new BuildingWasRemovedV2(BuildingPersistentLocalId));
227230
}
228231

229-
public void ChangeOutliningConstruction(ExtendedWkbGeometry extendedWkbGeometry)
232+
public void ChangeOutliningConstruction(
233+
ExtendedWkbGeometry extendedWkbGeometry,
234+
IBuildingGeometries buildingGeometries)
230235
{
231236
GuardRemovedBuilding();
232237

@@ -245,6 +250,9 @@ public void ChangeOutliningConstruction(ExtendedWkbGeometry extendedWkbGeometry)
245250
var geometry = WKBReaderFactory.Create().Read(extendedWkbGeometry);
246251
GuardOutline(geometry);
247252

253+
if(buildingGeometries.GetOverlappingBuildingOutlines(BuildingPersistentLocalId, extendedWkbGeometry).Any())
254+
throw new BuildingGeometryOverlapsWithOutlinedBuildingException();
255+
248256
var newBuildingGeometry = new BuildingGeometry(extendedWkbGeometry, BuildingGeometryMethod.Outlined);
249257
var plannedOrRealizedBuildingUnits = _buildingUnits.PlannedBuildingUnits()
250258
.Concat(_buildingUnits.RealizedBuildingUnits())

src/BuildingRegistry/Building/BuildingCommandHandlerModule.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ public BuildingCommandHandlerModule(
8080
var newBuilding = Building.Plan(
8181
buildingFactory,
8282
message.Command.BuildingPersistentLocalId,
83-
message.Command.Geometry);
83+
message.Command.Geometry,
84+
buildingGeometries);
8485

8586
buildingRepository().Add(streamId, newBuilding);
8687
});
@@ -190,7 +191,7 @@ public BuildingCommandHandlerModule(
190191
var streamId = new BuildingStreamId(message.Command.BuildingPersistentLocalId);
191192
var building = await buildingRepository().GetAsync(streamId, ct);
192193

193-
building.ChangeOutliningConstruction(message.Command.Geometry);
194+
building.ChangeOutliningConstruction(message.Command.Geometry, buildingGeometries);
194195
});
195196

196197
For<RealizeAndMeasureUnplannedBuilding>()

src/BuildingRegistry/Building/IBuildingGeometries.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ public interface IBuildingGeometries
88
ICollection<BuildingGeometryData> GetOverlappingBuildings(
99
BuildingPersistentLocalId buildingPersistentLocalId,
1010
ExtendedWkbGeometry extendedWkbGeometry);
11+
12+
ICollection<BuildingGeometryData> GetOverlappingBuildingOutlines(
13+
BuildingPersistentLocalId buildingPersistentLocalId,
14+
ExtendedWkbGeometry extendedWkbGeometry);
1115
}
1216

1317
public sealed class BuildingGeometryData

test/BuildingRegistry.Tests/AggregateTests/WhenChangingBuildingMeasurement/GivenBuildingExists.cs

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace BuildingRegistry.Tests.AggregateTests.WhenChangingBuildingMeasurement
33
using System;
44
using System.Collections.Generic;
55
using System.Linq;
6+
using Api.BackOffice.Handlers.Lambda;
67
using AutoFixture;
78
using Be.Vlaanderen.Basisregisters.AggregateSource;
89
using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting;
@@ -122,7 +123,7 @@ public void
122123
BuildingUnitStatus.Planned,
123124
new BuildingUnitPersistentLocalId(2),
124125
positionGeometryMethod: BuildingUnitPositionGeometryMethod.AppointedByAdministrator,
125-
extendedWkbGeometry: new BuildingRegistry.Legacy.ExtendedWkbGeometry(GeometryHelper.ValidPolygon.AsBinary()))
126+
extendedWkbGeometry: new BuildingRegistry.Legacy.ExtendedWkbGeometry(GeometryHelper.PointNotInPolygon.AsBinary()))
126127
.WithBuildingUnit(
127128
BuildingUnitStatus.Realized,
128129
new BuildingUnitPersistentLocalId(3),
@@ -231,39 +232,6 @@ public void WithBuildingGeometryMethodIsOutlined_ThenThrowsBuildingHasInvalidBui
231232
.Throws(new BuildingHasInvalidGeometryMethodException()));
232233
}
233234

234-
[Theory]
235-
[InlineData("Planned")]
236-
[InlineData("Realized")]
237-
public void
238-
WithBuildingUnitsOutsideOfChangedBuildingGeometry_ThenThrowsBuildingUnitPositionIsOutsideBuildingGeometryException(
239-
string buildingUnitStatus)
240-
{
241-
var command = new ChangeBuildingOutline(
242-
Fixture.Create<BuildingPersistentLocalId>(),
243-
new ExtendedWkbGeometry(GeometryHelper.SecondValidPolygon.AsBinary()),
244-
Fixture.Create<Provenance>());
245-
246-
var initialBuildingGeometry = new BuildingGeometry(
247-
new ExtendedWkbGeometry(GeometryHelper.ValidPolygon.AsBinary()),
248-
BuildingGeometryMethod.Outlined);
249-
250-
var buildingWasMigrated = new BuildingWasMigratedBuilder(Fixture)
251-
.WithBuildingGeometry(initialBuildingGeometry)
252-
.WithBuildingUnit(
253-
BuildingUnitStatus.Parse(buildingUnitStatus)!.Value,
254-
new BuildingUnitPersistentLocalId(1),
255-
positionGeometryMethod: BuildingUnitPositionGeometryMethod.AppointedByAdministrator,
256-
extendedWkbGeometry: new BuildingRegistry.Legacy.ExtendedWkbGeometry(GeometryHelper.PointNotInPolygon.AsBinary()))
257-
.Build();
258-
259-
Assert(new Scenario()
260-
.Given(
261-
new BuildingStreamId(Fixture.Create<BuildingPersistentLocalId>()),
262-
buildingWasMigrated)
263-
.When(command)
264-
.Throws(new BuildingHasBuildingUnitsOutsideBuildingGeometryException()));
265-
}
266-
267235
[Fact]
268236
public void StateCheck()
269237
{
@@ -302,7 +270,7 @@ public void StateCheck()
302270
sut.Initialize(new List<object> {buildingWasMigrated});
303271

304272
// Act
305-
sut.ChangeOutliningConstruction(changedBuildingGeometry);
273+
sut.ChangeOutliningConstruction(changedBuildingGeometry, new NoOverlappingBuildingGeometries());
306274

307275
// Assert
308276
sut.BuildingGeometry.Should()

test/BuildingRegistry.Tests/AggregateTests/WhenChangingBuildingOutline/GivenBuildingExists.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace BuildingRegistry.Tests.AggregateTests.WhenChangingBuildingOutline
33
using System;
44
using System.Collections.Generic;
55
using System.Linq;
6+
using Api.BackOffice.Handlers.Lambda;
67
using AutoFixture;
78
using Be.Vlaanderen.Basisregisters.AggregateSource;
89
using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting;
@@ -15,6 +16,7 @@ namespace BuildingRegistry.Tests.AggregateTests.WhenChangingBuildingOutline
1516
using Extensions;
1617
using Fixtures;
1718
using FluentAssertions;
19+
using Moq;
1820
using Xunit;
1921
using Xunit.Abstractions;
2022
using BuildingUnitFunction = BuildingRegistry.Legacy.BuildingUnitFunction;
@@ -324,6 +326,38 @@ public void WithGeometryTooSmall_ThenBuildingTooSmallExceptionWasThrown()
324326
.Throws(new BuildingOutlineIsTooSmallException()));
325327
}
326328

329+
[Fact]
330+
public void WithOverlappingOutlinedBuilding_ThenThrowsBuildingGeometryOverlapsWithOutlinedBuildingException()
331+
{
332+
var command = Fixture.Create<ChangeBuildingOutline>();
333+
334+
FakeBuildingGeometries
335+
.Setup(x => x.GetOverlappingBuildingOutlines(
336+
It.IsAny<BuildingPersistentLocalId>(),
337+
It.IsAny<ExtendedWkbGeometry>()))
338+
.Returns(new[]
339+
{
340+
new BuildingGeometryData(
341+
1,
342+
BuildingStatus.Planned,
343+
BuildingGeometryMethod.Outlined,
344+
GeometryHelper.ValidPolygon,
345+
false)
346+
});
347+
348+
var buildingWasMigrated = new BuildingWasMigratedBuilder(Fixture)
349+
.WithBuildingStatus(BuildingStatus.UnderConstruction)
350+
.WithBuildingGeometry(new BuildingGeometry(ExtendedWkbGeometry.CreateEWkb(GeometryHelper.ValidPolygon.AsBinary()), BuildingGeometryMethod.Outlined))
351+
.Build();
352+
353+
Assert(new Scenario()
354+
.Given(
355+
new BuildingStreamId(Fixture.Create<BuildingPersistentLocalId>()),
356+
buildingWasMigrated)
357+
.When(command)
358+
.Throws(new BuildingGeometryOverlapsWithOutlinedBuildingException()));
359+
}
360+
327361
[Fact]
328362
public void StateCheck()
329363
{
@@ -360,7 +394,7 @@ public void StateCheck()
360394
sut.Initialize(new List<object> { buildingWasMigrated });
361395

362396
// Act
363-
sut.ChangeOutliningConstruction(changedBuildingGeometry);
397+
sut.ChangeOutliningConstruction(changedBuildingGeometry, new NoOverlappingBuildingGeometries());
364398

365399
// Assert
366400
sut.BuildingGeometry.Should()

test/BuildingRegistry.Tests/AggregateTests/WhenPlanningBuilding/GivenNoBuildingExists.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
namespace BuildingRegistry.Tests.AggregateTests.WhenPlanningBuilding
22
{
3+
using Api.BackOffice.Handlers.Lambda;
34
using AutoFixture;
45
using Be.Vlaanderen.Basisregisters.AggregateSource;
56
using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting;
67
using Be.Vlaanderen.Basisregisters.AggregateSource.Testing;
7-
using Be.Vlaanderen.Basisregisters.GrAr.Provenance;
88
using Building;
99
using Building.Commands;
10-
using Building.Datastructures;
1110
using Building.Events;
1211
using Building.Exceptions;
1312
using Fixtures;
1413
using FluentAssertions;
15-
using NetTopologySuite.IO;
14+
using Moq;
1615
using Xunit;
1716
using Xunit.Abstractions;
1817

@@ -61,6 +60,30 @@ public void WithGeometryTooSmall_ThenBuildingTooSmallExceptionWasThrown()
6160
.Throws(new BuildingOutlineIsTooSmallException()));
6261
}
6362

63+
[Fact]
64+
public void WithOverlappingOutlinedBuilding_ThenThrowsBuildingGeometryOverlapsWithMeasuredBuildingException()
65+
{
66+
var command = Fixture.Create<PlanBuilding>();
67+
68+
FakeBuildingGeometries
69+
.Setup(x => x.GetOverlappingBuildingOutlines(
70+
command.BuildingPersistentLocalId,
71+
It.IsAny<ExtendedWkbGeometry>()))
72+
.Returns([
73+
new BuildingGeometryData(
74+
command.BuildingPersistentLocalId + 1,
75+
BuildingStatus.Planned,
76+
BuildingGeometryMethod.Outlined,
77+
GeometryHelper.ValidPolygon,
78+
false)
79+
]);
80+
81+
Assert(new Scenario()
82+
.GivenNone()
83+
.When(command)
84+
.Throws(new BuildingGeometryOverlapsWithOutlinedBuildingException()));
85+
}
86+
6487
[Fact]
6588
public void ThenBuildingStateWasCorrectlySet()
6689
{
@@ -70,7 +93,8 @@ public void ThenBuildingStateWasCorrectlySet()
7093
var result = Building.Plan(
7194
new BuildingFactory(NoSnapshotStrategy.Instance),
7295
command.BuildingPersistentLocalId,
73-
command.Geometry);
96+
command.Geometry,
97+
new NoOverlappingBuildingGeometries());
7498

7599
// Assert
76100
result.Should().NotBeNull();

0 commit comments

Comments
 (0)