Skip to content

Commit

Permalink
Merge pull request #540 from fab-geocommuns/inspector_close_bdg_point
Browse files Browse the repository at this point in the history
Meilleure prise en compte des bâtiments point lors de l'inspection
  • Loading branch information
pauletienney authored Feb 4, 2025
2 parents dd995c4 + 3838ba3 commit 54bf7f8
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ app/.Trash-0/*
/app/notebooks/rapprochements/**/*.shx
/app/notebooks/rapprochements/**/*.xlsx

/app/notebooks/evaluation_bal/**/*.csv

# Remove files in data folder since they are used for importing default data
source_data/

Expand Down
22 changes: 15 additions & 7 deletions app/batid/services/candidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,18 @@ def get_candidate(self):

def get_matching_bdgs(self):

self.matching_bdgs = Building.objects.filter(
shape__intersects=self.candidate.shape
).filter(status__in=BuildingStatusService.REAL_BUILDINGS_STATUS, is_active=True)
q = (
"SELECT id, ST_AsEWKB(shape) as shape "
f"FROM {Building._meta.db_table} "
"WHERE ST_DWithin(shape::geography, ST_GeomFromText(%(c_shape)s)::geography, 3) "
"AND status IN %(status)s "
"AND is_active = true"
)
params = {
"c_shape": f"{self.candidate.shape}",
"status": tuple(BuildingStatusService.REAL_BUILDINGS_STATUS),
}
self.matching_bdgs = Building.objects.raw(q, params)

def inspect_candidate(self):

Expand Down Expand Up @@ -291,11 +300,10 @@ def match_points(
def match_point_poly(
a: GEOSGeometry, b: GEOSGeometry
) -> Literal["match", "no_match", "conflict"]:
# NB : this intersection verification is already done in the sql query BUT we want to be sure this matching condition is always verified even the SQL query is modified
if a.intersects(b):
return "match"

return "no_match"
# We are sure the point is close to the polygon (the db query keeps buildings in a 3 meters radius)
# So, it's always a match
return "match"


def shape_family(shape: GEOSGeometry):
Expand Down
175 changes: 175 additions & 0 deletions app/batid/tests/test_inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,181 @@ def test_result(self):
self.assertEqual(c.inspection_details["reason"], "ambiguous_overlap")


class TestCandidateCLoseToPointBdg(InspectTest):
"""
Some buildings have only a Point in the shape attribute.
BD Topo (and others) can help us to transform this point into a polygon.
The problem is sometimes the candidate does not intersect the building.
We have to attach the candidate to the building if it is close enough.
We verify the inspection attaches candidate polygon and building point when they are close enough.
POINT_BDG is a building with a point shape.
It is close to the candidate. Its shape must be updated with the candidate polygon.
FAR_POINT_BDG is a building with a point shape.
It is far from the candidate and should be ignored.
POLY_BDG_NEIGHBOR is a building with a polygon shape.
It intersects the candidate but not enough. It should be ignored
"""

bdgs_data = [
{
"id": "POINT_BDG",
"source": "bdtopo",
"geometry": {
"coordinates": [-0.4054973749373687, 42.13386683679241],
"type": "Point",
},
},
{
"id": "FAR_POINT_BDG",
"source": "bdtopo",
"geometry": {
"coordinates": [-0.40521491170318313, 42.13399847871867],
"type": "Point",
},
},
{
"id": "POLY_BDG_NEIGHBOR",
"source": "bdtopo",
"geometry": {
"coordinates": [
[
[-0.4054742368722941, 42.134111441961494],
[-0.4054853481118812, 42.133891163317315],
[-0.4052423823313802, 42.133890613993145],
[-0.40526386406216375, 42.13413561214],
[-0.4054742368722941, 42.134111441961494],
]
],
"type": "Polygon",
},
},
]

candidates_data = [
{
"id": "CDT_POLY",
"source": "bdnb",
"geometry": {
"coordinates": [
[
[-0.4055384428695845, 42.1338676176932],
[-0.4054805265556638, 42.13390978632495],
[-0.40546683724414834, 42.1341050111069],
[-0.4059112133287499, 42.13408705045228],
[-0.40588488773067866, 42.13384575246573],
[-0.4055384428695845, 42.1338676176932],
]
],
"type": "Polygon",
},
},
]

def test_result(self):

i = Inspector()
i.inspect()

# The candidate updated a building
c = Candidate.objects.all().first()
self.assertEqual(c.inspection_details["decision"], "update")

# Updated building now has a polygon
rnb_id = c.inspection_details["rnb_id"]
bdg = Building.objects.get(rnb_id=rnb_id)

self.assertEqual(bdg.shape.geom_type, "Polygon")
self.assertEqual(len(bdg.ext_ids), 2)
self.assertEqual(bdg.ext_ids[1]["id"], "CDT_POLY")


class TestCandidateCLoseToAmbiguousPointsBdgs(InspectTest):
bdgs_data = [
{
"id": "CLOSE_ONE",
"source": "bdnb",
"geometry": {
"coordinates": [-0.40526639833487366, 42.13435554467577],
"type": "Point",
},
},
{
"id": "CLOSE_TWO",
"source": "bdnb",
"geometry": {
"coordinates": [-0.4054133335975223, 42.134342572850386],
"type": "Point",
},
},
{
"id": "INSIDE",
"source": "bdnb",
"geometry": {
"coordinates": [-0.4052891383165331, 42.134270579167264],
"type": "Point",
},
},
{
"id": "FAR",
"source": "bdnb",
"geometry": {
"coordinates": [-0.40496020881957406, 42.13401039632299],
"type": "Point",
},
},
]

candidates_data = [
{
"id": "AMBIGUOUS_POLY",
"source": "bdnb",
"geometry": {
"coordinates": [
[
[-0.40533767926592645, 42.134363059816536],
[-0.4054953663784602, 42.134262535288656],
[-0.4052270216421334, 42.13414867568085],
[-0.40517030961083833, 42.13431484909324],
[-0.40533767926592645, 42.134363059816536],
]
],
"type": "Polygon",
},
},
]

def test_result(self):

i = Inspector()
i.inspect()

# The candidate has been refused
c = Candidate.objects.all().first()
self.assertEqual(c.inspection_details["decision"], "refusal")
self.assertEqual(c.inspection_details["reason"], "too_many_geomatches")
# There are 4 bdgs in the database but one is too far from the candidate
self.assertEqual(len(c.inspection_details["matches"]), 3)

# The buildings have not been updated
bdgs = Building.objects.all()
self.assertEqual(len(bdgs), 4)

for bdg in bdgs:

self.assertEqual(bdg.shape.geom_type, "Point")
self.assertEqual(len(bdg.ext_ids), 1)

history_rows = (
BuildingWithHistory.objects.filter(rnb_id=bdg.rnb_id).all().count()
)
self.assertEqual(history_rows, 1)


def data_to_candidate(data):
b_import = BuildingImport.objects.create(
departement="33",
Expand Down

0 comments on commit 54bf7f8

Please sign in to comment.